reek 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +9 -4
  4. data/CHANGELOG +8 -0
  5. data/Gemfile +6 -4
  6. data/README.md +6 -0
  7. data/docs/API.md +51 -22
  8. data/docs/Configuration-Files.md +12 -1
  9. data/docs/Feature-Envy.md +30 -10
  10. data/docs/How-reek-works-internally.md +109 -39
  11. data/docs/RSpec-matchers.md +26 -22
  12. data/docs/Reek-Driven-Development.md +0 -8
  13. data/docs/Utility-Function.md +8 -10
  14. data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +2 -2
  15. data/features/programmatic_access.feature +21 -2
  16. data/features/samples.feature +3 -1
  17. data/lib/reek.rb +2 -2
  18. data/lib/reek/{core → ast}/ast_node_class_map.rb +8 -8
  19. data/lib/reek/{sexp/sexp_node.rb → ast/node.rb} +47 -6
  20. data/lib/reek/{core → ast}/object_refs.rb +2 -1
  21. data/lib/reek/{core → ast}/reference_collector.rb +2 -1
  22. data/lib/reek/{sexp → ast}/sexp_extensions.rb +10 -5
  23. data/lib/reek/{sexp → ast}/sexp_formatter.rb +7 -5
  24. data/lib/reek/cli/application.rb +1 -0
  25. data/lib/reek/cli/command.rb +1 -0
  26. data/lib/reek/cli/input.rb +4 -1
  27. data/lib/reek/cli/option_interpreter.rb +6 -4
  28. data/lib/reek/cli/options.rb +2 -1
  29. data/lib/reek/cli/reek_command.rb +3 -2
  30. data/lib/reek/cli/silencer.rb +1 -0
  31. data/lib/reek/{core → cli}/warning_collector.rb +2 -1
  32. data/lib/reek/code_comment.rb +36 -0
  33. data/lib/reek/configuration/app_configuration.rb +17 -2
  34. data/lib/reek/configuration/configuration_file_finder.rb +1 -0
  35. data/lib/reek/{core → context}/code_context.rb +7 -5
  36. data/lib/reek/{core → context}/method_context.rb +5 -3
  37. data/lib/reek/{core → context}/module_context.rb +8 -3
  38. data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
  39. data/lib/reek/{core → context}/singleton_method_context.rb +2 -1
  40. data/lib/reek/examiner.rb +82 -0
  41. data/lib/reek/report/formatter.rb +70 -0
  42. data/lib/reek/report/heading_formatter.rb +45 -0
  43. data/lib/reek/report/location_formatter.rb +35 -0
  44. data/lib/reek/report/report.rb +198 -0
  45. data/lib/reek/smells.rb +24 -13
  46. data/lib/reek/smells/attribute.rb +6 -4
  47. data/lib/reek/smells/boolean_parameter.rb +3 -1
  48. data/lib/reek/smells/class_variable.rb +3 -1
  49. data/lib/reek/smells/control_parameter.rb +3 -1
  50. data/lib/reek/smells/data_clump.rb +3 -1
  51. data/lib/reek/smells/duplicate_method_call.rb +3 -1
  52. data/lib/reek/smells/feature_envy.rb +3 -1
  53. data/lib/reek/smells/irresponsible_module.rb +12 -7
  54. data/lib/reek/smells/long_parameter_list.rb +5 -3
  55. data/lib/reek/smells/long_yield_list.rb +3 -1
  56. data/lib/reek/smells/module_initialize.rb +3 -1
  57. data/lib/reek/smells/nested_iterators.rb +3 -1
  58. data/lib/reek/smells/nil_check.rb +3 -1
  59. data/lib/reek/smells/prima_donna_method.rb +3 -1
  60. data/lib/reek/smells/repeated_conditional.rb +5 -3
  61. data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
  62. data/lib/reek/smells/smell_detector.rb +9 -7
  63. data/lib/reek/{core → smells}/smell_repository.rb +3 -2
  64. data/lib/reek/smells/smell_warning.rb +6 -4
  65. data/lib/reek/smells/too_many_instance_variables.rb +3 -1
  66. data/lib/reek/smells/too_many_methods.rb +3 -1
  67. data/lib/reek/smells/too_many_statements.rb +3 -1
  68. data/lib/reek/smells/uncommunicative_method_name.rb +3 -1
  69. data/lib/reek/smells/uncommunicative_module_name.rb +3 -1
  70. data/lib/reek/smells/uncommunicative_parameter_name.rb +3 -1
  71. data/lib/reek/smells/uncommunicative_variable_name.rb +3 -1
  72. data/lib/reek/smells/unused_parameters.rb +3 -1
  73. data/lib/reek/smells/utility_function.rb +5 -2
  74. data/lib/reek/source/source_code.rb +40 -9
  75. data/lib/reek/source/source_locator.rb +30 -12
  76. data/lib/reek/spec/should_reek.rb +5 -4
  77. data/lib/reek/spec/should_reek_of.rb +3 -2
  78. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  79. data/lib/reek/tree_dresser.rb +32 -0
  80. data/lib/reek/tree_walker.rb +182 -0
  81. data/lib/reek/version.rb +1 -1
  82. data/reek.gemspec +3 -3
  83. data/spec/factories/factories.rb +2 -0
  84. data/spec/reek/{sexp/sexp_node_spec.rb → ast/node_spec.rb} +2 -2
  85. data/spec/reek/{core → ast}/object_refs_spec.rb +3 -3
  86. data/spec/reek/{core → ast}/reference_collector_spec.rb +2 -2
  87. data/spec/reek/{sexp → ast}/sexp_extensions_spec.rb +6 -16
  88. data/spec/reek/{sexp → ast}/sexp_formatter_spec.rb +2 -2
  89. data/spec/reek/cli/option_interpreter_spec.rb +2 -1
  90. data/spec/reek/{core → cli}/warning_collector_spec.rb +3 -3
  91. data/spec/reek/{core/code_comment_spec.rb → code_comment_spec.rb} +3 -3
  92. data/spec/reek/configuration/app_configuration_spec.rb +31 -18
  93. data/spec/reek/{core → context}/code_context_spec.rb +14 -15
  94. data/spec/reek/{core → context}/method_context_spec.rb +8 -8
  95. data/spec/reek/{core → context}/module_context_spec.rb +4 -4
  96. data/spec/reek/context/root_context_spec.rb +14 -0
  97. data/spec/reek/{core → context}/singleton_method_context_spec.rb +4 -4
  98. data/spec/reek/{core/examiner_spec.rb → examiner_spec.rb} +3 -42
  99. data/spec/reek/{cli → report}/html_report_spec.rb +5 -5
  100. data/spec/reek/report/json_report_spec.rb +20 -0
  101. data/spec/reek/{cli → report}/text_report_spec.rb +14 -14
  102. data/spec/reek/{cli → report}/xml_report_spec.rb +7 -7
  103. data/spec/reek/report/yaml_report_spec.rb +20 -0
  104. data/spec/reek/smells/attribute_spec.rb +2 -1
  105. data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
  106. data/spec/reek/smells/class_variable_spec.rb +5 -5
  107. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  108. data/spec/reek/smells/data_clump_spec.rb +1 -1
  109. data/spec/reek/smells/duplicate_method_call_spec.rb +3 -3
  110. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  111. data/spec/reek/smells/irresponsible_module_spec.rb +24 -28
  112. data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
  113. data/spec/reek/smells/long_yield_list_spec.rb +2 -2
  114. data/spec/reek/smells/nested_iterators_spec.rb +1 -1
  115. data/spec/reek/smells/nil_check_spec.rb +2 -2
  116. data/spec/reek/smells/prima_donna_method_spec.rb +3 -3
  117. data/spec/reek/smells/repeated_conditional_spec.rb +6 -6
  118. data/spec/reek/{core → smells}/smell_configuration_spec.rb +4 -4
  119. data/spec/reek/smells/smell_detector_shared.rb +2 -2
  120. data/spec/reek/{core → smells}/smell_repository_spec.rb +5 -4
  121. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  122. data/spec/reek/smells/too_many_methods_spec.rb +4 -4
  123. data/spec/reek/smells/too_many_statements_spec.rb +2 -2
  124. data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
  125. data/spec/reek/smells/uncommunicative_module_name_spec.rb +4 -4
  126. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
  127. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
  128. data/spec/reek/smells/utility_function_spec.rb +23 -1
  129. data/spec/reek/source/source_code_spec.rb +1 -1
  130. data/spec/reek/source/source_locator_spec.rb +30 -0
  131. data/spec/reek/spec/should_reek_of_spec.rb +0 -17
  132. data/spec/reek/spec/should_reek_spec.rb +0 -25
  133. data/spec/reek/tree_dresser_spec.rb +16 -0
  134. data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -5
  135. data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
  136. data/spec/samples/configuration/with_excluded_paths.reek +4 -0
  137. data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
  138. data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
  139. data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
  140. data/spec/spec_helper.rb +6 -7
  141. data/tasks/develop.rake +2 -2
  142. metadata +71 -69
  143. data/lib/reek/cli/report/formatter.rb +0 -69
  144. data/lib/reek/cli/report/heading_formatter.rb +0 -45
  145. data/lib/reek/cli/report/location_formatter.rb +0 -34
  146. data/lib/reek/cli/report/report.rb +0 -191
  147. data/lib/reek/core/ast_node.rb +0 -38
  148. data/lib/reek/core/code_comment.rb +0 -37
  149. data/lib/reek/core/examiner.rb +0 -85
  150. data/lib/reek/core/tree_dresser.rb +0 -24
  151. data/lib/reek/core/tree_walker.rb +0 -180
  152. data/lib/reek/source/source_repository.rb +0 -43
  153. data/spec/reek/cli/json_report_spec.rb +0 -20
  154. data/spec/reek/cli/yaml_report_spec.rb +0 -20
  155. data/spec/reek/core/object_source_spec.rb +0 -18
  156. data/spec/reek/core/stop_context_spec.rb +0 -14
  157. data/spec/reek/core/tree_dresser_spec.rb +0 -16
