reek 3.3.1 → 3.4.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 +4 -0
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +3 -0
- data/README.md +12 -4
- data/Rakefile +1 -0
- data/config/defaults.reek +1 -2
- data/docs/Nested-Iterators.md +6 -1
- data/docs/Smell-Suppression.md +69 -5
- data/docs/Uncommunicative-Module-Name.md +1 -1
- data/docs/Utility-Function.md +13 -1
- data/docs/style-guide.md +18 -0
- data/docs/templates/default/docstring/html/public_api_marker.erb +3 -0
- data/docs/templates/default/docstring/setup.rb +41 -0
- data/docs/templates/default/fulldoc/html/css/common.css +1 -0
- data/docs/yard_plugin.rb +2 -0
- data/features/command_line_interface/smell_selection.feature +1 -0
- data/features/configuration_files/masking_smells.feature +12 -0
- data/features/samples.feature +27 -26
- data/features/step_definitions/sample_file_steps.rb +25 -30
- data/lib/reek/ast/ast_node_class_map.rb +1 -1
- data/lib/reek/ast/node.rb +3 -4
- data/lib/reek/ast/object_refs.rb +2 -2
- data/lib/reek/ast/reference_collector.rb +0 -1
- data/lib/reek/ast/sexp_extensions.rb +1 -2
- data/lib/reek/ast/sexp_formatter.rb +1 -2
- data/lib/reek/cli/application.rb +0 -1
- data/lib/reek/cli/command.rb +0 -1
- data/lib/reek/cli/input.rb +2 -1
- data/lib/reek/cli/option_interpreter.rb +0 -1
- data/lib/reek/cli/options.rb +3 -1
- data/lib/reek/cli/reek_command.rb +0 -1
- data/lib/reek/cli/silencer.rb +4 -4
- data/lib/reek/cli/warning_collector.rb +0 -1
- data/lib/reek/code_comment.rb +6 -11
- data/lib/reek/configuration/app_configuration.rb +0 -3
- data/lib/reek/configuration/configuration_file_finder.rb +4 -1
- data/lib/reek/configuration/configuration_validator.rb +1 -0
- data/lib/reek/configuration/directory_directives.rb +4 -0
- data/lib/reek/configuration/excluded_paths.rb +2 -1
- data/lib/reek/context/code_context.rb +11 -4
- data/lib/reek/context/method_context.rb +1 -6
- data/lib/reek/context/module_context.rb +0 -1
- data/lib/reek/context/root_context.rb +0 -1
- data/lib/reek/context/singleton_method_context.rb +0 -1
- data/lib/reek/examiner.rb +15 -15
- data/lib/reek/rake/task.rb +14 -0
- data/lib/reek/report.rb +0 -8
- data/lib/reek/report/formatter.rb +13 -12
- data/lib/reek/report/heading_formatter.rb +2 -1
- data/lib/reek/report/location_formatter.rb +0 -3
- data/lib/reek/report/report.rb +34 -14
- data/lib/reek/smells/attribute.rb +6 -6
- data/lib/reek/smells/boolean_parameter.rb +8 -8
- data/lib/reek/smells/class_variable.rb +7 -6
- data/lib/reek/smells/control_parameter.rb +8 -7
- data/lib/reek/smells/data_clump.rb +18 -20
- data/lib/reek/smells/duplicate_method_call.rb +10 -6
- data/lib/reek/smells/feature_envy.rb +17 -9
- data/lib/reek/smells/irresponsible_module.rb +6 -6
- data/lib/reek/smells/long_parameter_list.rb +7 -7
- data/lib/reek/smells/long_yield_list.rb +10 -9
- data/lib/reek/smells/module_initialize.rb +7 -6
- data/lib/reek/smells/nested_iterators.rb +5 -6
- data/lib/reek/smells/nil_check.rb +4 -5
- data/lib/reek/smells/prima_donna_method.rb +5 -5
- data/lib/reek/smells/repeated_conditional.rb +9 -6
- data/lib/reek/smells/smell_configuration.rb +1 -7
- data/lib/reek/smells/smell_detector.rb +28 -25
- data/lib/reek/smells/smell_repository.rb +18 -19
- data/lib/reek/smells/smell_warning.rb +34 -21
- data/lib/reek/smells/too_many_instance_variables.rb +5 -6
- data/lib/reek/smells/too_many_methods.rb +6 -6
- data/lib/reek/smells/too_many_statements.rb +5 -6
- data/lib/reek/smells/uncommunicative_method_name.rb +6 -6
- data/lib/reek/smells/uncommunicative_module_name.rb +36 -22
- data/lib/reek/smells/uncommunicative_parameter_name.rb +27 -19
- data/lib/reek/smells/uncommunicative_variable_name.rb +13 -7
- data/lib/reek/smells/unused_parameters.rb +10 -9
- data/lib/reek/smells/utility_function.rb +22 -11
- data/lib/reek/source/source_code.rb +11 -12
- data/lib/reek/source/source_locator.rb +6 -1
- data/lib/reek/spec.rb +11 -0
- data/lib/reek/spec/should_reek.rb +0 -3
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/spec/should_reek_only_of.rb +0 -1
- data/lib/reek/tree_dresser.rb +3 -1
- data/lib/reek/tree_walker.rb +4 -5
- data/lib/reek/version.rb +4 -1
- data/reek.gemspec +1 -1
- data/spec/factories/factories.rb +17 -6
- data/spec/quality/reek_source_spec.rb +3 -1
- data/spec/reek/cli/warning_collector_spec.rb +3 -2
- data/spec/reek/code_comment_spec.rb +8 -10
- data/spec/reek/configuration/directory_directives_spec.rb +2 -2
- data/spec/reek/configuration/excluded_paths_spec.rb +2 -2
- data/spec/reek/context/method_context_spec.rb +0 -26
- data/spec/reek/report/json_report_spec.rb +83 -6
- data/spec/reek/report/yaml_report_spec.rb +76 -6
- data/spec/reek/smells/attribute_spec.rb +1 -1
- data/spec/reek/smells/boolean_parameter_spec.rb +2 -3
- 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 +1 -1
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/smells/feature_envy_spec.rb +1 -0
- data/spec/reek/smells/irresponsible_module_spec.rb +1 -1
- data/spec/reek/smells/long_parameter_list_spec.rb +1 -1
- data/spec/reek/smells/long_yield_list_spec.rb +1 -1
- data/spec/reek/smells/nested_iterators_spec.rb +1 -1
- data/spec/reek/smells/repeated_conditional_spec.rb +1 -1
- data/spec/reek/smells/smell_configuration_spec.rb +9 -9
- data/spec/reek/smells/smell_detector_shared.rb +0 -9
- data/spec/reek/smells/smell_repository_spec.rb +1 -8
- data/spec/reek/smells/smell_warning_spec.rb +3 -2
- data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
- data/spec/reek/smells/too_many_methods_spec.rb +2 -4
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +22 -5
- 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/utility_function_spec.rb +49 -0
- metadata +9 -5
@@ -9,7 +9,6 @@ module Reek
|
|
9
9
|
# +TooManyStatements+ reports any method with more than 5 statements.
|
10
10
|
#
|
11
11
|
# See {file:docs/Too-Many-Statements.md} for details.
|
12
|
-
# @api private
|
13
12
|
class TooManyStatements < SmellDetector
|
14
13
|
# The name of the config field that sets the maximum number of
|
15
14
|
# statements permitted in any method.
|
@@ -38,11 +37,11 @@ module Reek
|
|
38
37
|
DEFAULT_MAX_STATEMENTS)
|
39
38
|
count = ctx.num_statements
|
40
39
|
return [] if count <= max_allowed_statements
|
41
|
-
[
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
[smell_warning(
|
41
|
+
context: ctx,
|
42
|
+
lines: [ctx.exp.line],
|
43
|
+
message: "has approx #{count} statements",
|
44
|
+
parameters: { count: count })]
|
46
45
|
end
|
47
46
|
end
|
48
47
|
end
|
@@ -17,7 +17,6 @@ module Reek
|
|
17
17
|
# * names ending with a number
|
18
18
|
#
|
19
19
|
# See {file:docs/Uncommunicative-Method-Name.md} for details.
|
20
|
-
# @api private
|
21
20
|
class UncommunicativeMethodName < SmellDetector
|
22
21
|
# The name of the config field that lists the regexps of
|
23
22
|
# smelly names to be reported.
|
@@ -46,6 +45,7 @@ module Reek
|
|
46
45
|
#
|
47
46
|
# @return [Array<SmellWarning>]
|
48
47
|
#
|
48
|
+
# :reek:TooManyStatements: { max_statements: 9 }
|
49
49
|
def examine_context(ctx)
|
50
50
|
reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
|
51
51
|
accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
|
@@ -54,11 +54,11 @@ module Reek
|
|
54
54
|
var = name.gsub(/^[@\*\&]*/, '')
|
55
55
|
return [] if accept_names.include?(var)
|
56
56
|
return [] unless reject_names.find { |patt| patt =~ var }
|
57
|
-
[
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
[smell_warning(
|
58
|
+
context: ctx,
|
59
|
+
lines: [ctx.exp.line],
|
60
|
+
message: "has the name '#{name}'",
|
61
|
+
parameters: { name: name })]
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -17,18 +17,17 @@ module Reek
|
|
17
17
|
# * names ending with a number
|
18
18
|
#
|
19
19
|
# See {file:docs/Uncommunicative-Module-Name.md} for details.
|
20
|
-
# @api private
|
21
20
|
class UncommunicativeModuleName < SmellDetector
|
22
21
|
# The name of the config field that lists the regexps of
|
23
22
|
# smelly names to be reported.
|
24
23
|
REJECT_KEY = 'reject'
|
25
|
-
|
24
|
+
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/]
|
26
25
|
|
27
26
|
# The name of the config field that lists the specific names that are
|
28
27
|
# to be treated as exceptions; these names will not be reported as
|
29
28
|
# uncommunicative.
|
30
29
|
ACCEPT_KEY = 'accept'
|
31
|
-
|
30
|
+
DEFAULT_ACCEPT_NAMES = []
|
32
31
|
|
33
32
|
def self.smell_category
|
34
33
|
'UncommunicativeName'
|
@@ -36,12 +35,12 @@ module Reek
|
|
36
35
|
|
37
36
|
def self.default_config
|
38
37
|
super.merge(
|
39
|
-
REJECT_KEY =>
|
40
|
-
ACCEPT_KEY =>
|
38
|
+
REJECT_KEY => DEFAULT_REJECT_PATTERNS,
|
39
|
+
ACCEPT_KEY => DEFAULT_ACCEPT_NAMES
|
41
40
|
)
|
42
41
|
end
|
43
42
|
|
44
|
-
def self.contexts
|
43
|
+
def self.contexts
|
45
44
|
[:module, :class]
|
46
45
|
end
|
47
46
|
|
@@ -50,22 +49,37 @@ module Reek
|
|
50
49
|
#
|
51
50
|
# @return [Array<SmellWarning>]
|
52
51
|
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
52
|
+
def examine_context(context)
|
53
|
+
fully_qualified_name = context.full_name
|
54
|
+
exp = context.exp
|
55
|
+
module_name = exp.simple_name
|
56
|
+
|
57
|
+
return [] if acceptable_name?(context: context,
|
58
|
+
module_name: module_name,
|
59
|
+
fully_qualified_name: fully_qualified_name)
|
60
|
+
|
61
|
+
[smell_warning(
|
62
|
+
context: context,
|
63
|
+
lines: [exp.line],
|
64
|
+
message: "has the name '#{module_name}'",
|
65
|
+
parameters: { name: module_name })]
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# FIXME: switch to required kwargs when dropping Ruby 2.0 compatibility
|
71
|
+
# :reek:ControlParameter
|
72
|
+
def acceptable_name?(context: raise, module_name: raise, fully_qualified_name: raise)
|
73
|
+
accept_names(context).any? { |accept_name| fully_qualified_name == accept_name } ||
|
74
|
+
reject_patterns(context).none? { |pattern| module_name.match pattern }
|
75
|
+
end
|
76
|
+
|
77
|
+
def reject_patterns(context)
|
78
|
+
value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
|
79
|
+
end
|
80
|
+
|
81
|
+
def accept_names(context)
|
82
|
+
value(ACCEPT_KEY, context, DEFAULT_ACCEPT_NAMES)
|
69
83
|
end
|
70
84
|
end
|
71
85
|
end
|
@@ -17,18 +17,17 @@ module Reek
|
|
17
17
|
# * names ending with a number
|
18
18
|
#
|
19
19
|
# See {file:docs/Uncommunicative-Parameter-Name.md} for details.
|
20
|
-
# @api private
|
21
20
|
class UncommunicativeParameterName < SmellDetector
|
22
21
|
# The name of the config field that lists the regexps of
|
23
22
|
# smelly names to be reported.
|
24
23
|
REJECT_KEY = 'reject'
|
25
|
-
|
24
|
+
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/]
|
26
25
|
|
27
26
|
# The name of the config field that lists the specific names that are
|
28
27
|
# to be treated as exceptions; these names will not be reported as
|
29
28
|
# uncommunicative.
|
30
29
|
ACCEPT_KEY = 'accept'
|
31
|
-
|
30
|
+
DEFAULT_ACCEPT_NAMES = []
|
32
31
|
|
33
32
|
def self.smell_category
|
34
33
|
'UncommunicativeName'
|
@@ -36,8 +35,8 @@ module Reek
|
|
36
35
|
|
37
36
|
def self.default_config
|
38
37
|
super.merge(
|
39
|
-
REJECT_KEY =>
|
40
|
-
ACCEPT_KEY =>
|
38
|
+
REJECT_KEY => DEFAULT_REJECT_PATTERNS,
|
39
|
+
ACCEPT_KEY => DEFAULT_ACCEPT_NAMES
|
41
40
|
)
|
42
41
|
end
|
43
42
|
|
@@ -47,29 +46,38 @@ module Reek
|
|
47
46
|
# @return [Array<SmellWarning>]
|
48
47
|
#
|
49
48
|
def examine_context(ctx)
|
50
|
-
self.reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
|
51
|
-
self.accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
|
52
49
|
context_expression = ctx.exp
|
53
50
|
context_expression.parameter_names.select do |name|
|
54
|
-
bad_name?(name) && ctx.uses_param?(name)
|
51
|
+
bad_name?(ctx, name) && ctx.uses_param?(name)
|
55
52
|
end.map do |name|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
smell_warning(
|
54
|
+
context: ctx,
|
55
|
+
lines: [context_expression.line],
|
56
|
+
message: "has the parameter name '#{name}'",
|
57
|
+
parameters: { name: name.to_s })
|
61
58
|
end
|
62
59
|
end
|
63
60
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
private
|
62
|
+
|
63
|
+
def bad_name?(ctx, name)
|
64
|
+
sanitized_name = sanitize name
|
65
|
+
return false if sanitized_name == '*' || accept_names(ctx).include?(sanitized_name)
|
66
|
+
reject_patterns(ctx).any? { |pattern| sanitized_name.match pattern }
|
68
67
|
end
|
69
68
|
|
70
|
-
|
69
|
+
def reject_patterns(context)
|
70
|
+
value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
+
def accept_names(context)
|
74
|
+
value(ACCEPT_KEY, context, DEFAULT_ACCEPT_NAMES)
|
75
|
+
end
|
76
|
+
|
77
|
+
# :reek:UtilityFunction
|
78
|
+
def sanitize(name)
|
79
|
+
name.to_s.gsub(/^[@\*\&]*/, '')
|
80
|
+
end
|
73
81
|
end
|
74
82
|
end
|
75
83
|
end
|
@@ -17,7 +17,8 @@ module Reek
|
|
17
17
|
# * names ending with a number
|
18
18
|
#
|
19
19
|
# See {file:docs/Uncommunicative-Variable-Name.md} for details.
|
20
|
-
#
|
20
|
+
#
|
21
|
+
# :reek:DataClump: { max_copies: 4 }
|
21
22
|
class UncommunicativeVariableName < SmellDetector
|
22
23
|
# The name of the config field that lists the regexps of
|
23
24
|
# smelly names to be reported.
|
@@ -41,7 +42,7 @@ module Reek
|
|
41
42
|
)
|
42
43
|
end
|
43
44
|
|
44
|
-
def self.contexts
|
45
|
+
def self.contexts
|
45
46
|
[:module, :class, :def, :defs]
|
46
47
|
end
|
47
48
|
|
@@ -56,11 +57,11 @@ module Reek
|
|
56
57
|
variable_names(ctx.exp).select do |name, _lines|
|
57
58
|
bad_name?(name, ctx)
|
58
59
|
end.map do |name, lines|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
smell_warning(
|
61
|
+
context: ctx,
|
62
|
+
lines: lines,
|
63
|
+
message: "has the variable name '#{name}'",
|
64
|
+
parameters: { name: name.to_s })
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -70,6 +71,7 @@ module Reek
|
|
70
71
|
reject_names.find { |patt| patt =~ var }
|
71
72
|
end
|
72
73
|
|
74
|
+
# :reek:TooManyStatements: { max_statements: 6 }
|
73
75
|
def variable_names(exp)
|
74
76
|
result = Hash.new { |hash, key| hash[key] = [] }
|
75
77
|
find_assignment_variable_names(exp, result)
|
@@ -77,6 +79,7 @@ module Reek
|
|
77
79
|
result.to_a.sort_by { |name, _| name.to_s }
|
78
80
|
end
|
79
81
|
|
82
|
+
# :reek:UtilityFunction
|
80
83
|
def find_assignment_variable_names(exp, accumulator)
|
81
84
|
assignment_nodes = exp.each_node(:lvasgn, [:class, :module, :defs, :def])
|
82
85
|
|
@@ -88,6 +91,8 @@ module Reek
|
|
88
91
|
assignment_nodes.each { |asgn| accumulator[asgn[1]].push(asgn.line) }
|
89
92
|
end
|
90
93
|
|
94
|
+
# :reek:FeatureEnvy
|
95
|
+
# :reek:TooManyStatements: { max_statements: 6 }
|
91
96
|
def find_block_argument_variable_names(exp, accumulator)
|
92
97
|
arg_search_exp = case exp.first
|
93
98
|
when :class, :module
|
@@ -115,6 +120,7 @@ module Reek
|
|
115
120
|
end
|
116
121
|
end
|
117
122
|
|
123
|
+
# :reek:UtilityFunction
|
118
124
|
def record_variable_name(exp, symbol, accumulator)
|
119
125
|
varname = symbol.to_s.sub(/^\*/, '')
|
120
126
|
return if varname == ''
|
@@ -7,7 +7,6 @@ module Reek
|
|
7
7
|
# Methods should use their parameters.
|
8
8
|
#
|
9
9
|
# See {file:docs/Unused-Parameters.md} for details.
|
10
|
-
# @api private
|
11
10
|
class UnusedParameters < SmellDetector
|
12
11
|
def self.smell_category
|
13
12
|
'UnusedCode'
|
@@ -18,14 +17,16 @@ module Reek
|
|
18
17
|
#
|
19
18
|
# @return [Array<SmellWarning>]
|
20
19
|
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
# :reek:FeatureEnvy
|
21
|
+
def examine_context(ctx)
|
22
|
+
return [] if ctx.uses_super_with_implicit_arguments?
|
23
|
+
ctx.unused_params.map do |param|
|
24
|
+
name = param.name.to_s
|
25
|
+
smell_warning(
|
26
|
+
context: ctx,
|
27
|
+
lines: [ctx.exp.line],
|
28
|
+
message: "has unused parameter '#{name}'",
|
29
|
+
parameters: { name: name })
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
@@ -36,8 +36,10 @@ module Reek
|
|
36
36
|
# +FeatureEnvy+ is reported instead.
|
37
37
|
#
|
38
38
|
# See {file:docs/Utility-Function.md} for details.
|
39
|
-
# @api private
|
40
39
|
class UtilityFunction < SmellDetector
|
40
|
+
PUBLIC_METHODS_ONLY_KEY = 'public_methods_only'.freeze
|
41
|
+
PUBLIC_METHODS_ONLY_DEFAULT = false
|
42
|
+
|
41
43
|
def self.smell_category
|
42
44
|
'LowCohesion'
|
43
45
|
end
|
@@ -53,24 +55,33 @@ module Reek
|
|
53
55
|
#
|
54
56
|
# @return [Array<SmellWarning>]
|
55
57
|
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
return [] if
|
60
|
-
return [] if
|
58
|
+
# :reek:FeatureEnvy
|
59
|
+
# :reek:TooManyStatements: { max_statements: 6 }
|
60
|
+
def examine_context(ctx)
|
61
|
+
return [] if ctx.singleton_method?
|
62
|
+
return [] if ctx.num_statements == 0
|
63
|
+
return [] if ctx.references_self?
|
64
|
+
return [] if num_helper_methods(ctx).zero?
|
65
|
+
return [] if ignore_method?(ctx)
|
61
66
|
|
62
|
-
[
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
+
[smell_warning(
|
68
|
+
context: ctx,
|
69
|
+
lines: [ctx.exp.line],
|
70
|
+
message: "doesn't depend on instance state (maybe move it to another class?)",
|
71
|
+
parameters: { name: ctx.full_name })]
|
67
72
|
end
|
68
73
|
|
69
74
|
private
|
70
75
|
|
76
|
+
# :reek:UtilityFunction
|
71
77
|
def num_helper_methods(method_ctx)
|
72
78
|
method_ctx.local_nodes(:send).length
|
73
79
|
end
|
80
|
+
|
81
|
+
def ignore_method?(method_ctx)
|
82
|
+
method_ctx.non_public_visibility? &&
|
83
|
+
value(PUBLIC_METHODS_ONLY_KEY, method_ctx, PUBLIC_METHODS_ONLY_DEFAULT)
|
84
|
+
end
|
74
85
|
end
|
75
86
|
end
|
76
87
|
end
|
@@ -6,27 +6,25 @@ require_relative '../tree_dresser'
|
|
6
6
|
require_relative '../ast/node'
|
7
7
|
|
8
8
|
module Reek
|
9
|
-
# @api private
|
10
9
|
module Source
|
11
10
|
#
|
12
11
|
# A +Source+ object represents a chunk of Ruby source code.
|
13
12
|
#
|
14
|
-
# @api private
|
15
13
|
class SourceCode
|
16
14
|
IO_IDENTIFIER = 'STDIN'
|
17
15
|
STRING_IDENTIFIER = 'string'
|
18
16
|
|
19
|
-
attr_reader :
|
17
|
+
attr_reader :origin
|
20
18
|
|
21
19
|
# Initializer.
|
22
20
|
#
|
23
|
-
# code
|
24
|
-
#
|
25
|
-
# parser
|
26
|
-
def initialize(code,
|
27
|
-
@source
|
28
|
-
@
|
29
|
-
@parser
|
21
|
+
# code - Ruby code as String
|
22
|
+
# origin - 'STDIN', 'string' or a filepath as String
|
23
|
+
# parser - the parser to use for generating AST's out of the given source
|
24
|
+
def initialize(code, origin, parser: Parser::Ruby22)
|
25
|
+
@source = code
|
26
|
+
@origin = origin
|
27
|
+
@parser = parser
|
30
28
|
end
|
31
29
|
|
32
30
|
# Initializes an instance of SourceCode given a source.
|
@@ -38,6 +36,7 @@ module Reek
|
|
38
36
|
# @param source [File|IO|String] - the given source
|
39
37
|
#
|
40
38
|
# @return an instance of SourceCode
|
39
|
+
# :reek:DuplicateMethodCall: { max_calls: 2 }
|
41
40
|
def self.from(source)
|
42
41
|
case source
|
43
42
|
when File then new(source.read, source.path)
|
@@ -82,9 +81,9 @@ module Reek
|
|
82
81
|
@syntax_tree ||=
|
83
82
|
begin
|
84
83
|
begin
|
85
|
-
ast, comments = parser.parse_with_comments(source,
|
84
|
+
ast, comments = parser.parse_with_comments(source, origin)
|
86
85
|
rescue Racc::ParseError, Parser::SyntaxError => error
|
87
|
-
$stderr.puts "#{
|
86
|
+
$stderr.puts "#{origin}: #{error.class.name}: #{error}"
|
88
87
|
end
|
89
88
|
|
90
89
|
# See https://whitequark.github.io/parser/Parser/Source/Comment/Associator.html
|
@@ -7,7 +7,6 @@ module Reek
|
|
7
7
|
#
|
8
8
|
# Finds Ruby source files in a filesystem.
|
9
9
|
#
|
10
|
-
# @api private
|
11
10
|
class SourceLocator
|
12
11
|
# Initialize with the paths we want to search.
|
13
12
|
#
|
@@ -32,6 +31,8 @@ module Reek
|
|
32
31
|
|
33
32
|
private_attr_reader :configuration, :paths
|
34
33
|
|
34
|
+
# :reek:TooManyStatements: { max_statements: 6 }
|
35
|
+
# :reek:NestedIterators: { max_allowed_nesting: 2 }
|
35
36
|
def source_paths
|
36
37
|
paths.each_with_object([]) do |given_path, relevant_paths|
|
37
38
|
print_no_such_file_error(given_path) && next unless given_path.exist?
|
@@ -49,10 +50,12 @@ module Reek
|
|
49
50
|
configuration.path_excluded?(path)
|
50
51
|
end
|
51
52
|
|
53
|
+
# :reek:UtilityFunction
|
52
54
|
def print_no_such_file_error(path)
|
53
55
|
$stderr.puts "Error: No such file - #{path}"
|
54
56
|
end
|
55
57
|
|
58
|
+
# :reek:UtilityFunction
|
56
59
|
def hidden_directory?(path)
|
57
60
|
path.basename.to_s.start_with? '.'
|
58
61
|
end
|
@@ -61,10 +64,12 @@ module Reek
|
|
61
64
|
path_excluded?(path) || hidden_directory?(path)
|
62
65
|
end
|
63
66
|
|
67
|
+
# :reek:UtilityFunction
|
64
68
|
def ruby_file?(path)
|
65
69
|
path.extname == '.rb'
|
66
70
|
end
|
67
71
|
|
72
|
+
# :reek:UtilityFunction
|
68
73
|
def current_directory?(path)
|
69
74
|
[Pathname.new('.'), Pathname.new('./')].include?(path)
|
70
75
|
end
|