reek 3.3.1 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/CHANGELOG.md +6 -0
  4. data/CONTRIBUTING.md +3 -0
  5. data/README.md +12 -4
  6. data/Rakefile +1 -0
  7. data/config/defaults.reek +1 -2
  8. data/docs/Nested-Iterators.md +6 -1
  9. data/docs/Smell-Suppression.md +69 -5
  10. data/docs/Uncommunicative-Module-Name.md +1 -1
  11. data/docs/Utility-Function.md +13 -1
  12. data/docs/style-guide.md +18 -0
  13. data/docs/templates/default/docstring/html/public_api_marker.erb +3 -0
  14. data/docs/templates/default/docstring/setup.rb +41 -0
  15. data/docs/templates/default/fulldoc/html/css/common.css +1 -0
  16. data/docs/yard_plugin.rb +2 -0
  17. data/features/command_line_interface/smell_selection.feature +1 -0
  18. data/features/configuration_files/masking_smells.feature +12 -0
  19. data/features/samples.feature +27 -26
  20. data/features/step_definitions/sample_file_steps.rb +25 -30
  21. data/lib/reek/ast/ast_node_class_map.rb +1 -1
  22. data/lib/reek/ast/node.rb +3 -4
  23. data/lib/reek/ast/object_refs.rb +2 -2
  24. data/lib/reek/ast/reference_collector.rb +0 -1
  25. data/lib/reek/ast/sexp_extensions.rb +1 -2
  26. data/lib/reek/ast/sexp_formatter.rb +1 -2
  27. data/lib/reek/cli/application.rb +0 -1
  28. data/lib/reek/cli/command.rb +0 -1
  29. data/lib/reek/cli/input.rb +2 -1
  30. data/lib/reek/cli/option_interpreter.rb +0 -1
  31. data/lib/reek/cli/options.rb +3 -1
  32. data/lib/reek/cli/reek_command.rb +0 -1
  33. data/lib/reek/cli/silencer.rb +4 -4
  34. data/lib/reek/cli/warning_collector.rb +0 -1
  35. data/lib/reek/code_comment.rb +6 -11
  36. data/lib/reek/configuration/app_configuration.rb +0 -3
  37. data/lib/reek/configuration/configuration_file_finder.rb +4 -1
  38. data/lib/reek/configuration/configuration_validator.rb +1 -0
  39. data/lib/reek/configuration/directory_directives.rb +4 -0
  40. data/lib/reek/configuration/excluded_paths.rb +2 -1
  41. data/lib/reek/context/code_context.rb +11 -4
  42. data/lib/reek/context/method_context.rb +1 -6
  43. data/lib/reek/context/module_context.rb +0 -1
  44. data/lib/reek/context/root_context.rb +0 -1
  45. data/lib/reek/context/singleton_method_context.rb +0 -1
  46. data/lib/reek/examiner.rb +15 -15
  47. data/lib/reek/rake/task.rb +14 -0
  48. data/lib/reek/report.rb +0 -8
  49. data/lib/reek/report/formatter.rb +13 -12
  50. data/lib/reek/report/heading_formatter.rb +2 -1
  51. data/lib/reek/report/location_formatter.rb +0 -3
  52. data/lib/reek/report/report.rb +34 -14
  53. data/lib/reek/smells/attribute.rb +6 -6
  54. data/lib/reek/smells/boolean_parameter.rb +8 -8
  55. data/lib/reek/smells/class_variable.rb +7 -6
  56. data/lib/reek/smells/control_parameter.rb +8 -7
  57. data/lib/reek/smells/data_clump.rb +18 -20
  58. data/lib/reek/smells/duplicate_method_call.rb +10 -6
  59. data/lib/reek/smells/feature_envy.rb +17 -9
  60. data/lib/reek/smells/irresponsible_module.rb +6 -6
  61. data/lib/reek/smells/long_parameter_list.rb +7 -7
  62. data/lib/reek/smells/long_yield_list.rb +10 -9
  63. data/lib/reek/smells/module_initialize.rb +7 -6
  64. data/lib/reek/smells/nested_iterators.rb +5 -6
  65. data/lib/reek/smells/nil_check.rb +4 -5
  66. data/lib/reek/smells/prima_donna_method.rb +5 -5
  67. data/lib/reek/smells/repeated_conditional.rb +9 -6
  68. data/lib/reek/smells/smell_configuration.rb +1 -7
  69. data/lib/reek/smells/smell_detector.rb +28 -25
  70. data/lib/reek/smells/smell_repository.rb +18 -19
  71. data/lib/reek/smells/smell_warning.rb +34 -21
  72. data/lib/reek/smells/too_many_instance_variables.rb +5 -6
  73. data/lib/reek/smells/too_many_methods.rb +6 -6
  74. data/lib/reek/smells/too_many_statements.rb +5 -6
  75. data/lib/reek/smells/uncommunicative_method_name.rb +6 -6
  76. data/lib/reek/smells/uncommunicative_module_name.rb +36 -22
  77. data/lib/reek/smells/uncommunicative_parameter_name.rb +27 -19
  78. data/lib/reek/smells/uncommunicative_variable_name.rb +13 -7
  79. data/lib/reek/smells/unused_parameters.rb +10 -9
  80. data/lib/reek/smells/utility_function.rb +22 -11
  81. data/lib/reek/source/source_code.rb +11 -12
  82. data/lib/reek/source/source_locator.rb +6 -1
  83. data/lib/reek/spec.rb +11 -0
  84. data/lib/reek/spec/should_reek.rb +0 -3
  85. data/lib/reek/spec/should_reek_of.rb +1 -1
  86. data/lib/reek/spec/should_reek_only_of.rb +0 -1
  87. data/lib/reek/tree_dresser.rb +3 -1
  88. data/lib/reek/tree_walker.rb +4 -5
  89. data/lib/reek/version.rb +4 -1
  90. data/reek.gemspec +1 -1
  91. data/spec/factories/factories.rb +17 -6
  92. data/spec/quality/reek_source_spec.rb +3 -1
  93. data/spec/reek/cli/warning_collector_spec.rb +3 -2
  94. data/spec/reek/code_comment_spec.rb +8 -10
  95. data/spec/reek/configuration/directory_directives_spec.rb +2 -2
  96. data/spec/reek/configuration/excluded_paths_spec.rb +2 -2
  97. data/spec/reek/context/method_context_spec.rb +0 -26
  98. data/spec/reek/report/json_report_spec.rb +83 -6
  99. data/spec/reek/report/yaml_report_spec.rb +76 -6
  100. data/spec/reek/smells/attribute_spec.rb +1 -1
  101. data/spec/reek/smells/boolean_parameter_spec.rb +2 -3
  102. data/spec/reek/smells/class_variable_spec.rb +1 -1
  103. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  104. data/spec/reek/smells/data_clump_spec.rb +1 -1
  105. data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
  106. data/spec/reek/smells/feature_envy_spec.rb +1 -0
  107. data/spec/reek/smells/irresponsible_module_spec.rb +1 -1
  108. data/spec/reek/smells/long_parameter_list_spec.rb +1 -1
  109. data/spec/reek/smells/long_yield_list_spec.rb +1 -1
  110. data/spec/reek/smells/nested_iterators_spec.rb +1 -1
  111. data/spec/reek/smells/repeated_conditional_spec.rb +1 -1
  112. data/spec/reek/smells/smell_configuration_spec.rb +9 -9
  113. data/spec/reek/smells/smell_detector_shared.rb +0 -9
  114. data/spec/reek/smells/smell_repository_spec.rb +1 -8
  115. data/spec/reek/smells/smell_warning_spec.rb +3 -2
  116. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  117. data/spec/reek/smells/too_many_methods_spec.rb +2 -4
  118. data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
  119. data/spec/reek/smells/uncommunicative_module_name_spec.rb +22 -5
  120. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +1 -1
  121. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +1 -1
  122. data/spec/reek/smells/utility_function_spec.rb +49 -0
  123. 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