@@ -1,24 +0,0 @@
1
- require_relative 'ast_node_class_map'
2
-
3
- module Reek
4
- module Core
5
- #
6
- # Adorns an abstract syntax tree with mix-in modules to make accessing
7
- # the tree more understandable and less implementation-dependent.
8
- #
9
- class TreeDresser
10
- def initialize(klass_map = ASTNodeClassMap.new)
11
- @klass_map = klass_map
12
- end
13
-
14
- def dress(sexp, comment_map)
15
- return sexp unless sexp.is_a? AST::Node
16
- type = sexp.type
17
- children = sexp.children.map { |child| dress(child, comment_map) }
18
- comments = comment_map[sexp]
19
- @klass_map.klass_for(type).new(type, children,
20
- location: sexp.loc, comments: comments)
21
- end
22
- end
23
- end
24
- end
@@ -1,180 +0,0 @@
1
- require_relative 'method_context'
2
- require_relative 'module_context'
3
- require_relative 'stop_context'
4
- require_relative 'singleton_method_context'
5
-
6
- module Reek
7
- module Core
8
- #
9
- # Traverses a Sexp abstract syntax tree and fires events whenever
10
- # it encounters specific node types.
11
- #
12
- # SMELL: This class is responsible for counting statements and for feeding
13
- # each context to the smell repository.
14
- class TreeWalker
15
- def initialize(smell_repository = Core::SmellRepository.new)
16
- @smell_repository = smell_repository
17
- @element = StopContext.new
18
- end
19
-
20
- def process(exp)
21
- context_processor = "process_#{exp.type}"
22
- if context_processor_exists?(context_processor)
23
- send(context_processor, exp)
24
- else
25
- process_default exp
26
- end
27
- @element
28
- end
29
-
30
- def process_default(exp)
31
- exp.children.each do |child|
32
- process(child) if child.is_a? AST::Node
33
- end
34
- end
35
-
36
- def process_module(exp)
37
- inside_new_context(ModuleContext, exp) do
38
- process_default(exp)
39
- end
40
- end
41
-
42
- alias_method :process_class, :process_module
43
-
44
- def process_def(exp)
45
- inside_new_context(MethodContext, exp) do
46
- count_clause(exp.body)
47
- process_default(exp)
48
- end
49
- end
50
-
51
- def process_defs(exp)
52
- inside_new_context(SingletonMethodContext, exp) do
53
- count_clause(exp.body)
54
- process_default(exp)
55
- end
56
- end
57
-
58
- def process_args(_) end
59
-
60
- #
61
- # Recording of calls to methods and self
62
- #
63
-
64
- def process_send(exp)
65
- @element.record_call_to(exp)
66
- process_default(exp)
67
- end
68
-
69
- alias_method :process_attrasgn, :process_send
70
- alias_method :process_op_asgn, :process_send
71
-
72
- def process_ivar(exp)
73
- @element.record_use_of_self
74
- process_default(exp)
75
- end
76
-
77
- alias_method :process_ivasgn, :process_ivar
78
-
79
- def process_self(_)
80
- @element.record_use_of_self
81
- end
82
-
83
- alias_method :process_zsuper, :process_self
84
-
85
- #
86
- # Statement counting
87
- #
88
-
89
- def process_block(exp)
90
- count_clause(exp.block)
91
- process_default(exp)
92
- end
93
-
94
- def process_begin(exp)
95
- count_statement_list(exp.children)
96
- @element.count_statements(-1)
97
- process_default(exp)
98
- end
99
-
100
- alias_method :process_kwbegin, :process_begin
101
-
102
- def process_if(exp)
103
- count_clause(exp[2])
104
- count_clause(exp[3])
105
- @element.count_statements(-1)
106
- process_default(exp)
107
- end
108
-
109
- def process_while(exp)
110
- count_clause(exp[2])
111
- @element.count_statements(-1)
112
- process_default(exp)
113
- end
114
-
115
- alias_method :process_until, :process_while
116
-
117
- def process_for(exp)
118
- count_clause(exp[3])
119
- @element.count_statements(-1)
120
- process_default(exp)
121
- end
122
-
123
- def process_rescue(exp)
124
- count_clause(exp[1])
125
- @element.count_statements(-1)
126
- process_default(exp)
127
- end
128
-
129
- def process_resbody(exp)
130
- count_statement_list(exp[2..-1].compact)
131
- process_default(exp)
132
- end
133
-
134
- def process_case(exp)
135
- count_clause(exp.else_body)
136
- @element.count_statements(-1)
137
- process_default(exp)
138
- end
139
-
140
- def process_when(exp)
141
- count_clause(exp.body)
142
- process_default(exp)
143
- end
144
-
145
- private
146
-
147
- def context_processor_exists?(name)
148
- respond_to?(name)
149
- end
150
-
151
- def count_clause(sexp)
152
- @element.count_statements(1) if sexp
153
- end
154
-
155
- def count_statement_list(statement_list)
156
- @element.count_statements statement_list.length
157
- end
158
-
159
- def inside_new_context(klass, exp)
160
- scope = klass.new(@element, exp)
161
- push(scope) do
162
- yield
163
- check_smells(exp.type)
164
- end
165
- scope
166
- end
167
-
168
- def check_smells(type)
169
- @smell_repository.examine(@element, type)
170
- end
171
-
172
- def push(scope)
173
- orig = @element
174
- @element = scope
175
- yield
176
- @element = orig
177
- end
178
- end
179
- end
180
- end
@@ -1,43 +0,0 @@
1
- require_relative 'source_code'
2
- require_relative 'source_locator'
3
-
4
- module Reek
5
- module Source
6
- #
7
- # A collection of source code. If the collection is initialized with an array
8
- # it is assumed to be a list of file paths. Otherwise it is assumed to be a
9
- # single unit of Ruby source code.
10
- #
11
- class SourceRepository
12
- attr_reader :description
13
-
14
- def initialize(description, sources)
15
- @description = description
16
- @sources = sources
17
- end
18
-
19
- # TODO: This method is a least partially broken.
20
- # Regardless of how you call reek, be it:
21
- # reek lib/
22
- # reek lib/file_one.rb lib/file_two.rb
23
- # echo "def m; end" | reek
24
- # we *always* end up in the "when Source::SourceCode" branch.
25
- # So it seems like 80% of this method is never used.
26
- def self.parse(source)
27
- case source
28
- when Array
29
- new 'dir', Source::SourceLocator.new(source).sources
30
- when Source::SourceCode
31
- new source.description, [source]
32
- else
33
- src = Source::SourceCode.from source
34
- new src.description, [src]
35
- end
36
- end
37
-
38
- def each(&block)
39
- @sources.each(&block)
40
- end
41
- end
42
- end
43
- end
@@ -1,20 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/examiner'
3
- require_relative '../../../lib/reek/cli/report/report'
4
- require_relative '../../../lib/reek/cli/report/formatter'
5
-
6
- RSpec.describe Reek::CLI::Report::JSONReport do
7
- let(:instance) { Reek::CLI::Report::JSONReport.new }
8
-
9
- context 'empty source' do
10
- let(:examiner) { Reek::Core::Examiner.new('') }
11
-
12
- before do
13
- instance.add_examiner examiner
14
- end
15
-
16
- it 'prints empty json' do
17
- expect { instance.show }.to output(/^\[\]$/).to_stdout
18
- end
19
- end
20
- end
@@ -1,20 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/examiner'
3
- require_relative '../../../lib/reek/cli/report/report'
4
- require_relative '../../../lib/reek/cli/report/formatter'
5
-
6
- RSpec.describe Reek::CLI::Report::YAMLReport do
7
- let(:instance) { Reek::CLI::Report::YAMLReport.new }
8
-
9
- context 'empty source' do
10
- let(:examiner) { Reek::Core::Examiner.new('') }
11
-
12
- before do
13
- instance.add_examiner examiner
14
- end
15
-
16
- it 'prints empty yaml' do
17
- expect { instance.show }.to output(/^--- \[\]\n.*$/).to_stdout
18
- end
19
- end
20
- end
@@ -1,18 +0,0 @@
1
- require_relative '../../spec_helper'
2
-
3
- RSpec.describe Dir do
4
- it 'reports correct smells via the Dir matcher' do
5
- files = Dir['spec/samples/two_smelly_files/*.rb']
6
- expect(files).to reek
7
- expect(files).to reek_of(:UncommunicativeVariableName)
8
- expect(files).not_to reek_of(:LargeClass)
9
- end
10
-
11
- it 'copes with daft file specs' do
12
- expect(Dir['spec/samples/two_smelly_files/*/.rb']).not_to reek
13
- end
14
-
15
- it 'copes with empty array' do
16
- expect([]).not_to reek
17
- end
18
- end
@@ -1,14 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/stop_context'
3
-
4
- RSpec.describe Reek::Core::StopContext do
5
- before :each do
6
- @stop = Reek::Core::StopContext.new
7
- end
8
-
9
- context 'full_name' do
10
- it 'reports full context' do
11
- expect(@stop.full_name).to eq('')
12
- end
13
- end
14
- end
@@ -1,16 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/tree_dresser'
3
-
4
- RSpec.describe Reek::Core::TreeDresser do
5
- let(:ifnode) { Parser::AST::Node.new(:if) }
6
- let(:sendnode) { Parser::AST::Node.new(:send) }
7
- let(:dresser) { described_class.new }
8
-
9
- it 'dresses :if sexp with IfNode' do
10
- expect(dresser.dress(ifnode, {})).to be_a Reek::Sexp::SexpExtensions::IfNode
11
- end
12
-
13
- it 'dresses :send sexp with SendNode' do
14
- expect(dresser.dress(sendnode, {})).to be_a Reek::Sexp::SexpExtensions::SendNode
15
- end
16
- end