reek 3.1 → 3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/{CHANGELOG → CHANGELOG.md} +150 -123
- data/README.md +61 -21
- data/Rakefile +2 -1
- data/bin/reek +1 -0
- data/config/defaults.reek +2 -2
- data/docs/Attribute.md +9 -13
- data/docs/Basic-Smell-Options.md +2 -2
- data/docs/Command-Line-Options.md +2 -2
- data/docs/Too-Many-Instance-Variables.md +1 -1
- data/features/samples.feature +22 -31
- data/features/step_definitions/sample_file_steps.rb +2 -2
- data/features/support/env.rb +1 -0
- data/lib/reek.rb +1 -0
- data/lib/reek/ast/ast_node_class_map.rb +5 -1
- data/lib/reek/ast/node.rb +4 -2
- data/lib/reek/ast/object_refs.rb +9 -5
- data/lib/reek/ast/reference_collector.rb +4 -2
- data/lib/reek/cli/application.rb +12 -9
- data/lib/reek/cli/command.rb +4 -0
- data/lib/reek/cli/input.rb +4 -4
- data/lib/reek/cli/option_interpreter.rb +11 -7
- data/lib/reek/cli/options.rb +42 -40
- data/lib/reek/cli/reek_command.rb +3 -3
- data/lib/reek/cli/warning_collector.rb +7 -3
- data/lib/reek/code_comment.rb +5 -1
- data/lib/reek/configuration/app_configuration.rb +4 -4
- data/lib/reek/context/code_context.rb +19 -17
- data/lib/reek/examiner.rb +8 -6
- data/lib/reek/rake/task.rb +13 -22
- data/lib/reek/report/formatter.rb +5 -1
- data/lib/reek/report/report.rb +46 -44
- data/lib/reek/smells/attribute.rb +42 -24
- data/lib/reek/smells/control_parameter.rb +21 -13
- data/lib/reek/smells/data_clump.rb +17 -9
- data/lib/reek/smells/duplicate_method_call.rb +12 -6
- data/lib/reek/smells/long_parameter_list.rb +2 -2
- data/lib/reek/smells/long_yield_list.rb +4 -4
- data/lib/reek/smells/nested_iterators.rb +4 -2
- data/lib/reek/smells/nil_check.rb +6 -2
- data/lib/reek/smells/repeated_conditional.rb +2 -2
- data/lib/reek/smells/smell_configuration.rb +15 -7
- data/lib/reek/smells/smell_detector.rb +23 -10
- data/lib/reek/smells/smell_repository.rb +9 -16
- data/lib/reek/smells/smell_warning.rb +6 -6
- data/lib/reek/smells/too_many_instance_variables.rb +4 -4
- data/lib/reek/smells/too_many_methods.rb +2 -2
- data/lib/reek/smells/too_many_statements.rb +4 -4
- data/lib/reek/smells/uncommunicative_method_name.rb +5 -5
- data/lib/reek/smells/uncommunicative_module_name.rb +5 -5
- data/lib/reek/smells/uncommunicative_parameter_name.rb +8 -4
- data/lib/reek/smells/uncommunicative_variable_name.rb +8 -4
- data/lib/reek/source/source_code.rb +6 -2
- data/lib/reek/source/source_locator.rb +4 -4
- data/lib/reek/spec/should_reek.rb +9 -4
- data/lib/reek/spec/should_reek_of.rb +8 -5
- data/lib/reek/spec/should_reek_only_of.rb +12 -8
- data/lib/reek/tree_dresser.rb +6 -2
- data/lib/reek/tree_walker.rb +28 -22
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +6 -5
- data/spec/gem/yard_spec.rb +6 -9
- data/spec/reek/code_comment_spec.rb +1 -1
- data/spec/reek/report/xml_report_spec.rb +11 -21
- data/spec/reek/smells/attribute_spec.rb +73 -57
- data/spec/reek/smells/too_many_instance_variables_spec.rb +26 -12
- data/spec/reek/source/source_locator_spec.rb +2 -2
- data/spec/samples/checkstyle.xml +12 -1
- data/spec/spec_helper.rb +1 -0
- metadata +20 -7
- data/spec/samples/unusual_syntax.rb +0 -21
data/lib/reek/cli/command.rb
CHANGED
data/lib/reek/cli/input.rb
CHANGED
@@ -26,9 +26,9 @@ module Reek
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def no_source_files_given?
|
29
|
-
# At this point we have deleted all options from
|
30
|
-
# are paths to the source files. If
|
31
|
-
|
29
|
+
# At this point we have deleted all options from argv. The only remaining entries
|
30
|
+
# are paths to the source files. If argv is empty, this means that no files were given.
|
31
|
+
argv.empty?
|
32
32
|
end
|
33
33
|
|
34
34
|
def working_directory_as_source
|
@@ -36,7 +36,7 @@ module Reek
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def sources_from_argv
|
39
|
-
Source::SourceLocator.new(
|
39
|
+
Source::SourceLocator.new(argv).sources
|
40
40
|
end
|
41
41
|
|
42
42
|
def source_from_pipe
|
@@ -13,11 +13,11 @@ module Reek
|
|
13
13
|
|
14
14
|
extend Forwardable
|
15
15
|
|
16
|
-
def_delegators
|
16
|
+
def_delegators :options, :smells_to_detect
|
17
17
|
|
18
18
|
def initialize(options)
|
19
19
|
@options = options
|
20
|
-
@argv =
|
20
|
+
@argv = options.argv
|
21
21
|
end
|
22
22
|
|
23
23
|
def reporter
|
@@ -30,7 +30,7 @@ module Reek
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def report_class
|
33
|
-
Report.report_class(
|
33
|
+
Report.report_class(options.report_format)
|
34
34
|
end
|
35
35
|
|
36
36
|
def warning_formatter
|
@@ -38,20 +38,24 @@ module Reek
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def warning_formatter_class
|
41
|
-
Report.warning_formatter_class(
|
41
|
+
Report.warning_formatter_class(options.show_links ? :wiki_links : :simple)
|
42
42
|
end
|
43
43
|
|
44
44
|
def location_formatter
|
45
|
-
Report.location_formatter(
|
45
|
+
Report.location_formatter(options.location_format)
|
46
46
|
end
|
47
47
|
|
48
48
|
def heading_formatter
|
49
|
-
Report.heading_formatter(
|
49
|
+
Report.heading_formatter(options.show_empty ? :verbose : :quiet)
|
50
50
|
end
|
51
51
|
|
52
52
|
def sort_by_issue_count
|
53
|
-
|
53
|
+
options.sorting == :smelliness
|
54
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
private_attr_reader :argv, :options
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|
data/lib/reek/cli/options.rb
CHANGED
@@ -23,14 +23,16 @@ module Reek
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def parse
|
26
|
-
|
27
|
-
|
28
|
-
Rainbow.enabled =
|
29
|
-
|
26
|
+
parser.parse!(argv)
|
27
|
+
options.argv = argv
|
28
|
+
Rainbow.enabled = options.colored
|
29
|
+
options
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
+
private_attr_reader :argv, :options, :parser
|
35
|
+
|
34
36
|
def color_support?
|
35
37
|
$stdout.tty?
|
36
38
|
end
|
@@ -44,8 +46,8 @@ module Reek
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def set_banner
|
47
|
-
program_name =
|
48
|
-
|
49
|
+
program_name = parser.program_name
|
50
|
+
parser.banner = <<-EOB.gsub(/^[ ]+/, '')
|
49
51
|
Usage: #{program_name} [options] [files]
|
50
52
|
|
51
53
|
Examples:
|
@@ -60,29 +62,29 @@ module Reek
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def set_alternative_formatter_options
|
63
|
-
|
64
|
-
|
65
|
+
parser.separator "\nReport format:"
|
66
|
+
parser.on(
|
65
67
|
'-f', '--format FORMAT', [:html, :text, :yaml, :json, :xml],
|
66
68
|
'Report smells in the given format:',
|
67
69
|
' html', ' text (default)', ' yaml', ' json', ' xml'
|
68
70
|
) do |opt|
|
69
|
-
|
71
|
+
options.report_format = opt
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
75
|
def set_configuration_options
|
74
|
-
|
75
|
-
|
76
|
+
parser.separator 'Configuration:'
|
77
|
+
parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
|
76
78
|
raise ArgumentError, "Config file #{file} doesn't exist" unless File.exist?(file)
|
77
|
-
|
79
|
+
options.config_file = Pathname.new(file)
|
78
80
|
end
|
79
|
-
|
80
|
-
|
81
|
+
parser.on('--smell SMELL', 'Detect smell SMELL (default: all enabled smells)') do |smell|
|
82
|
+
options.smells_to_detect << smell
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
84
86
|
def set_report_formatting_options
|
85
|
-
|
87
|
+
parser.separator "\nText format options:"
|
86
88
|
set_up_color_option
|
87
89
|
set_up_verbosity_options
|
88
90
|
set_up_location_formatting_options
|
@@ -90,50 +92,50 @@ module Reek
|
|
90
92
|
end
|
91
93
|
|
92
94
|
def set_up_color_option
|
93
|
-
|
94
|
-
|
95
|
+
parser.on('--[no-]color', 'Use colors for the output (default: true)') do |opt|
|
96
|
+
options.colored = opt
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
98
100
|
def set_up_verbosity_options
|
99
|
-
|
100
|
-
|
101
|
-
|
101
|
+
parser.on('-V', '--[no-]empty-headings',
|
102
|
+
'Show headings for smell-free source files (default: false)') do |show_empty|
|
103
|
+
options.show_empty = show_empty
|
102
104
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
105
|
+
parser.on('-U', '--[no-]wiki-links',
|
106
|
+
'Show link to related wiki page for each smell (default: false)') do |show_links|
|
107
|
+
options.show_links = show_links
|
106
108
|
end
|
107
109
|
end
|
108
110
|
|
109
111
|
def set_up_location_formatting_options
|
110
|
-
|
111
|
-
|
112
|
-
|
112
|
+
parser.on('-n', '--[no-]line-numbers',
|
113
|
+
'Show line numbers in the output (default: true)') do |show_numbers|
|
114
|
+
options.location_format = show_numbers ? :numbers : :plain
|
113
115
|
end
|
114
|
-
|
115
|
-
|
116
|
-
|
116
|
+
parser.on('-s', '--single-line',
|
117
|
+
'Show location in editor-compatible single-line-per-smell format') do
|
118
|
+
options.location_format = :single_line
|
117
119
|
end
|
118
120
|
end
|
119
121
|
|
120
122
|
def set_up_sorting_option
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
123
|
+
parser.on('--sort-by SORTING', [:smelliness, :none],
|
124
|
+
'Sort reported files by the given criterium:',
|
125
|
+
' smelliness ("smelliest" files first)',
|
126
|
+
' none (default - output in processing order)') do |sorting|
|
127
|
+
options.sorting = sorting
|
128
|
+
end
|
127
129
|
end
|
128
130
|
|
129
131
|
def set_utility_options
|
130
|
-
|
131
|
-
|
132
|
-
puts
|
132
|
+
parser.separator "\nUtility options:"
|
133
|
+
parser.on_tail('-h', '--help', 'Show this message') do
|
134
|
+
puts parser
|
133
135
|
exit
|
134
136
|
end
|
135
|
-
|
136
|
-
puts "#{
|
137
|
+
parser.on_tail('-v', '--version', 'Show version') do
|
138
|
+
puts "#{parser.program_name} #{Reek::Version::STRING}\n"
|
137
139
|
exit
|
138
140
|
end
|
139
141
|
end
|
@@ -10,7 +10,7 @@ module Reek
|
|
10
10
|
# @api private
|
11
11
|
class ReekCommand < Command
|
12
12
|
def execute(app)
|
13
|
-
|
13
|
+
options.sources.each do |source|
|
14
14
|
reporter.add_examiner Examiner.new(source, smell_names, configuration: app.configuration)
|
15
15
|
end
|
16
16
|
reporter.smells? ? app.report_smells : app.report_success
|
@@ -20,11 +20,11 @@ module Reek
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def reporter
|
23
|
-
@reporter ||=
|
23
|
+
@reporter ||= options.reporter
|
24
24
|
end
|
25
25
|
|
26
26
|
def smell_names
|
27
|
-
@smell_names ||=
|
27
|
+
@smell_names ||= options.smells_to_detect
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -8,16 +8,20 @@ module Reek
|
|
8
8
|
# @api private
|
9
9
|
class WarningCollector
|
10
10
|
def initialize
|
11
|
-
@
|
11
|
+
@warnings_set = Set.new
|
12
12
|
end
|
13
13
|
|
14
14
|
def found_smell(warning)
|
15
|
-
|
15
|
+
warnings_set.add(warning)
|
16
16
|
end
|
17
17
|
|
18
18
|
def warnings
|
19
|
-
|
19
|
+
warnings_set.to_a.sort
|
20
20
|
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
private_attr_reader :warnings_set
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
data/lib/reek/code_comment.rb
CHANGED
@@ -21,7 +21,7 @@ module Reek
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def descriptive?
|
24
|
-
|
24
|
+
text.split(/\s+/).length >= 2
|
25
25
|
end
|
26
26
|
|
27
27
|
protected
|
@@ -32,5 +32,9 @@ module Reek
|
|
32
32
|
# TODO: extend this to all configs -------------------^
|
33
33
|
# TODO: extend to allow configuration of whole smell class, not just subclass
|
34
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
private_attr_reader :text
|
35
39
|
end
|
36
40
|
end
|
@@ -40,9 +40,9 @@ module Reek
|
|
40
40
|
# config_file = #<Pathname:config/defaults.reek>,
|
41
41
|
# argv = [ "lib/reek/spec" ]>
|
42
42
|
def initialize(options = nil)
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
@directory_directives = {}
|
44
|
+
@default_directive = {}
|
45
|
+
@exclude_paths = []
|
46
46
|
|
47
47
|
load options
|
48
48
|
end
|
@@ -56,7 +56,7 @@ module Reek
|
|
56
56
|
|
57
57
|
private
|
58
58
|
|
59
|
-
|
59
|
+
private_attr_writer :exclude_paths
|
60
60
|
|
61
61
|
# @param source_via [String] - the source of the code inspected
|
62
62
|
# Might be a string, STDIN or Filename / Pathname. We're only interested in the source
|
@@ -70,11 +70,11 @@ module Reek
|
|
70
70
|
# @param child [CodeContext] the child context to register
|
71
71
|
def append_child_context(child)
|
72
72
|
child.visibility = tracked_visibility
|
73
|
-
|
73
|
+
children << child
|
74
74
|
end
|
75
75
|
|
76
76
|
def count_statements(num)
|
77
|
-
|
77
|
+
self.num_statements += num
|
78
78
|
end
|
79
79
|
|
80
80
|
def record_call_to(exp)
|
@@ -83,19 +83,19 @@ module Reek
|
|
83
83
|
case type
|
84
84
|
when :lvar, :lvasgn
|
85
85
|
unless exp.object_creation_call?
|
86
|
-
|
86
|
+
refs.record_reference_to(receiver.name, line: exp.line)
|
87
87
|
end
|
88
88
|
when :self
|
89
|
-
|
89
|
+
refs.record_reference_to(:self, line: exp.line)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
93
|
def record_use_of_self
|
94
|
-
|
94
|
+
refs.record_reference_to(:self)
|
95
95
|
end
|
96
96
|
|
97
97
|
def name
|
98
|
-
|
98
|
+
exp.name
|
99
99
|
end
|
100
100
|
|
101
101
|
def local_nodes(type, &blk)
|
@@ -103,7 +103,7 @@ module Reek
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def each_node(type, ignoring, &blk)
|
106
|
-
|
106
|
+
exp.each_node(type, ignoring, &blk)
|
107
107
|
end
|
108
108
|
|
109
109
|
def matches?(candidates)
|
@@ -119,8 +119,7 @@ module Reek
|
|
119
119
|
end
|
120
120
|
|
121
121
|
def full_name
|
122
|
-
context
|
123
|
-
exp.full_name(context)
|
122
|
+
exp.full_name(context ? context.full_name : '')
|
124
123
|
end
|
125
124
|
|
126
125
|
def config_for(detector_class)
|
@@ -140,46 +139,49 @@ module Reek
|
|
140
139
|
# @param names [Array<Symbol>]
|
141
140
|
def track_visibility(visibility, names = [])
|
142
141
|
if names.any?
|
143
|
-
|
142
|
+
children.each do |child|
|
144
143
|
child.visibility = visibility if names.include? child.name
|
145
144
|
end
|
146
145
|
else
|
147
|
-
|
146
|
+
self.tracked_visibility = visibility
|
148
147
|
end
|
149
148
|
end
|
150
149
|
|
151
150
|
def type
|
152
|
-
|
151
|
+
exp.type
|
153
152
|
end
|
154
153
|
|
155
154
|
# Iterate over +self+ and child contexts.
|
156
155
|
def each(&block)
|
157
156
|
yield self
|
158
|
-
|
157
|
+
children.each do |child|
|
159
158
|
child.each(&block)
|
160
159
|
end
|
161
160
|
end
|
162
161
|
|
163
162
|
protected
|
164
163
|
|
165
|
-
attr_writer :visibility
|
164
|
+
attr_writer :num_statements, :visibility
|
166
165
|
|
167
166
|
private
|
168
167
|
|
168
|
+
private_attr_writer :tracked_visibility
|
169
|
+
private_attr_reader :context, :refs
|
170
|
+
|
169
171
|
def tracked_visibility
|
170
172
|
@tracked_visibility ||= :public
|
171
173
|
end
|
172
174
|
|
173
175
|
def config
|
174
|
-
@config ||= if
|
175
|
-
CodeComment.new(
|
176
|
+
@config ||= if exp
|
177
|
+
CodeComment.new(exp.full_comment || '').config
|
176
178
|
else
|
177
179
|
{}
|
178
180
|
end
|
179
181
|
end
|
180
182
|
|
181
183
|
def context_config_for(detector_class)
|
182
|
-
|
184
|
+
context ? context.config_for(detector_class) : {}
|
183
185
|
end
|
184
186
|
end
|
185
187
|
end
|
data/lib/reek/examiner.rb
CHANGED
@@ -39,14 +39,14 @@ module Reek
|
|
39
39
|
# @return [String] description of the source being analysed
|
40
40
|
#
|
41
41
|
def description
|
42
|
-
@description ||=
|
42
|
+
@description ||= source.description
|
43
43
|
end
|
44
44
|
|
45
45
|
#
|
46
46
|
# @return [Array<SmellWarning>] the smells found in the source
|
47
47
|
#
|
48
48
|
def smells
|
49
|
-
@smells ||=
|
49
|
+
@smells ||= collector.warnings
|
50
50
|
end
|
51
51
|
|
52
52
|
#
|
@@ -65,13 +65,15 @@ module Reek
|
|
65
65
|
|
66
66
|
private
|
67
67
|
|
68
|
+
private_attr_reader :configuration, :collector, :smell_types, :source
|
69
|
+
|
68
70
|
def run
|
69
71
|
smell_repository = Smells::SmellRepository.new(source_description: description,
|
70
|
-
smell_types:
|
71
|
-
configuration:
|
72
|
-
syntax_tree =
|
72
|
+
smell_types: smell_types,
|
73
|
+
configuration: configuration)
|
74
|
+
syntax_tree = source.syntax_tree
|
73
75
|
TreeWalker.new(smell_repository, syntax_tree).walk if syntax_tree
|
74
|
-
smell_repository.report_on(
|
76
|
+
smell_repository.report_on(collector)
|
75
77
|
end
|
76
78
|
|
77
79
|
def eligible_smell_types(filter_by_smells = [])
|