reek 4.7.3 → 4.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +1 -0
  4. data/.travis.yml +4 -2
  5. data/CHANGELOG.md +10 -0
  6. data/Gemfile +3 -3
  7. data/README.md +19 -4
  8. data/lib/reek/ast/node.rb +37 -49
  9. data/lib/reek/ast/reference_collector.rb +2 -4
  10. data/lib/reek/ast/sexp_extensions/case.rb +1 -1
  11. data/lib/reek/ast/sexp_extensions/if.rb +8 -1
  12. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  13. data/lib/reek/ast/sexp_extensions/methods.rb +3 -5
  14. data/lib/reek/configuration/configuration_file_finder.rb +3 -3
  15. data/lib/reek/context/code_context.rb +4 -7
  16. data/lib/reek/context/method_context.rb +5 -10
  17. data/lib/reek/context/module_context.rb +3 -3
  18. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +9 -9
  19. data/lib/reek/errors/bad_detector_in_comment_error.rb +7 -7
  20. data/lib/reek/errors/base_error.rb +3 -0
  21. data/lib/reek/errors/encoding_error.rb +16 -11
  22. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +7 -7
  23. data/lib/reek/errors/incomprehensible_source_error.rb +20 -22
  24. data/lib/reek/examiner.rb +18 -14
  25. data/lib/reek/logging_error_handler.rb +7 -5
  26. data/lib/reek/smell_detectors/class_variable.rb +3 -10
  27. data/lib/reek/smell_detectors/duplicate_method_call.rb +1 -1
  28. data/lib/reek/smell_detectors/instance_variable_assumption.rb +1 -9
  29. data/lib/reek/smell_detectors/manual_dispatch.rb +1 -1
  30. data/lib/reek/smell_detectors/module_initialize.rb +1 -1
  31. data/lib/reek/smell_detectors/nested_iterators.rb +2 -1
  32. data/lib/reek/smell_detectors/too_many_constants.rb +1 -1
  33. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +2 -2
  34. data/lib/reek/smell_detectors/utility_function.rb +1 -1
  35. data/lib/reek/source/source_code.rb +9 -23
  36. data/lib/reek/version.rb +1 -1
  37. data/reek.gemspec +2 -2
  38. data/spec/factories/factories.rb +2 -13
  39. data/spec/reek/ast/node_spec.rb +98 -5
  40. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  41. data/spec/reek/ast/sexp_extensions_spec.rb +2 -2
  42. data/spec/reek/cli/application_spec.rb +39 -41
  43. data/spec/reek/cli/command/todo_list_command_spec.rb +2 -2
  44. data/spec/reek/code_comment_spec.rb +32 -32
  45. data/spec/reek/configuration/app_configuration_spec.rb +3 -3
  46. data/spec/reek/configuration/configuration_file_finder_spec.rb +1 -1
  47. data/spec/reek/configuration/directory_directives_spec.rb +3 -3
  48. data/spec/reek/configuration/excluded_paths_spec.rb +1 -1
  49. data/spec/reek/context/code_context_spec.rb +59 -95
  50. data/spec/reek/context/ghost_context_spec.rb +1 -1
  51. data/spec/reek/context/root_context_spec.rb +1 -1
  52. data/spec/reek/errors/base_error_spec.rb +13 -0
  53. data/spec/reek/examiner_spec.rb +72 -29
  54. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
  55. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  56. data/spec/reek/report/xml_report_spec.rb +2 -2
  57. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  58. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  59. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  60. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  61. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  62. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  63. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +3 -3
  64. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  65. data/spec/reek/smell_detectors/unused_private_method_spec.rb +9 -9
  66. data/spec/reek/smell_detectors/utility_function_spec.rb +5 -5
  67. data/spec/reek/smell_warning_spec.rb +8 -8
  68. data/spec/reek/source/source_code_spec.rb +5 -25
  69. data/spec/reek/source/source_locator_spec.rb +6 -6
  70. data/spec/reek/spec/should_reek_of_spec.rb +7 -7
  71. data/spec/reek/spec/should_reek_spec.rb +2 -2
  72. data/spec/reek/spec/smell_matcher_spec.rb +3 -3
  73. data/spec/reek/tree_dresser_spec.rb +11 -12
  74. data/spec/spec_helper.rb +3 -12
  75. metadata +10 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a71c30d4b1f39eee7c330e391cb1e80afa0a2d3b
4
- data.tar.gz: fad4cfa8b8d369ff2e5c6ac5a22f3be8abc03891
3
+ metadata.gz: 03a8001a3b22bcc9d88119e88b42854baec8a7e6
4
+ data.tar.gz: 51eeaee185f5d97ef76ba341a875aad0202c14ad
5
5
  SHA512:
6
- metadata.gz: 15725dcd429906aba55b5dd379f01b1bf5e6eb08dd8df814a8403752c1356481137e0804618a449010c1b6dbb90da5543f21cf9401d2faaacbb6254734b01f68
7
- data.tar.gz: 4a7a38b3b04059de3f93bb90ee14354fc0052f6c8a23ff3f96b8f456643087aa9f51aca3ff22758ef118f4c3fc05c741892a9658c512c0f0f171ee7cbbbfbd9a
6
+ metadata.gz: a8302cbf65eb954433fb21243998da37fdc1755a7a82f10a465f7205c9b697f8498b593584d3fbc857c6d8544bbd49441c42f62ea5049916ad2a02bdf7a6ef94
7
+ data.tar.gz: 3150c68090b560dbdd91e51dc520c63bc6d6367c3efa4772e64205969b22fb0cdf292ed297a3f09ef271f32b9594d9380bc817cc855021e003c641a561606143
@@ -1,16 +1,21 @@
1
- engines:
1
+ version: "2"
2
+ plugins:
2
3
  rubocop:
3
4
  enabled: true
4
5
  reek:
5
6
  enabled: true
6
- ratings:
7
- paths:
8
- - lib/**/*
9
- exclude_paths:
10
- - coverage/
11
- - docs/
12
- - features/
13
- - logo/
14
- - pkg/
15
- - spec/
16
- - tmp/
7
+ duplication:
8
+ enabled: true
9
+ config:
10
+ languages:
11
+ ruby:
12
+ mass_threshold: 35
13
+ exclude_patterns:
14
+ - coverage/
15
+ - docs/
16
+ - features/
17
+ - logo/
18
+ - pkg/
19
+ - samples/
20
+ - spec/
21
+ - tmp/
@@ -117,6 +117,7 @@ RSpec/MultipleExpectations:
117
117
  # FIXME: Update specs to avoid offenses
118
118
  RSpec/NestedGroups:
119
119
  Exclude:
120
+ - 'spec/reek/report/code_climate/code_climate_fingerprint_spec.rb'
120
121
  - 'spec/reek/cli/application_spec.rb'
121
122
 
122
123
  # rubocop-rspec expects a CodeClimate namespace to go with the code_climate directory.
@@ -2,6 +2,7 @@ sudo: false
2
2
  dist: trusty
3
3
  cache: bundler
4
4
  language: ruby
5
+ before_install: gem update --system
5
6
  bundler_args: --without debugging
6
7
  script: bundle exec rake ci
7
8
  rvm:
@@ -9,13 +10,14 @@ rvm:
9
10
  - 2.2
10
11
  - 2.3
11
12
  - 2.4
12
- - jruby-9.1.7.0
13
+ - 2.5
14
+ - jruby-9.1.15.0
13
15
  - jruby-head
14
16
  - ruby-head
15
17
  - rubinius-3
16
18
  matrix:
17
19
  allow_failures:
18
- - rvm: jruby-9.1.7.0
20
+ - rvm: jruby-9.1.15.0
19
21
  - rvm: jruby-head
20
22
  - rvm: ruby-head
21
23
  - rvm: rubinius-3
@@ -1,5 +1,15 @@
1
1
  # Change log
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 4.8.0 (2018-03-05)
6
+
7
+ * (pocke) Support Ruby 2.5
8
+ * (mvz) Print original exception class in certain error messages
9
+ * (mvz) Only include long exception message in CLI output
10
+ * (chastell) Add official support for Ruby 2.5
11
+ * (mvz) Do not detect ModuleInitialize for nested dynamic classes
12
+
3
13
  ## 4.7.3 (2017-11-05)
4
14
 
5
15
  * (mvz) Handle UTF-8 files in all locales
data/Gemfile CHANGED
@@ -9,15 +9,15 @@ group :development do
9
9
  gem 'aruba', '~> 0.14.0'
10
10
  gem 'ataru', '~> 0.2.0'
11
11
  gem 'cucumber', '~> 3.0'
12
- gem 'factory_girl', '~> 4.0'
12
+ gem 'factory_bot', '~> 4.0'
13
13
  gem 'rake', '~> 12.0'
14
14
  gem 'rspec', '~> 3.0'
15
15
  gem 'simplecov', '~> 0.15.0'
16
16
  gem 'yard', '~> 0.9.5'
17
17
 
18
18
  if RUBY_VERSION >= '2.3'