- [SmellWarning.new(self,
42
- context: ctx.full_name,
43
- lines: [ctx.exp.line],
44
- message: "has approx #{count} statements",
45
- parameters: { count: count })]
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
- [SmellWarning.new(self,
58
- context: ctx.full_name,
59
- lines: [ctx.exp.line],
60
- message: "has the name '#{name}'",
61
- parameters: { name: name })]
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
- DEFAULT_REJECT_SET = [/^.$/, /[0-9]$/]
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
- DEFAULT_ACCEPT_SET = ['Inline::C']
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 => DEFAULT_REJECT_SET,
40
- ACCEPT_KEY => DEFAULT_ACCEPT_SET
38
+ REJECT_KEY => DEFAULT_REJECT_PATTERNS,
39
+ ACCEPT_KEY => DEFAULT_ACCEPT_NAMES
41
40
  )
42
41
  end
43
42
 
44
- def self.contexts # :nodoc:
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
- # :reek:Duplication { allow_calls: [ to_s ] }
54
- def examine_context(ctx)
55
- reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
56
- accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
57
- exp = ctx.exp
58
- full_name = ctx.full_name
59
- name = exp.simple_name.to_s
60
- return [] if accept_names.include?(full_name)
61
- var = name.gsub(/^[@\*\&]*/, '')
62
- return [] if accept_names.include?(var)
63
- return [] unless reject_names.find { |patt| patt =~ var }
64
- [SmellWarning.new(self,
65
- context: full_name,
66
- lines: [exp.line],
67
- message: "has the name '#{name}'",
68
- parameters: { name: name })]
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
- DEFAULT_REJECT_SET = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/]
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
- DEFAULT_ACCEPT_SET = []
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 => DEFAULT_REJECT_SET,
40
- ACCEPT_KEY => DEFAULT_ACCEPT_SET
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
- SmellWarning.new(self,
57
- context: ctx.full_name,
58
- lines: [context_expression.line],
59
- message: "has the parameter name '#{name}'",
60
- parameters: { name: name.to_s })
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
- def bad_name?(name)
65
- var = name.to_s.gsub(/^[@\*\&]*/, '')
66
- return false if var == '*' || accept_names.include?(var)
67
- reject_names.find { |patt| patt =~ var }
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
- private
69
+ def reject_patterns(context)
70
+ value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
71
+ end
71
72
 
