reek 1.3.8 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +11 -0
- data/README.md +22 -14
- data/Rakefile +2 -15
- data/assets/html_output.html.erb +103 -0
- data/features/command_line_interface/options.feature +3 -0
- data/features/command_line_interface/smell_selection.feature +19 -0
- data/features/rake_task/rake_task.feature +1 -1
- data/features/reports/reports.feature +16 -0
- data/features/reports/yaml.feature +26 -23
- data/features/samples.feature +2 -1
- data/features/step_definitions/reek_steps.rb +15 -15
- data/features/support/env.rb +7 -9
- data/lib/reek/cli/application.rb +2 -4
- data/lib/reek/cli/command.rb +12 -0
- data/lib/reek/cli/help_command.rb +3 -6
- data/lib/reek/cli/options.rb +147 -0
- data/lib/reek/cli/reek_command.rb +18 -14
- data/lib/reek/cli/report/formatter.rb +56 -0
- data/lib/reek/cli/report/report.rb +106 -0
- data/lib/reek/cli/report/strategy.rb +63 -0
- data/lib/reek/cli/version_command.rb +3 -6
- data/lib/reek/config_file_exception.rb +0 -1
- data/lib/reek/core/code_context.rb +1 -3
- data/lib/reek/core/code_parser.rb +13 -12
- data/lib/reek/core/method_context.rb +13 -2
- data/lib/reek/core/module_context.rb +0 -4
- data/lib/reek/core/object_refs.rb +2 -3
- data/lib/reek/core/singleton_method_context.rb +0 -2
- data/lib/reek/core/smell_configuration.rb +3 -5
- data/lib/reek/core/smell_repository.rb +7 -8
- data/lib/reek/core/sniffer.rb +4 -10
- data/lib/reek/core/stop_context.rb +2 -4
- data/lib/reek/core/warning_collector.rb +0 -1
- data/lib/reek/examiner.rb +19 -17
- data/lib/reek/rake/task.rb +7 -10
- data/lib/reek/smell_warning.rb +4 -8
- data/lib/reek/smells.rb +0 -1
- data/lib/reek/smells/attribute.rb +8 -11
- data/lib/reek/smells/boolean_parameter.rb +5 -7
- data/lib/reek/smells/class_variable.rb +6 -7
- data/lib/reek/smells/control_parameter.rb +78 -45
- data/lib/reek/smells/data_clump.rb +13 -16
- data/lib/reek/smells/duplicate_method_call.rb +13 -11
- data/lib/reek/smells/feature_envy.rb +6 -7
- data/lib/reek/smells/irresponsible_module.rb +4 -6
- data/lib/reek/smells/long_parameter_list.rb +5 -7
- data/lib/reek/smells/long_yield_list.rb +2 -4
- data/lib/reek/smells/nested_iterators.rb +12 -22
- data/lib/reek/smells/nil_check.rb +35 -46
- data/lib/reek/smells/prima_donna_method.rb +24 -16
- data/lib/reek/smells/repeated_conditional.rb +8 -10
- data/lib/reek/smells/smell_detector.rb +9 -7
- data/lib/reek/smells/too_many_instance_variables.rb +7 -9
- data/lib/reek/smells/too_many_methods.rb +6 -8
- data/lib/reek/smells/too_many_statements.rb +4 -6
- data/lib/reek/smells/uncommunicative_method_name.rb +5 -7
- data/lib/reek/smells/uncommunicative_module_name.rb +5 -7
- data/lib/reek/smells/uncommunicative_parameter_name.rb +7 -9
- data/lib/reek/smells/uncommunicative_variable_name.rb +15 -18
- data/lib/reek/smells/unused_parameters.rb +5 -45
- data/lib/reek/smells/utility_function.rb +9 -10
- data/lib/reek/source.rb +0 -1
- data/lib/reek/source/code_comment.rb +7 -8
- data/lib/reek/source/config_file.rb +2 -4
- data/lib/reek/source/core_extras.rb +1 -1
- data/lib/reek/source/reference_collector.rb +1 -2
- data/lib/reek/source/sexp_extensions.rb +93 -10
- data/lib/reek/source/sexp_formatter.rb +2 -3
- data/lib/reek/source/sexp_node.rb +19 -15
- data/lib/reek/source/source_code.rb +4 -14
- data/lib/reek/source/source_file.rb +3 -5
- data/lib/reek/source/source_locator.rb +5 -6
- data/lib/reek/source/source_repository.rb +3 -3
- data/lib/reek/source/tree_dresser.rb +2 -2
- data/lib/reek/spec.rb +1 -2
- data/lib/reek/spec/should_reek.rb +8 -5
- data/lib/reek/spec/should_reek_of.rb +6 -4
- data/lib/reek/spec/should_reek_only_of.rb +10 -6
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +34 -30
- data/spec/gem/updates_spec.rb +3 -4
- data/spec/gem/yard_spec.rb +1 -2
- data/spec/matchers/smell_of_matcher.rb +12 -14
- data/spec/quality/reek_source_spec.rb +42 -0
- data/spec/reek/cli/help_command_spec.rb +7 -5
- data/spec/reek/cli/report_spec.rb +89 -22
- data/spec/reek/cli/version_command_spec.rb +8 -6
- data/spec/reek/core/code_context_spec.rb +25 -26
- data/spec/reek/core/code_parser_spec.rb +6 -6
- data/spec/reek/core/method_context_spec.rb +18 -18
- data/spec/reek/core/module_context_spec.rb +5 -5
- data/spec/reek/core/object_refs_spec.rb +21 -22
- data/spec/reek/core/smell_configuration_spec.rb +22 -21
- data/spec/reek/core/stop_context_spec.rb +2 -2
- data/spec/reek/core/warning_collector_spec.rb +3 -3
- data/spec/reek/examiner_spec.rb +9 -9
- data/spec/reek/smell_warning_spec.rb +29 -29
- data/spec/reek/smells/attribute_spec.rb +6 -6
- data/spec/reek/smells/behaves_like_variable_detector.rb +6 -6
- data/spec/reek/smells/boolean_parameter_spec.rb +17 -17
- data/spec/reek/smells/class_variable_spec.rb +9 -9
- data/spec/reek/smells/control_parameter_spec.rb +161 -137
- data/spec/reek/smells/data_clump_spec.rb +22 -19
- data/spec/reek/smells/duplicate_method_call_spec.rb +71 -27
- data/spec/reek/smells/feature_envy_spec.rb +32 -32
- data/spec/reek/smells/irresponsible_module_spec.rb +21 -21
- data/spec/reek/smells/long_parameter_list_spec.rb +14 -14
- data/spec/reek/smells/long_yield_list_spec.rb +6 -6
- data/spec/reek/smells/nested_iterators_spec.rb +21 -21
- data/spec/reek/smells/nil_check_spec.rb +23 -15
- data/spec/reek/smells/prima_donna_method_spec.rb +5 -5
- data/spec/reek/smells/repeated_conditional_spec.rb +14 -14
- data/spec/reek/smells/smell_detector_shared.rb +9 -9
- data/spec/reek/smells/too_many_instance_variables_spec.rb +12 -12
- data/spec/reek/smells/too_many_methods_spec.rb +10 -10
- data/spec/reek/smells/too_many_statements_spec.rb +41 -41
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +4 -4
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +12 -12
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +21 -21
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +49 -49
- data/spec/reek/smells/unused_parameters_spec.rb +26 -16
- data/spec/reek/smells/utility_function_spec.rb +20 -20
- data/spec/reek/source/code_comment_spec.rb +37 -37
- data/spec/reek/source/object_source_spec.rb +5 -5
- data/spec/reek/source/reference_collector_spec.rb +9 -9
- data/spec/reek/source/sexp_extensions_spec.rb +73 -52
- data/spec/reek/source/sexp_formatter_spec.rb +3 -4
- data/spec/reek/source/sexp_node_spec.rb +3 -3
- data/spec/reek/source/source_code_spec.rb +16 -15
- data/spec/reek/source/tree_dresser_spec.rb +2 -2
- data/spec/reek/spec/should_reek_of_spec.rb +11 -11
- data/spec/reek/spec/should_reek_only_of_spec.rb +11 -11
- data/spec/reek/spec/should_reek_spec.rb +11 -11
- data/spec/samples/one_smelly_file/dirty.rb +3 -0
- data/spec/spec_helper.rb +0 -6
- data/tasks/develop.rake +8 -16
- data/tasks/reek.rake +5 -13
- data/tasks/test.rake +5 -22
- metadata +56 -34
- data/lib/reek/cli/command_line.rb +0 -126
- data/lib/reek/cli/report.rb +0 -138
@@ -3,7 +3,6 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# Simulated Polymorphism occurs when
|
9
8
|
# * code uses a case statement (especially on a type field);
|
@@ -22,9 +21,8 @@ module Reek
|
|
22
21
|
# testing the same value throughout a single class.
|
23
22
|
#
|
24
23
|
class RepeatedConditional < SmellDetector
|
25
|
-
|
26
24
|
SMELL_CLASS = 'SimulatedPolymorphism'
|
27
|
-
SMELL_SUBCLASS =
|
25
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
28
26
|
|
29
27
|
def self.contexts # :nodoc:
|
30
28
|
[:class]
|
@@ -47,7 +45,7 @@ module Reek
|
|
47
45
|
#
|
48
46
|
def examine_context(ctx)
|
49
47
|
@max_identical_ifs = value(MAX_IDENTICAL_IFS_KEY, ctx, DEFAULT_MAX_IFS)
|
50
|
-
conditional_counts(ctx).select do |
|
48
|
+
conditional_counts(ctx).select do |_key, lines|
|
51
49
|
lines.length > @max_identical_ifs
|
52
50
|
end.map do |key, lines|
|
53
51
|
occurs = lines.length
|
@@ -55,7 +53,7 @@ module Reek
|
|
55
53
|
SmellWarning.new(SMELL_CLASS, ctx.full_name, lines,
|
56
54
|
"tests #{expr} at least #{occurs} times",
|
57
55
|
@source, SMELL_SUBCLASS,
|
58
|
-
|
56
|
+
'expression' => expr, 'occurrences' => occurs)
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
@@ -65,13 +63,13 @@ module Reek
|
|
65
63
|
# occurs. Ignores nested classes and modules.
|
66
64
|
#
|
67
65
|
def conditional_counts(sexp)
|
68
|
-
result = Hash.new {|hash, key| hash[key] = []}
|
69
|
-
collector = proc
|
66
|
+
result = Hash.new { |hash, key| hash[key] = [] }
|
67
|
+
collector = proc do |node|
|
70
68
|
condition = node.condition
|
71
|
-
next if condition.nil?
|
69
|
+
next if condition.nil? || condition == s(:call, nil, :block_given?)
|
72
70
|
result[condition].push(condition.line)
|
73
|
-
|
74
|
-
[:if, :case].each {|stmt| sexp.local_nodes(stmt, &collector) }
|
71
|
+
end
|
72
|
+
[:if, :case].each { |stmt| sexp.local_nodes(stmt, &collector) }
|
75
73
|
result
|
76
74
|
end
|
77
75
|
end
|
@@ -4,7 +4,6 @@ require 'reek/core/smell_configuration'
|
|
4
4
|
|
5
5
|
module Reek
|
6
6
|
module Smells
|
7
|
-
|
8
7
|
module ExcludeInitialize
|
9
8
|
def self.default_config
|
10
9
|
super.merge(EXCLUDE_KEY => ['initialize'])
|
@@ -15,7 +14,6 @@ module Reek
|
|
15
14
|
# Shared responsibilities of all smell detectors.
|
16
15
|
#
|
17
16
|
class SmellDetector
|
18
|
-
|
19
17
|
# The name of the config field that lists the names of code contexts
|
20
18
|
# that should not be checked. Add this field to the config for each
|
21
19
|
# smell that should ignore this code element.
|
@@ -65,11 +63,15 @@ module Reek
|
|
65
63
|
end
|
66
64
|
|
67
65
|
def examine(context)
|
68
|
-
|
69
|
-
if
|
70
|
-
|
71
|
-
|
72
|
-
|
66
|
+
return unless enabled_for? context
|
67
|
+
return if exception?(context)
|
68
|
+
|
69
|
+
sm = examine_context(context)
|
70
|
+
@smells_found += sm
|
71
|
+
end
|
72
|
+
|
73
|
+
def enabled_for?(context)
|
74
|
+
enabled? && config_for(context)[Core::SmellConfiguration::ENABLED_KEY] != false
|
73
75
|
end
|
74
76
|
|
75
77
|
def exception?(context)
|
@@ -3,18 +3,16 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# A Large Class is a class or module that has a large number of
|
9
8
|
# instance variables, methods or lines of code.
|
10
|
-
#
|
9
|
+
#
|
11
10
|
# +TooManyInstanceVariables' reports classes having more than a
|
12
11
|
# configurable number of instance variables.
|
13
12
|
#
|
14
13
|
class TooManyInstanceVariables < SmellDetector
|
15
|
-
|
16
14
|
SMELL_CLASS = 'LargeClass'
|
17
|
-
SMELL_SUBCLASS =
|
15
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
18
16
|
IVAR_COUNT_KEY = 'ivar_count'
|
19
17
|
|
20
18
|
# The name of the config field that sets the maximum number of instance
|
@@ -44,15 +42,15 @@ module Reek
|
|
44
42
|
check_num_ivars(ctx)
|
45
43
|
end
|
46
44
|
|
47
|
-
|
45
|
+
private
|
48
46
|
|
49
47
|
def check_num_ivars(ctx) # :nodoc:
|
50
|
-
count = ctx.local_nodes(:iasgn).map {|iasgn| iasgn[1]}.uniq.length
|
48
|
+
count = ctx.local_nodes(:iasgn).map { |iasgn| iasgn[1] }.uniq.length
|
51
49
|
return [] if count <= @max_allowed_ivars
|
52
50
|
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
"has at least #{count} instance variables",
|
52
|
+
@source, SMELL_SUBCLASS,
|
53
|
+
IVAR_COUNT_KEY => count)
|
56
54
|
[smell]
|
57
55
|
end
|
58
56
|
end
|
@@ -3,20 +3,18 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# A Large Class is a class or module that has a large number of
|
9
8
|
# instance variables, methods or lines of code.
|
10
|
-
#
|
9
|
+
#
|
11
10
|
# +TooManyMethods+ reports classes having more than a configurable number
|
12
11
|
# of methods. The method count includes public, protected and private
|
13
12
|
# methods, and excludes methods inherited from superclasses or included
|
14
13
|
# modules.
|
15
14
|
#
|
16
15
|
class TooManyMethods < SmellDetector
|
17
|
-
|
18
16
|
SMELL_CLASS = 'LargeClass'
|
19
|
-
SMELL_SUBCLASS =
|
17
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
20
18
|
METHOD_COUNT_KEY = 'method_count'
|
21
19
|
|
22
20
|
# The name of the config field that sets the maximum number of methods
|
@@ -46,15 +44,15 @@ module Reek
|
|
46
44
|
check_num_methods(ctx)
|
47
45
|
end
|
48
46
|
|
49
|
-
|
47
|
+
private
|
50
48
|
|
51
49
|
def check_num_methods(ctx) # :nodoc:
|
52
50
|
actual = ctx.local_nodes(:defn).length
|
53
51
|
return [] if actual <= @max_allowed_methods
|
54
52
|
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
"has at least #{actual} methods",
|
54
|
+
@source, SMELL_SUBCLASS,
|
55
|
+
METHOD_COUNT_KEY => actual)
|
58
56
|
[smell]
|
59
57
|
end
|
60
58
|
end
|
@@ -3,16 +3,14 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# A Long Method is any method that has a large number of lines.
|
9
8
|
#
|
10
9
|
# +TooManyStatements+ reports any method with more than 5 statements.
|
11
10
|
#
|
12
11
|
class TooManyStatements < SmellDetector
|
13
|
-
|
14
12
|
SMELL_CLASS = 'LongMethod'
|
15
|
-
SMELL_SUBCLASS =
|
13
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
16
14
|
|
17
15
|
STATEMENT_COUNT_KEY = 'statement_count'
|
18
16
|
|
@@ -39,9 +37,9 @@ module Reek
|
|
39
37
|
num = ctx.num_statements
|
40
38
|
return [] if num <= @max_allowed_statements
|
41
39
|
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
"has approx #{num} statements",
|
41
|
+
@source, SMELL_SUBCLASS,
|
42
|
+
STATEMENT_COUNT_KEY => num)
|
45
43
|
[smell]
|
46
44
|
end
|
47
45
|
end
|
@@ -3,11 +3,10 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# An Uncommunicative Name is a name that doesn't communicate its intent
|
9
8
|
# well enough.
|
10
|
-
#
|
9
|
+
#
|
11
10
|
# Poor names make it hard for the reader to build a mental picture
|
12
11
|
# of what's going on in the code. They can also be mis-interpreted;
|
13
12
|
# and they hurt the flow of reading, because the reader must slow
|
@@ -18,9 +17,8 @@ module Reek
|
|
18
17
|
# * names ending with a number
|
19
18
|
#
|
20
19
|
class UncommunicativeMethodName < SmellDetector
|
21
|
-
|
22
20
|
SMELL_CLASS = 'UncommunicativeName'
|
23
|
-
SMELL_SUBCLASS =
|
21
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
24
22
|
METHOD_NAME_KEY = 'method_name'
|
25
23
|
|
26
24
|
# The name of the config field that lists the regexps of
|
@@ -59,10 +57,10 @@ module Reek
|
|
59
57
|
return [] if @accept_names.include?(ctx.full_name)
|
60
58
|
var = name.gsub(/^[@\*\&]*/, '')
|
61
59
|
return [] if @accept_names.include?(var)
|
62
|
-
return [] unless @reject_names.
|
60
|
+
return [] unless @reject_names.find { |patt| patt =~ var }
|
63
61
|
smell = SmellWarning.new('UncommunicativeName', ctx.full_name, [ctx.exp.line],
|
64
|
-
|
65
|
-
|
62
|
+
"has the name '#{name}'",
|
63
|
+
@source, 'UncommunicativeMethodName', METHOD_NAME_KEY => name)
|
66
64
|
[smell]
|
67
65
|
end
|
68
66
|
end
|
@@ -3,11 +3,10 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# An Uncommunicative Name is a name that doesn't communicate its intent
|
9
8
|
# well enough.
|
10
|
-
#
|
9
|
+
#
|
11
10
|
# Poor names make it hard for the reader to build a mental picture
|
12
11
|
# of what's going on in the code. They can also be mis-interpreted;
|
13
12
|
# and they hurt the flow of reading, because the reader must slow
|
@@ -18,9 +17,8 @@ module Reek
|
|
18
17
|
# * names ending with a number
|
19
18
|
#
|
20
19
|
class UncommunicativeModuleName < SmellDetector
|
21
|
-
|
22
20
|
SMELL_CLASS = 'UncommunicativeName'
|
23
|
-
SMELL_SUBCLASS =
|
21
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
24
22
|
MODULE_NAME_KEY = 'module_name'
|
25
23
|
|
26
24
|
# The name of the config field that lists the regexps of
|
@@ -62,10 +60,10 @@ module Reek
|
|
62
60
|
return [] if @accept_names.include?(full_name)
|
63
61
|
var = name.gsub(/^[@\*\&]*/, '')
|
64
62
|
return [] if @accept_names.include?(var)
|
65
|
-
return [] unless @reject_names.
|
63
|
+
return [] unless @reject_names.find { |patt| patt =~ var }
|
66
64
|
smell = SmellWarning.new(SMELL_CLASS, full_name, [exp.line],
|
67
|
-
|
68
|
-
|
65
|
+
"has the name '#{name}'",
|
66
|
+
@source, SMELL_SUBCLASS, MODULE_NAME_KEY => name)
|
69
67
|
[smell]
|
70
68
|
end
|
71
69
|
end
|
@@ -3,11 +3,10 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# An Uncommunicative Name is a name that doesn't communicate its intent
|
9
8
|
# well enough.
|
10
|
-
#
|
9
|
+
#
|
11
10
|
# Poor names make it hard for the reader to build a mental picture
|
12
11
|
# of what's going on in the code. They can also be mis-interpreted;
|
13
12
|
# and they hurt the flow of reading, because the reader must slow
|
@@ -18,9 +17,8 @@ module Reek
|
|
18
17
|
# * names ending with a number
|
19
18
|
#
|
20
19
|
class UncommunicativeParameterName < SmellDetector
|
21
|
-
|
22
20
|
SMELL_CLASS = 'UncommunicativeName'
|
23
|
-
SMELL_SUBCLASS =
|
21
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
24
22
|
PARAMETER_NAME_KEY = 'parameter_name'
|
25
23
|
|
26
24
|
# The name of the config field that lists the regexps of
|
@@ -57,18 +55,18 @@ module Reek
|
|
57
55
|
@accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
|
58
56
|
context_expression = ctx.exp
|
59
57
|
context_expression.parameter_names.select do |name|
|
60
|
-
|
58
|
+
bad_name?(name) && ctx.uses_param?(name)
|
61
59
|
end.map do |name|
|
62
60
|
SmellWarning.new(SMELL_CLASS, ctx.full_name, [context_expression.line],
|
63
61
|
"has the parameter name '#{name}'",
|
64
|
-
@source, SMELL_SUBCLASS,
|
62
|
+
@source, SMELL_SUBCLASS, PARAMETER_NAME_KEY => name.to_s)
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|
68
|
-
def
|
66
|
+
def bad_name?(name)
|
69
67
|
var = name.to_s.gsub(/^[@\*\&]*/, '')
|
70
|
-
return false if var == '*'
|
71
|
-
@reject_names.
|
68
|
+
return false if var == '*' || @accept_names.include?(var)
|
69
|
+
@reject_names.find { |patt| patt =~ var }
|
72
70
|
end
|
73
71
|
end
|
74
72
|
end
|
@@ -3,11 +3,10 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# An Uncommunicative Name is a name that doesn't communicate its intent
|
9
8
|
# well enough.
|
10
|
-
#
|
9
|
+
#
|
11
10
|
# Poor names make it hard for the reader to build a mental picture
|
12
11
|
# of what's going on in the code. They can also be mis-interpreted;
|
13
12
|
# and they hurt the flow of reading, because the reader must slow
|
@@ -18,9 +17,8 @@ module Reek
|
|
18
17
|
# * names ending with a number
|
19
18
|
#
|
20
19
|
class UncommunicativeVariableName < SmellDetector
|
21
|
-
|
22
20
|
SMELL_CLASS = 'UncommunicativeName'
|
23
|
-
SMELL_SUBCLASS =
|
21
|
+
SMELL_SUBCLASS = name.split(/::/)[-1]
|
24
22
|
VARIABLE_NAME_KEY = 'variable_name'
|
25
23
|
|
26
24
|
# The name of the config field that lists the regexps of
|
@@ -55,37 +53,37 @@ module Reek
|
|
55
53
|
def examine_context(ctx)
|
56
54
|
@reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
|
57
55
|
@accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
|
58
|
-
variable_names(ctx.exp).select do |name,
|
59
|
-
|
56
|
+
variable_names(ctx.exp).select do |name, _lines|
|
57
|
+
bad_name?(name, ctx)
|
60
58
|
end.map do |name, lines|
|
61
59
|
SmellWarning.new(SMELL_CLASS, ctx.full_name, lines,
|
62
60
|
"has the variable name '#{name}'",
|
63
|
-
@source, SMELL_SUBCLASS,
|
61
|
+
@source, SMELL_SUBCLASS, VARIABLE_NAME_KEY => name.to_s)
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
67
|
-
def
|
65
|
+
def bad_name?(name, _ctx)
|
68
66
|
var = name.to_s.gsub(/^[@\*\&]*/, '')
|
69
67
|
return false if @accept_names.include?(var)
|
70
|
-
@reject_names.
|
68
|
+
@reject_names.find { |patt| patt =~ var }
|
71
69
|
end
|
72
70
|
|
73
71
|
def variable_names(exp)
|
74
|
-
result = Hash.new {|hash, key| hash[key] = []}
|
72
|
+
result = Hash.new { |hash, key| hash[key] = [] }
|
75
73
|
find_assignment_variable_names(exp, result)
|
76
74
|
find_block_argument_variable_names(exp, result)
|
77
|
-
result.to_a.sort_by {|name, _| name.to_s}
|
75
|
+
result.to_a.sort_by { |name, _| name.to_s }
|
78
76
|
end
|
79
77
|
|
80
78
|
def find_assignment_variable_names(exp, accumulator)
|
81
79
|
assignment_nodes = exp.each_node(:lasgn, [:class, :module, :defs, :defn])
|
82
80
|
|
83
81
|
case exp.first
|
84
|
-
|
85
|
-
|
82
|
+
when :class, :module
|
83
|
+
assignment_nodes += exp.each_node(:iasgn, [:class, :module])
|
86
84
|
end
|
87
85
|
|
88
|
-
assignment_nodes.each {|asgn| accumulator[asgn[1]].push(asgn.line) }
|
86
|
+
assignment_nodes.each { |asgn| accumulator[asgn[1]].push(asgn.line) }
|
89
87
|
end
|
90
88
|
|
91
89
|
def find_block_argument_variable_names(exp, accumulator)
|
@@ -115,10 +113,9 @@ module Reek
|
|
115
113
|
|
116
114
|
def record_variable_name(exp, symbol, accumulator)
|
117
115
|
varname = symbol.to_s.sub(/^\*/, '')
|
118
|
-
if varname
|
119
|
-
|
120
|
-
|
121
|
-
end
|
116
|
+
return if varname == ''
|
117
|
+
var = varname.to_sym
|
118
|
+
accumulator[var].push(exp.line)
|
122
119
|
end
|
123
120
|
end
|
124
121
|
end
|
@@ -3,81 +3,41 @@ require 'reek/smell_warning'
|
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
|
-
|
7
6
|
#
|
8
7
|
# Methods should use their parameters.
|
9
8
|
#
|
10
9
|
class UnusedParameters < SmellDetector
|
11
|
-
|
12
10
|
SMELL_CLASS = 'UnusedCode'
|
13
11
|
SMELL_SUBCLASS = name.split(/::/)[-1]
|
14
12
|
|
15
13
|
PARAMETER_KEY = 'parameter'
|
16
14
|
|
17
|
-
EMPTY_ARRAY = [].freeze
|
18
|
-
EMPTY_STRING = ''.freeze
|
19
|
-
SPLAT_MATCH = /^\*/.freeze
|
20
|
-
UNDERSCORE = '_'.freeze
|
21
|
-
|
22
15
|
#
|
23
16
|
# Checks whether the given method has any unused parameters.
|
24
17
|
#
|
25
18
|
# @return [Array<SmellWarning>]
|
26
19
|
#
|
27
20
|
def examine_context(method_ctx)
|
28
|
-
return
|
29
|
-
|
21
|
+
return [] if method_ctx.uses_super_with_implicit_arguments?
|
22
|
+
method_ctx.unused_params.map do |param|
|
30
23
|
smell_warning(method_ctx, param)
|
31
24
|
end
|
32
25
|
end
|
33
26
|
|
34
27
|
private
|
35
28
|
|
36
|
-
def unused_params(method_ctx)
|
37
|
-
params(method_ctx).select do |param|
|
38
|
-
param = sanitized_param(param)
|
39
|
-
next if skip?(param)
|
40
|
-
!method_ctx.uses_param?(param)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def skip?(param)
|
45
|
-
anonymous_splat?(param) || marked_unused?(param)
|
46
|
-
end
|
47
|
-
|
48
|
-
def params(method_ctx)
|
49
|
-
method_ctx.exp.arg_names || EMPTY_ARRAY
|
50
|
-
end
|
51
|
-
|
52
|
-
def sanitized_param(param)
|
53
|
-
param.to_s.sub(SPLAT_MATCH, EMPTY_STRING)
|
54
|
-
end
|
55
|
-
|
56
|
-
def marked_unused?(param)
|
57
|
-
param.start_with?(UNDERSCORE)
|
58
|
-
end
|
59
|
-
|
60
|
-
def anonymous_splat?(param)
|
61
|
-
param == EMPTY_STRING
|
62
|
-
end
|
63
|
-
|
64
|
-
def zsuper?(method_ctx)
|
65
|
-
method_ctx.exp.body.has_nested_node? :zsuper
|
66
|
-
end
|
67
|
-
|
68
29
|
def smell_warning(method_ctx, param)
|
69
|
-
param_name = param.to_s
|
30
|
+
param_name = param.name.to_s
|
70
31
|
SmellWarning.new(
|
71
32
|
SMELL_CLASS,
|
72
33
|
method_ctx.full_name,
|
73
|
-
[
|
34
|
+
[method_ctx.exp.line],
|
74
35
|
"has unused parameter '#{param_name}'",
|
75
36
|
@source,
|
76
37
|
SMELL_SUBCLASS,
|
77
|
-
|
38
|
+
PARAMETER_KEY => param_name
|
78
39
|
)
|
79
40
|
end
|
80
|
-
|
81
41
|
end
|
82
42
|
end
|
83
43
|
end
|