reek 4.4.2 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -1
- data/CHANGELOG.md +16 -0
- data/CONTRIBUTING.md +52 -23
- data/README.md +16 -3
- data/ataru_setup.rb +1 -1
- data/docs/API.md +1 -4
- data/docs/How-reek-works-internally.md +5 -5
- data/docs/Style-Guide.md +7 -0
- data/features/command_line_interface/options.feature +1 -0
- data/features/command_line_interface/show_progress.feature +33 -0
- data/features/configuration_files/masking_smells.feature +0 -1
- data/features/configuration_via_source_comments/erroneous_source_comments.feature +18 -4
- data/features/configuration_via_source_comments/well_formed_source_comments.feature +116 -0
- data/features/step_definitions/sample_file_steps.rb +5 -0
- data/features/todo_list.feature +42 -14
- data/lib/reek.rb +1 -1
- data/lib/reek/cli/application.rb +5 -0
- data/lib/reek/cli/command/report_command.rb +6 -1
- data/lib/reek/cli/command/todo_list_command.rb +1 -2
- data/lib/reek/cli/options.rb +19 -3
- data/lib/reek/code_comment.rb +83 -11
- data/lib/reek/configuration/configuration_validator.rb +2 -2
- data/lib/reek/errors/bad_detector_in_comment_error.rb +35 -0
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +34 -0
- data/lib/reek/examiner.rb +36 -14
- data/lib/reek/report.rb +28 -9
- data/lib/reek/report/base_report.rb +77 -0
- data/lib/reek/report/code_climate.rb +4 -0
- data/lib/reek/report/code_climate/code_climate_fingerprint.rb +48 -0
- data/lib/reek/report/code_climate/code_climate_formatter.rb +5 -0
- data/lib/reek/report/code_climate/code_climate_report.rb +19 -0
- data/lib/reek/report/formatter.rb +5 -56
- data/lib/reek/report/{heading_formatter.rb → formatter/heading_formatter.rb} +4 -4
- data/lib/reek/report/formatter/location_formatter.rb +41 -0
- data/lib/reek/report/formatter/progress_formatter.rb +80 -0
- data/lib/reek/report/formatter/simple_warning_formatter.rb +35 -0
- data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +35 -0
- data/lib/reek/report/html_report.rb +21 -0
- data/lib/reek/report/{html_report.html.erb → html_report/html_report.html.erb} +0 -0
- data/lib/reek/report/json_report.rb +18 -0
- data/lib/reek/report/text_report.rb +68 -0
- data/lib/reek/report/xml_report.rb +61 -0
- data/lib/reek/report/yaml_report.rb +18 -0
- data/lib/reek/smell_detectors.rb +30 -0
- data/lib/reek/{smells → smell_detectors}/attribute.rb +3 -3
- data/lib/reek/{smells/smell_detector.rb → smell_detectors/base_detector.rb} +3 -3
- data/lib/reek/{smells → smell_detectors}/boolean_parameter.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/class_variable.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/control_parameter.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/data_clump.rb +3 -3
- data/lib/reek/{smells/smell_repository.rb → smell_detectors/detector_repository.rb} +9 -9
- data/lib/reek/{smells → smell_detectors}/duplicate_method_call.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/feature_envy.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/instance_variable_assumption.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/irresponsible_module.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/long_parameter_list.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/long_yield_list.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/manual_dispatch.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/module_initialize.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/nested_iterators.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/nil_check.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/prima_donna_method.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/repeated_conditional.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/smell_configuration.rb +1 -1
- data/lib/reek/{smells → smell_detectors}/smell_warning.rb +1 -1
- data/lib/reek/{smells → smell_detectors}/subclassed_from_core_class.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/too_many_constants.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/too_many_instance_variables.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/too_many_methods.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/too_many_statements.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/uncommunicative_method_name.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/uncommunicative_module_name.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/uncommunicative_parameter_name.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/uncommunicative_variable_name.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/unused_parameters.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/unused_private_method.rb +3 -3
- data/lib/reek/{smells → smell_detectors}/utility_function.rb +3 -3
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +1 -1
- data/spec/factories/factories.rb +6 -6
- data/spec/reek/cli/command/report_command_spec.rb +3 -1
- data/spec/reek/cli/options_spec.rb +12 -2
- data/spec/reek/code_comment_spec.rb +18 -6
- data/spec/reek/configuration/app_configuration_spec.rb +12 -12
- data/spec/reek/configuration/default_directive_spec.rb +1 -1
- data/spec/reek/configuration/directory_directives_spec.rb +2 -2
- data/spec/reek/examiner_spec.rb +56 -28
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +122 -0
- data/spec/reek/report/{code_climate_formatter_spec.rb → code_climate/code_climate_formatter_spec.rb} +6 -2
- data/spec/reek/report/{code_climate_report_spec.rb → code_climate/code_climate_report_spec.rb} +7 -5
- data/spec/reek/report/{location_formatter_spec.rb → formatter/location_formatter_spec.rb} +4 -4
- data/spec/reek/report/formatter/progress_formatter_spec.rb +68 -0
- data/spec/reek/report/html_report_spec.rb +1 -1
- data/spec/reek/report/json_report_spec.rb +2 -2
- data/spec/reek/report/text_report_spec.rb +3 -4
- data/spec/reek/report/xml_report_spec.rb +1 -1
- data/spec/reek/report/yaml_report_spec.rb +2 -2
- data/spec/reek/report_spec.rb +3 -3
- data/spec/reek/{smells → smell_detectors}/attribute_spec.rb +2 -2
- data/spec/reek/{smells/smell_detector_spec.rb → smell_detectors/base_detector_spec.rb} +5 -5
- data/spec/reek/{smells → smell_detectors}/boolean_parameter_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/class_variable_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/control_parameter_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/data_clump_spec.rb +2 -2
- data/spec/reek/smell_detectors/detector_repository_spec.rb +22 -0
- data/spec/reek/{smells → smell_detectors}/duplicate_method_call_spec.rb +6 -6
- data/spec/reek/{smells → smell_detectors}/feature_envy_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/instance_variable_assumption_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/irresponsible_module_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/long_parameter_list_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/long_yield_list_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/manual_dispatch_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/module_initialize_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/nested_iterators_spec.rb +4 -4
- data/spec/reek/{smells → smell_detectors}/nil_check_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/prima_donna_method_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/repeated_conditional_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/smell_configuration_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/smell_warning_spec.rb +3 -3
- data/spec/reek/{smells → smell_detectors}/subclassed_from_core_class_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/too_many_constants_spec.rb +3 -3
- data/spec/reek/{smells → smell_detectors}/too_many_instance_variables_spec.rb +3 -3
- data/spec/reek/{smells → smell_detectors}/too_many_methods_spec.rb +3 -3
- data/spec/reek/{smells → smell_detectors}/too_many_statements_spec.rb +3 -3
- data/spec/reek/{smells → smell_detectors}/uncommunicative_method_name_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/uncommunicative_module_name_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/uncommunicative_parameter_name_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/uncommunicative_variable_name_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/unused_parameters_spec.rb +2 -2
- data/spec/reek/{smells → smell_detectors}/unused_private_method_spec.rb +4 -4
- data/spec/reek/{smells → smell_detectors}/utility_function_spec.rb +3 -3
- data/spec/reek/spec/should_reek_of_spec.rb +3 -3
- data/tasks/configuration.rake +2 -2
- metadata +95 -81
- data/features/smells/subclassed_from_core_class.feature +0 -14
- data/features/smells/too_many_constants.feature +0 -19
- data/lib/reek/errors.rb +0 -32
- data/lib/reek/report/location_formatter.rb +0 -39
- data/lib/reek/report/report.rb +0 -229
- data/lib/reek/smells.rb +0 -30
- data/spec/reek/smells/smell_repository_spec.rb +0 -22
@@ -15,6 +15,11 @@ Given(/^a directory called 'clean' containing two clean files$/) do
|
|
15
15
|
write_file('clean/clean_two.rb', contents)
|
16
16
|
end
|
17
17
|
|
18
|
+
Given(/^a directory called 'mixed_files' containing some clean and smelly files$/) do
|
19
|
+
write_file('mixed_files/clean.rb', CLEAN_FILE.read)
|
20
|
+
write_file('mixed_files/dirty.rb', SMELLY_FILE.read)
|
21
|
+
end
|
22
|
+
|
18
23
|
Given(/^a directory called 'smelly' containing two smelly files$/) do
|
19
24
|
contents = SMELLY_FILE.read
|
20
25
|
|
data/features/todo_list.feature
CHANGED
@@ -40,20 +40,6 @@ Feature:
|
|
40
40
|
When I run reek -c .todo.reek smelly.rb
|
41
41
|
Then it succeeds
|
42
42
|
|
43
|
-
Scenario: Respects a configuration file
|
44
|
-
Given the smelly file 'smelly.rb'
|
45
|
-
And a configuration file 'partial_mask.reek'
|
46
|
-
When I run reek -c partial_mask.reek --todo smelly.rb
|
47
|
-
Then it succeeds
|
48
|
-
And a file named ".todo.reek" should exist
|
49
|
-
And the file ".todo.reek" should contain:
|
50
|
-
"""
|
51
|
-
---
|
52
|
-
UncommunicativeVariableName:
|
53
|
-
exclude:
|
54
|
-
- Smelly#x
|
55
|
-
"""
|
56
|
-
|
57
43
|
Scenario: Reacts appropiately when there are no smells
|
58
44
|
Given the clean file 'clean.rb'
|
59
45
|
When I run reek --todo clean.rb
|
@@ -63,3 +49,45 @@ Feature:
|
|
63
49
|
|
64
50
|
'.todo.reek' not generated because there were no smells found!
|
65
51
|
"""
|
52
|
+
|
53
|
+
Scenario: Mercilessly overwrite existing .todo.reek files
|
54
|
+
Given the smelly file 'smelly.rb'
|
55
|
+
And a file named ".todo.reek" with:
|
56
|
+
"""
|
57
|
+
---
|
58
|
+
# smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
|
59
|
+
# so the configuration below will partially mask this
|
60
|
+
UncommunicativeMethodName:
|
61
|
+
enabled: false
|
62
|
+
"""
|
63
|
+
When I run `reek -c .todo.reek smelly.rb`
|
64
|
+
Then it reports:
|
65
|
+
"""
|
66
|
+
smelly.rb -- 1 warning:
|
67
|
+
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
|
68
|
+
"""
|
69
|
+
When I run `reek --todo smelly.rb`
|
70
|
+
Then it succeeds
|
71
|
+
When I run `reek -c .todo.reek smelly.rb`
|
72
|
+
Then it reports nothing
|
73
|
+
|
74
|
+
Scenario: Ignore existing other configuration files that are passed explicitly
|
75
|
+
Given the smelly file 'smelly.rb'
|
76
|
+
And a file named "config.reek" with:
|
77
|
+
"""
|
78
|
+
---
|
79
|
+
# smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
|
80
|
+
# so the configuration below will partially mask this
|
81
|
+
UncommunicativeMethodName:
|
82
|
+
enabled: false
|
83
|
+
"""
|
84
|
+
When I run `reek -c config.reek smelly.rb`
|
85
|
+
Then it reports:
|
86
|
+
"""
|
87
|
+
smelly.rb -- 1 warning:
|
88
|
+
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
|
89
|
+
"""
|
90
|
+
When I run `reek -c config.reek --todo smelly.rb`
|
91
|
+
Then it succeeds
|
92
|
+
When I run `reek -c .todo.reek smelly.rb`
|
93
|
+
Then it reports nothing
|
data/lib/reek.rb
CHANGED
data/lib/reek/cli/application.rb
CHANGED
@@ -54,6 +54,7 @@ module Reek
|
|
54
54
|
def sources
|
55
55
|
if no_source_files_given?
|
56
56
|
if input_was_piped?
|
57
|
+
disable_progress_output_unless_verbose
|
57
58
|
source_from_pipe
|
58
59
|
else
|
59
60
|
working_directory_as_source
|
@@ -89,6 +90,10 @@ module Reek
|
|
89
90
|
def source_from_pipe
|
90
91
|
[$stdin]
|
91
92
|
end
|
93
|
+
|
94
|
+
def disable_progress_output_unless_verbose
|
95
|
+
options.progress_format = :quiet unless options.show_empty
|
96
|
+
end
|
92
97
|
end
|
93
98
|
end
|
94
99
|
end
|
@@ -37,7 +37,8 @@ module Reek
|
|
37
37
|
warning_formatter: warning_formatter,
|
38
38
|
report_formatter: Report::Formatter,
|
39
39
|
sort_by_issue_count: sort_by_issue_count,
|
40
|
-
heading_formatter: heading_formatter
|
40
|
+
heading_formatter: heading_formatter,
|
41
|
+
progress_formatter: progress_formatter.new(sources.length))
|
41
42
|
end
|
42
43
|
|
43
44
|
def report_class
|
@@ -60,6 +61,10 @@ module Reek
|
|
60
61
|
Report.heading_formatter(options.show_empty ? :verbose : :quiet)
|
61
62
|
end
|
62
63
|
|
64
|
+
def progress_formatter
|
65
|
+
Report.progress_formatter(options.progress_format)
|
66
|
+
end
|
67
|
+
|
63
68
|
def sort_by_issue_count
|
64
69
|
options.sorting == :smelliness
|
65
70
|
end
|
data/lib/reek/cli/options.rb
CHANGED
@@ -10,7 +10,8 @@ module Reek
|
|
10
10
|
#
|
11
11
|
# See {file:docs/Command-Line-Options.md} for details.
|
12
12
|
#
|
13
|
-
# :reek:TooManyInstanceVariables: { max_instance_variables:
|
13
|
+
# :reek:TooManyInstanceVariables: { max_instance_variables: 11 }
|
14
|
+
# :reek:TooManyMethods: { max_methods: 17 }
|
14
15
|
# :reek:Attribute: { enabled: false }
|
15
16
|
#
|
16
17
|
class Options
|
@@ -22,6 +23,7 @@ module Reek
|
|
22
23
|
attr_accessor :colored,
|
23
24
|
:config_file,
|
24
25
|
:location_format,
|
26
|
+
:progress_format,
|
25
27
|
:report_format,
|
26
28
|
:show_empty,
|
27
29
|
:show_links,
|
@@ -35,9 +37,10 @@ module Reek
|
|
35
37
|
@parser = OptionParser.new
|
36
38
|
@report_format = :text
|
37
39
|
@location_format = :numbers
|
40
|
+
@progress_format = tty_output? ? :dots : :quiet
|
38
41
|
@show_links = true
|
39
42
|
@smells_to_detect = []
|
40
|
-
@colored =
|
43
|
+
@colored = tty_output?
|
41
44
|
@success_exit_code = DEFAULT_SUCCESS_EXIT_CODE
|
42
45
|
@failure_exit_code = DEFAULT_FAILURE_EXIT_CODE
|
43
46
|
@generate_todo_list = false
|
@@ -53,8 +56,12 @@ module Reek
|
|
53
56
|
|
54
57
|
private
|
55
58
|
|
59
|
+
# TTY output generally means the output will not undergo further
|
60
|
+
# processing by a machine, but will be viewed by a human. This means
|
61
|
+
# features like coloring can be safely enabled by default.
|
62
|
+
#
|
56
63
|
# :reek:UtilityFunction
|
57
|
-
def
|
64
|
+
def tty_output?
|
58
65
|
$stdout.tty?
|
59
66
|
end
|
60
67
|
|
@@ -113,11 +120,13 @@ module Reek
|
|
113
120
|
end
|
114
121
|
end
|
115
122
|
|
123
|
+
# :reek:TooManyStatements: { max_statements: 6 }
|
116
124
|
def set_report_formatting_options
|
117
125
|
parser.separator "\nText format options:"
|
118
126
|
set_up_color_option
|
119
127
|
set_up_verbosity_options
|
120
128
|
set_up_location_formatting_options
|
129
|
+
set_up_progress_formatting_options
|
121
130
|
set_up_sorting_option
|
122
131
|
end
|
123
132
|
|
@@ -149,6 +158,13 @@ module Reek
|
|
149
158
|
end
|
150
159
|
end
|
151
160
|
|
161
|
+
def set_up_progress_formatting_options
|
162
|
+
parser.on('-P', '--[no-]progress',
|
163
|
+
'Show progress of each source as it is examined (default: true)') do |show_progress|
|
164
|
+
self.progress_format = show_progress ? :dots : :quiet
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
152
168
|
def set_up_sorting_option
|
153
169
|
parser.on('--sort-by SORTING', [:smelliness, :none],
|
154
170
|
'Sort reported files by the given criterium:',
|
data/lib/reek/code_comment.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'yaml'
|
3
|
-
|
4
|
-
require_relative '
|
4
|
+
|
5
|
+
require_relative 'smell_detectors/base_detector'
|
6
|
+
require_relative 'errors/bad_detector_in_comment_error'
|
7
|
+
require_relative 'errors/garbage_detector_configuration_in_comment_error'
|
5
8
|
|
6
9
|
module Reek
|
7
10
|
#
|
@@ -32,16 +35,17 @@ module Reek
|
|
32
35
|
#
|
33
36
|
def initialize(comment:, line: nil, source: nil)
|
34
37
|
@original_comment = comment
|
38
|
+
@line = line
|
39
|
+
@source = source
|
35
40
|
@config = Hash.new { |hash, key| hash[key] = {} }
|
36
41
|
|
37
|
-
@original_comment.scan(CONFIGURATION_REGEX) do |
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@config.merge! detector => YAML.load(options || DISABLE_DETECTOR_CONFIGURATION)
|
42
|
+
@original_comment.scan(CONFIGURATION_REGEX) do |detector_name, _option_string, options|
|
43
|
+
CodeCommentValidator.new(detector_name: detector_name,
|
44
|
+
original_comment: original_comment,
|
45
|
+
line: line,
|
46
|
+
source: source,
|
47
|
+
options: options).validate
|
48
|
+
@config.merge! detector_name => YAML.load(options || DISABLE_DETECTOR_CONFIGURATION)
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
@@ -51,7 +55,7 @@ module Reek
|
|
51
55
|
|
52
56
|
private
|
53
57
|
|
54
|
-
attr_reader :original_comment
|
58
|
+
attr_reader :original_comment, :source, :line
|
55
59
|
|
56
60
|
def sanitized_comment
|
57
61
|
@sanitized_comment ||= original_comment.
|
@@ -59,5 +63,73 @@ module Reek
|
|
59
63
|
gsub(SANITIZE_REGEX, ' ').
|
60
64
|
strip
|
61
65
|
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# A typical configuration via code comment looks like this:
|
69
|
+
#
|
70
|
+
# :reek:DuplicateMethodCall { enabled: false }
|
71
|
+
#
|
72
|
+
# There are a lot of ways a user can introduce some errors here:
|
73
|
+
#
|
74
|
+
# 1.) Unknown smell detector
|
75
|
+
# 2.) Garbage in the detector configuration like { thats: a: bad: config }
|
76
|
+
# 3.) Unknown configuration keys (e.g. by doing a simple typo: "exclude" vs. "exlude" )
|
77
|
+
# 4.) Bad data types given as values for those keys
|
78
|
+
# This class validates [1] and [2] at the moment but will also validate [3]
|
79
|
+
# and [4] in the future.
|
80
|
+
#
|
81
|
+
# :reek:TooManyInstanceVariables: { max_instance_variables: 5 }
|
82
|
+
class CodeCommentValidator
|
83
|
+
#
|
84
|
+
# @param detector_name [String] - the detector class that was parsed out of the original
|
85
|
+
# comment, e.g. "DuplicateMethodCall" or "UnknownSmellDetector"
|
86
|
+
# @param original_comment [String] - the original comment as found in the source code
|
87
|
+
# @param line [Integer] - start of the expression the comment belongs to
|
88
|
+
# @param source [String] - path to source file or "string"
|
89
|
+
# @param options [String] - the configuration options as String for the detector that were
|
90
|
+
# extracted from the original comment
|
91
|
+
def initialize(detector_name:, original_comment:, line:, source:, options: {})
|
92
|
+
@detector_name = detector_name
|
93
|
+
@original_comment = original_comment
|
94
|
+
@line = line
|
95
|
+
@source = source
|
96
|
+
@options = options
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Method can raise the following errors:
|
101
|
+
# * Errors::BadDetectorInCommentError
|
102
|
+
# * Errors::GarbageDetectorConfigurationInCommentError
|
103
|
+
# @return [undefined]
|
104
|
+
def validate
|
105
|
+
escalate_bad_detector
|
106
|
+
escalate_bad_detector_configuration
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
attr_reader :detector_name,
|
112
|
+
:original_comment,
|
113
|
+
:line,
|
114
|
+
:source,
|
115
|
+
:options
|
116
|
+
|
117
|
+
def escalate_bad_detector
|
118
|
+
return if SmellDetectors::BaseDetector.valid_detector?(detector_name)
|
119
|
+
raise Errors::BadDetectorInCommentError, detector_name: detector_name,
|
120
|
+
original_comment: original_comment,
|
121
|
+
source: source,
|
122
|
+
line: line
|
123
|
+
end
|
124
|
+
|
125
|
+
def escalate_bad_detector_configuration
|
126
|
+
YAML.load(options || CodeComment::DISABLE_DETECTOR_CONFIGURATION)
|
127
|
+
rescue Psych::SyntaxError
|
128
|
+
raise Errors::GarbageDetectorConfigurationInCommentError, detector_name: detector_name,
|
129
|
+
original_comment: original_comment,
|
130
|
+
source: source,
|
131
|
+
line: line
|
132
|
+
end
|
133
|
+
end
|
62
134
|
end
|
63
135
|
end
|
@@ -14,7 +14,7 @@ module Reek
|
|
14
14
|
true
|
15
15
|
when String
|
16
16
|
begin
|
17
|
-
Reek::
|
17
|
+
Reek::SmellDetectors.const_defined? key
|
18
18
|
rescue NameError
|
19
19
|
false
|
20
20
|
end
|
@@ -27,7 +27,7 @@ module Reek
|
|
27
27
|
when Class
|
28
28
|
key
|
29
29
|
else
|
30
|
-
Reek::
|
30
|
+
Reek::SmellDetectors.const_get key
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
module Errors
|
5
|
+
# Gets raised when trying to configure a detector which is unknown to us.
|
6
|
+
# This might happen for multiple reasons. The users might have a typo in
|
7
|
+
# his comment or he might use a detector that does not exist anymore.
|
8
|
+
class BadDetectorInCommentError < RuntimeError
|
9
|
+
UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-EOS.freeze
|
10
|
+
|
11
|
+
Error: You are trying to configure an unknown smell detector '%s' in one
|
12
|
+
of your source code comments.
|
13
|
+
The source is '%s' and the comment belongs to the expression starting in line %d.
|
14
|
+
Here's the original comment:
|
15
|
+
|
16
|
+
%s
|
17
|
+
|
18
|
+
Please see the Reek docs for:
|
19
|
+
* how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
|
20
|
+
* what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
|
21
|
+
Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
|
22
|
+
|
23
|
+
EOS
|
24
|
+
|
25
|
+
def initialize(detector_name:, source:, line:, original_comment:)
|
26
|
+
message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
|
27
|
+
detector_name,
|
28
|
+
source,
|
29
|
+
line,
|
30
|
+
original_comment)
|
31
|
+
super message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
module Errors
|
5
|
+
# Gets raised when trying to use a configuration for a detector
|
6
|
+
# that can't be parsed into a hash.
|
7
|
+
class GarbageDetectorConfigurationInCommentError < RuntimeError
|
8
|
+
BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-EOS.freeze
|
9
|
+
|
10
|
+
Error: You are trying to configure the smell detector '%s'.
|
11
|
+
Unfortunately we can not parse the configuration you have given.
|
12
|
+
The source is '%s' and the comment belongs to the expression starting in line %d.
|
13
|
+
Here's the original comment:
|
14
|
+
|
15
|
+
%s
|
16
|
+
|
17
|
+
Please see the Reek docs for:
|
18
|
+
* how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
|
19
|
+
* what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
|
20
|
+
Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
|
21
|
+
|
22
|
+
EOS
|
23
|
+
|
24
|
+
def initialize(detector_name:, source:, line:, original_comment:)
|
25
|
+
message = format(BAD_DETECTOR_CONFIGURATION_MESSAGE,
|
26
|
+
detector_name,
|
27
|
+
source,
|
28
|
+
line,
|
29
|
+
original_comment)
|
30
|
+
super message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/reek/examiner.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require_relative 'context_builder'
|
3
|
+
require_relative 'errors/bad_detector_in_comment_error'
|
4
|
+
require_relative 'errors/garbage_detector_configuration_in_comment_error'
|
5
|
+
require_relative 'smell_detectors/detector_repository'
|
3
6
|
require_relative 'source/source_code'
|
4
|
-
require_relative 'smells/smell_repository'
|
5
7
|
|
6
8
|
module Reek
|
7
9
|
#
|
@@ -12,14 +14,30 @@ module Reek
|
|
12
14
|
INCOMPREHENSIBLE_SOURCE_TEMPLATE = <<-EOS.freeze
|
13
15
|
!!!
|
14
16
|
Source %s can not be processed by Reek.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
This is most likely either a bug in your Reek configuration (config file or
|
19
|
+
source code comments) or a Reek bug.
|
20
|
+
|
21
|
+
Please double check your Reek configuration taking the original exception
|
22
|
+
below into account - you might have misspelled a smell detector for instance.
|
23
|
+
(In the future Reek will handle configuration errors more gracefully, something
|
24
|
+
we are working on already).
|
25
|
+
|
26
|
+
If you feel that this is not a problem with your Reek configuration but with
|
27
|
+
Reek itself it would be great if you could report this back to the Reek
|
28
|
+
team by opening up a corresponding issue at https://github.com/troessner/reek/issues.
|
29
|
+
|
30
|
+
Please make sure to include the source in question, the Reek version
|
19
31
|
and the original exception below.
|
20
32
|
|
33
|
+
Exception message:
|
34
|
+
|
35
|
+
%s
|
36
|
+
|
21
37
|
Original exception:
|
38
|
+
|
22
39
|
%s
|
40
|
+
|
23
41
|
!!!
|
24
42
|
EOS
|
25
43
|
#
|
@@ -39,11 +57,11 @@ module Reek
|
|
39
57
|
def initialize(source,
|
40
58
|
filter_by_smells: [],
|
41
59
|
configuration: Configuration::AppConfiguration.default,
|
42
|
-
|
43
|
-
@source
|
44
|
-
@smell_types
|
45
|
-
@
|
46
|
-
|
60
|
+
detector_repository_class: SmellDetectors::DetectorRepository)
|
61
|
+
@source = Source::SourceCode.from(source)
|
62
|
+
@smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
|
63
|
+
@detector_repository = detector_repository_class.new(smell_types: @smell_types,
|
64
|
+
configuration: configuration.directive_for(description))
|
47
65
|
end
|
48
66
|
|
49
67
|
# @return [String] origin of the source being analysed
|
@@ -87,7 +105,7 @@ module Reek
|
|
87
105
|
|
88
106
|
private
|
89
107
|
|
90
|
-
attr_reader :source, :
|
108
|
+
attr_reader :source, :detector_repository
|
91
109
|
|
92
110
|
# Runs the Examiner on the given source to scan for code smells.
|
93
111
|
#
|
@@ -102,11 +120,15 @@ module Reek
|
|
102
120
|
return [] unless syntax_tree
|
103
121
|
begin
|
104
122
|
examine_tree
|
105
|
-
rescue BadDetectorInCommentError
|
123
|
+
rescue Errors::BadDetectorInCommentError,
|
124
|
+
Errors::GarbageDetectorConfigurationInCommentError => exception
|
106
125
|
warn exception
|
107
126
|
[]
|
108
127
|
rescue StandardError => exception
|
109
|
-
warn format(INCOMPREHENSIBLE_SOURCE_TEMPLATE,
|
128
|
+
warn format(INCOMPREHENSIBLE_SOURCE_TEMPLATE,
|
129
|
+
origin,
|
130
|
+
exception.message,
|
131
|
+
exception.backtrace.join("\n\t"))
|
110
132
|
[]
|
111
133
|
end
|
112
134
|
end
|
@@ -117,7 +139,7 @@ module Reek
|
|
117
139
|
|
118
140
|
def examine_tree
|
119
141
|
ContextBuilder.new(syntax_tree).context_tree.flat_map do |element|
|
120
|
-
|
142
|
+
detector_repository.examine(element)
|
121
143
|
end
|
122
144
|
end
|
123
145
|
end
|