reek 3.1 → 3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 = [])
|