19
- gem 'rubocop', '~> 0.51.0'
20
- gem 'rubocop-rspec', '~> 1.19.0'
19
+ gem 'rubocop', '~> 0.52.0'
20
+ gem 'rubocop-rspec', '~> 1.20'
21
21
  end
22
22
 
23
23
  platforms :mri do
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  - [Overview](#overview)
8
8
  - [Quickstart](#quickstart)
9
9
  - [Example](#example)
10
- - [Supported rubies](#supported-rubies)
10
+ - [Supported Ruby versions](#supported-ruby-versions)
11
11
  - [Fixing Smell Warnings](#fixing-smell-warnings)
12
12
  - [Sources](#sources)
13
13
  - [Code smells](#code-smells)
@@ -93,16 +93,17 @@ demo.rb -- 2 warnings:
93
93
  [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
94
94
  ```
95
95
 
96
- ## Supported rubies
96
+ ## Supported Ruby versions
97
97
 
98
- Reek is officially running on the following MRI rubies:
98
+ Reek is officially supported for the following CRuby versions:
99
99
 
100
100
  - 2.1
101
101
  - 2.2
102
102
  - 2.3
103
103
  - 2.4
104
+ - 2.5
104
105
 
105
- Other rubies like Rubinius or JRuby are not officially supported but should work as well.
106
+ Other Ruby implementations (like Rubinius or JRuby) are not officially supported but should work as well.
106
107
 
107
108
  ## Fixing Smell Warnings
108
109
 
@@ -528,6 +529,20 @@ If you don't feel like getting your hands dirty with code there are still other
528
529
  * Open up an [issue](https://github.com/troessner/reek/issues) and report bugs
529
530
  * Suggest other improvements like additional smells for instance
530
531
 
532
+ ### Running Code Climate locally
533
+
534
+ If you run into Code Climate issues (e.g., go over code duplication
535
+ threshold) you might want to be able to run Code Climate against
536
+ the Reek codebase locally. To do this, you need to do the following:
537
+
538
+ * [install Docker CE](https://docs.docker.com/engine/installation/)
539
+ * [install Code Climate CLI](https://github.com/codeclimate/codeclimate#installation)
540
+ * `gem install codeclimate`
541
+ * `codeclimate engines:install`
542
+
543
+ Now you can run various Code Climate engines,
544
+ e.g., `codeclimate analyze -e duplication`
545
+
531
546
  ## Output formats
532
547
 
533
548
  Reek supports 5 output formats:
@@ -33,55 +33,43 @@ module Reek
33
33
  loc && loc.line
34
34
  end
35
35
 
36
+ def name
37
+ to_s
38
+ end
39
+
36
40
  #
37
- # Carries out a depth-first traversal of this syntax tree, yielding
38
- # every Sexp of type `target_type`. The traversal ignores any node
39
- # whose type is listed in the Array `ignoring`.
40
- # Takes a block as well.
41
- #
42
- # target_type - the type to look for, e.g. :send, :block
43
- # ignoring - types to ignore, e.g. [:casgn, :class, :module]
44
- # blk - block to execute for every hit. Gets passed in the matching element itself.
41
+ # Carries out a depth-first traversal of this syntax tree, yielding every
42
+ # Sexp of the searched for type or types. The traversal stops at any node
43
+ # whose type is listed in `ignoring`.
45
44
  #
46
- # Examples:
47
- # context.each_node(:send, [:mlhs]) do |call_node| .... end
48
- # context.each_node(:lvar).any? { |it| it.var_name == 'something' }
45
+ # If a type is searched for *and* listed in ignoring, it will be yielded
46
+ # but traversal will not continue to its children.
49
47
  #
50
- # Returns an array with all matching nodes.
51
- # TODO: without a block, this doesn't do what one might expect
52
- def each_node(target_type, ignoring = [], &blk)
53
- if block_given?
54
- look_for_type(target_type, ignoring, &blk)
55
- else
56
- result = []
57
- look_for_type(target_type, ignoring) { |exp| result << exp }
58
- result
59
- end
60
- end
61
-
48
+ # If the root's type is ignored, traversal does not stop, unless the root
49
+ # is of a target type.
62
50
  #
63
- # Carries out a depth-first traversal of this syntax tree, yielding
64
- # every Sexp of type `target_type`. The traversal ignores any node
65
- # whose type is listed in the Array `ignoring`, including the top node.
66
51
  # Takes a block as well.
67
52
  #
68
- # target_types - the types to look for, e.g. [:send, :block]
69
- # ignoring - types to ignore, e.g. [:casgn, :class, :module]
70
- # blk - block to execute for every hit
53
+ # @param target_types [Symbol, Array<Symbol>] the type or types to look
54
+ # for
55
+ # @param ignoring [Array<Symbol>] types to ignore
56
+ # @param blk block to execute for every hit. Gets passed in the
57
+ # matching element itself.
71
58
  #
72
- # Examples:
73
- # exp.find_nodes([:block]).flat_map do |elem| ... end
59
+ # @example
60
+ # node.each_node(:send, [:mlhs]) do |call_node| .... end
61
+ # node.each_node(:lvar).any? { |it| it.var_name == 'something' }
62
+ # node.each_node([:block]).flat_map do |elem| ... end
74
63
  #
75
64
  # Returns an array with all matching nodes.
76
- def find_nodes(target_types, ignoring = [])
77
- result = []
78
- look_for_types(target_types, ignoring) { |exp| result << exp }
79
- result
65
+ def each_node(target_types, ignoring = [], &blk)
66
+ return enum_for(:each_node, target_types, ignoring) unless block_given?
67
+
68
+ look_for(Array(target_types), ignoring, &blk)
80
69
  end
81
70
 
82
71
  def contains_nested_node?(target_type)
83
- look_for_type(target_type) { |_elem| return true }
84
- false
72
+ each_node(target_type).any?
85
73
  end
86
74
 
87
75
  # :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
@@ -120,22 +108,22 @@ module Reek
120
108
  protected
121
109
 
122
110
  # See ".each_node" for documentation.
123
- def look_for_type(target_type, ignoring = [], &blk)
111
+ def look_for(target_types, ignoring, &blk)
112
+ if target_types.include? type
113
+ yield self
114
+ return if ignoring.include?(type)
115
+ end
124
116
  each_sexp do |elem|
125
- elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
117
+ elem.look_for_recurse(target_types, ignoring, &blk)
126
118
  end
127
- yield self if type == target_type
128
119
  end
129
120
 
130
- # See ".find_nodes" for documentation.
131
- def look_for_types(target_types, ignoring = [], &blk)
132
- return if ignoring.include?(type)
133
- if target_types.include? type
134
- yield self
135
- else
136
- each_sexp do |elem|
137
- elem.look_for_types(target_types, ignoring, &blk)
138
- end
121
+ # See ".each_node" for documentation.
122
+ def look_for_recurse(target_types, ignoring, &blk)
123
+ yield self if target_types.include? type
124
+ return if ignoring.include? type
125
+ each_sexp do |elem|
126
+ elem.look_for_recurse(target_types, ignoring, &blk)
139
127
  end
140
128
  end
141
129
 
@@ -12,7 +12,7 @@ module Reek
12
12
  end
13
13
 
14
14
  def num_refs_to_self
15
- (explicit_self_calls + implicit_self_calls).size
15
+ (explicit_self_calls.to_a + implicit_self_calls.to_a).size
16
16
  end
17
17
 
18
18
  private
@@ -20,9 +20,7 @@ module Reek
20
20
  attr_reader :ast
21
21
 
22
22
  def explicit_self_calls
23
- [:self, :super, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
24
- ast.each_node(node_type)
25
- end
23
+ ast.each_node([:self, :super, :zsuper, :ivar, :ivasgn])
26
24
  end
27
25
 
28
26
  def implicit_self_calls
@@ -10,7 +10,7 @@ module Reek
10
10
  end
11
11
 
12
12
  def body_nodes(type, ignoring = [])
13
- children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
13
+ children[1..-1].compact.flat_map { |child| child.each_node(type, ignoring).to_a }
14
14
  end
15
15
 
16
16
  def else_body
@@ -9,8 +9,15 @@ module Reek
9
9
  children.first
10
10
  end
11
11
 
12
+ # :reek:FeatureEnvy
12
13
  def body_nodes(type, ignoring = [])
13
- children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
14
+ children[1..-1].compact.flat_map do |child|
15
+ if ignoring.include? child.type
16
+ []
17
+ else
18
+ child.each_node(type, ignoring).to_a
19
+ end
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -10,7 +10,7 @@ module Reek
10
10
  end
11
11
 
12
12
  def body_nodes(type, ignoring = [])
13
- children[1].find_nodes type, ignoring
13
+ children[1].each_node type, ignoring
14
14
  end
15
15
  end
16
16
 
@@ -30,11 +30,9 @@ module Reek
30
30
  end
31
31
 
32
32
  def body_nodes(types, ignoring = [])
33
- if body
34
- body.find_nodes(types, ignoring)
35
- else
36
- []
37
- end
33
+ return [] unless body
34
+ return [] if ignoring.include?(body.type)
35
+ body.each_node(types, ignoring | types)
38
36
  end
39
37
  end
40
38
 
@@ -20,8 +20,8 @@ module Reek
20
20
  module ConfigurationFileFinder
21
21
  TOO_MANY_CONFIGURATION_FILES_MESSAGE = <<-MESSAGE.freeze
22
22
 
23
- Error: Found multiple configuration files %s
24
- while scanning directory %s.
23
+ Error: Found multiple configuration files %<files>s
24
+ while scanning directory %<directory>s.
25
25
 
26
26
  Reek supports only one configuration file. You have 2 options now:
27
27
  1) Remove all offending files.
@@ -111,7 +111,7 @@ module Reek
111
111
  #
112
112
  def escalate_too_many_configuration_files(found, directory)
113
113
  offensive_files = found.map { |file| "'#{file.basename}'" }.join(', ')
114
- warn format(TOO_MANY_CONFIGURATION_FILES_MESSAGE, offensive_files, directory)
114
+ warn format(TOO_MANY_CONFIGURATION_FILES_MESSAGE, files: offensive_files, directory: directory)
115
115
  exit 1
116
116
  end
117
117
  end
@@ -19,7 +19,6 @@ module Reek
19
19
  class CodeContext
20
20
  include Enumerable
21
21
  extend Forwardable
22
- delegate each_node: :exp
23
22
  delegate [:name, :type] => :exp
24
23
 
25
24
  attr_reader :children, :parent, :exp, :statement_counter
@@ -40,12 +39,12 @@ module Reek
40
39
  # @yield block that is executed for every node.
41
40
  #
42
41
  def local_nodes(type, ignored = [], &blk)
43
- ignored += [:casgn, :class, :module]
44
- each_node(type, ignored, &blk)
42
+ ignored |= [:class, :module]
43
+ exp.each_node(type, ignored, &blk)
45
44
  end
46
45
 
47
46
  # Iterate over `self` and child contexts.
48
- # The main difference (among others) to `each_node` is that we are traversing
47
+ # The main difference (among others) to `local_nodes` is that we are traversing
49
48
  # `CodeContexts` here, not AST nodes (see `Reek::AST::Node`).
50
49
  #
51
50
  # @yield block that is executed for every node.
@@ -119,9 +118,7 @@ module Reek
119
118
  line = exp.line
120
119
  case type
121
120
  when :lvar, :lvasgn
122
- unless exp.object_creation_call?
123
- refs.record_reference(name: receiver.name, line: line)
124
- end
121
+ refs.record_reference(name: receiver.name, line: line) unless exp.object_creation_call?
125
122
  when :self
126
123
  refs.record_reference(name: :self, line: line)
127
124
  end
@@ -23,21 +23,16 @@ module Reek
23
23
  end
24
24
 
25
25
  def uses_param?(param)
26
- # local_nodes(:lvasgn) catches:
26
+ # :lvasgn catches:
27
+ #
27
28
  # def foo(bar); bar += 1; end
28
- # In this example there is no `lvar` node present.
29
29
  #
30
- # local_nodes(:lvar) catches:
30
+ # :lvar catches:
31
+ #
31
32
  # def foo(bar); other(bar); end
32
33
  # def foo(bar); tmp = other(bar); tmp[0]; end
33
34
  #
34
- # Note that in the last example the `lvar` node for `bar` is part of an `lvasgn` node for `tmp`.
35
- # This means that if we would just search for [:lvar, :lvasgn]
36
- # (e.g. via Reek::AST::Node#find_nodes) this would fail for this example since we would
37
- # stop at the `lvasgn` and not detect the contained `lvar`.
38
- # Hence we first get all `lvar` nodes followed by all `lvasgn` nodes.
39
- #
40
- (local_nodes(:lvar) + local_nodes(:lvasgn)).find { |node| node.var_name == param.name }
35
+ local_nodes([:lvar, :lvasgn]).find { |node| node.var_name == param.name }
41
36
  end
42
37
 
43
38
  # :reek:FeatureEnvy
@@ -43,9 +43,9 @@ module Reek
43
43
  AttributeContext
44
44
  end
45
45
 
46
- def defined_instance_methods(visibility: :public)
46
+ def defined_instance_methods(visibility: :any)
47
47
  instance_method_children.select do |context|
48
- context.visibility == visibility
48
+ visibility == :any || context.visibility == visibility
49
49
  end
50
50
  end
51
51
 
@@ -59,7 +59,7 @@ module Reek
59
59
  # @deprecated use `defined_instance_methods` instead
60
60
  #
61
61
  def node_instance_methods
62
- local_nodes(:def)
62
+ local_nodes(:def).to_a
63
63
  end
64
64
 
65
65
  def descriptively_commented?