72
- private_attr_accessor :accept_names, :reject_names
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
- # @api private
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 # :nodoc:
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
- SmellWarning.new(self,
60
- context: ctx.full_name,
61
- lines: lines,
62
- message: "has the variable name '#{name}'",
63
- parameters: { name: name.to_s })
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
- def examine_context(method_ctx)
22
- return [] if method_ctx.uses_super_with_implicit_arguments?
23
- method_ctx.unused_params.map do |param|
24
- SmellWarning.new(self,
25
- context: method_ctx.full_name,
26
- lines: [method_ctx.exp.line],
27
- message: "has unused parameter '#{param.name}'",
28
- parameters: { name: param.name.to_s })
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
- def examine_context(method_ctx)
57
- return [] if method_ctx.singleton_method?
58
- return [] if method_ctx.num_statements == 0
59
- return [] if method_ctx.references_self?
60
- return [] if num_helper_methods(method_ctx).zero?
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
- [SmellWarning.new(self,
63
- context: method_ctx.full_name,
64
- lines: [method_ctx.exp.line],
65
- message: "doesn't depend on instance state",
66
- parameters: { name: method_ctx.full_name })]
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 :description
17
+ attr_reader :origin
20
18
 
21
19
  # Initializer.
22
20
  #
23
- # code - Ruby code as String
24
- # description - 'STDIN', 'string' or a filepath as String
25
- # parser - the parser to use for generating AST's out of the given source
26
- def initialize(code, description, parser: Parser::Ruby22)
27
- @source = code
28
- @description = description
29
- @parser = 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, description)
84
+ ast, comments = parser.parse_with_comments(source, origin)
86
85
  rescue Racc::ParseError, Parser::SyntaxError => error
87
- $stderr.puts "#{description}: #{error.class.name}: #{error}"
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