reek 2.0.4 → 3.0.0
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/.gitignore +13 -0
- data/.rubocop.yml +47 -0
- data/.travis.yml +31 -0
- data/.yardopts +3 -6
- data/CHANGELOG +24 -0
- data/CONTRIBUTING.md +111 -0
- data/Gemfile +16 -0
- data/README.md +112 -59
- data/bin/reek +2 -2
- data/config/cucumber.yml +3 -0
- data/config/defaults.reek +0 -1
- data/docs/API.md +79 -0
- data/docs/Attribute.md +43 -0
- data/docs/Basic-Smell-Options.md +44 -0
- data/docs/Boolean-Parameter.md +52 -0
- data/docs/Class-Variable.md +40 -0
- data/docs/Code-Smells.md +34 -0
- data/docs/Command-Line-Options.md +84 -0
- data/docs/Configuration-Files.md +49 -0
- data/docs/Control-Couple.md +22 -0
- data/docs/Control-Parameter.md +29 -0
- data/docs/Data-Clump.md +44 -0
- data/docs/Duplicate-Method-Call.md +49 -0
- data/docs/Feature-Envy.md +49 -0
- data/docs/How-reek-works-internally.md +114 -0
- data/docs/Irresponsible-Module.md +39 -0
- data/docs/Large-Class.md +20 -0
- data/docs/Long-Parameter-List.md +38 -0
- data/docs/Long-Yield-List.md +36 -0
- data/docs/Module-Initialize.md +62 -0
- data/docs/Nested-Iterators.md +38 -0
- data/docs/Nil-Check.md +39 -0
- data/docs/Prima-Donna-Method.md +53 -0
- data/docs/RSpec-matchers.md +137 -0
- data/docs/Rake-Task.md +58 -0
- data/docs/Reek-Driven-Development.md +37 -0
- data/docs/Repeated-Conditional.md +44 -0
- data/docs/Simulated-Polymorphism.md +16 -0
- data/docs/Smell-Suppression.md +32 -0
- data/docs/Too-Many-Instance-Variables.md +43 -0
- data/docs/Too-Many-Methods.md +55 -0
- data/docs/Too-Many-Statements.md +50 -0
- data/docs/Uncommunicative-Method-Name.md +24 -0
- data/docs/Uncommunicative-Module-Name.md +23 -0
- data/docs/Uncommunicative-Name.md +16 -0
- data/docs/Uncommunicative-Parameter-Name.md +24 -0
- data/docs/Uncommunicative-Variable-Name.md +24 -0
- data/docs/Unused-Parameters.md +27 -0
- data/docs/Utility-Function.md +44 -0
- data/docs/Versioning-Policy.md +7 -0
- data/docs/YAML-Reports.md +111 -0
- data/docs/yard_plugin.rb +14 -0
- data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +6 -5
- data/features/command_line_interface/options.feature +1 -0
- data/features/command_line_interface/smell_selection.feature +4 -8
- data/features/command_line_interface/smells_count.feature +25 -32
- data/features/command_line_interface/stdin.feature +2 -2
- data/features/configuration_files/masking_smells.feature +30 -41
- data/features/configuration_files/overrides_defaults.feature +5 -3
- data/features/configuration_loading.feature +26 -23
- data/features/programmatic_access.feature +43 -0
- data/features/rake_task/rake_task.feature +25 -22
- data/features/reports/json.feature +24 -26
- data/features/reports/reports.feature +77 -103
- data/features/reports/yaml.feature +26 -20
- data/features/samples.feature +26 -31
- data/features/step_definitions/.rubocop.yml +5 -0
- data/features/step_definitions/reek_steps.rb +16 -28
- data/features/step_definitions/sample_file_steps.rb +158 -0
- data/features/support/env.rb +16 -27
- data/lib/reek/{source → ast}/ast_node_class_map.rb +11 -12
- data/lib/reek/ast/node.rb +132 -0
- data/lib/reek/{core → ast}/object_refs.rb +2 -1
- data/lib/reek/ast/reference_collector.rb +32 -0
- data/lib/reek/{source → ast}/sexp_extensions.rb +43 -7
- data/lib/reek/ast/sexp_formatter.rb +31 -0
- data/lib/reek/cli/application.rb +5 -4
- data/lib/reek/cli/command.rb +2 -1
- data/lib/reek/cli/input.rb +6 -5
- data/lib/reek/cli/option_interpreter.rb +13 -9
- data/lib/reek/cli/options.rb +16 -8
- data/lib/reek/cli/reek_command.rb +4 -3
- data/lib/reek/cli/silencer.rb +14 -0
- data/lib/reek/{core → cli}/warning_collector.rb +2 -1
- data/lib/reek/code_comment.rb +36 -0
- data/lib/reek/configuration/app_configuration.rb +18 -3
- data/lib/reek/configuration/configuration_file_finder.rb +18 -32
- data/lib/reek/{core → context}/code_context.rb +8 -6
- data/lib/reek/{core → context}/method_context.rb +11 -5
- data/lib/reek/{core → context}/module_context.rb +9 -4
- data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
- data/lib/reek/{core → context}/singleton_method_context.rb +3 -6
- data/lib/reek/examiner.rb +32 -29
- data/lib/reek/report/formatter.rb +70 -0
- data/lib/reek/report/heading_formatter.rb +45 -0
- data/lib/reek/report/location_formatter.rb +35 -0
- data/lib/reek/report/report.rb +198 -0
- data/lib/reek/smells/attribute.rb +41 -11
- data/lib/reek/smells/boolean_parameter.rb +4 -2
- data/lib/reek/smells/class_variable.rb +4 -2
- data/lib/reek/smells/control_parameter.rb +4 -2
- data/lib/reek/smells/data_clump.rb +4 -3
- data/lib/reek/smells/duplicate_method_call.rb +15 -5
- data/lib/reek/smells/feature_envy.rb +8 -2
- data/lib/reek/smells/irresponsible_module.rb +13 -8
- data/lib/reek/smells/long_parameter_list.rb +6 -4
- data/lib/reek/smells/long_yield_list.rb +4 -2
- data/lib/reek/smells/module_initialize.rb +4 -2
- data/lib/reek/smells/nested_iterators.rb +4 -2
- data/lib/reek/smells/nil_check.rb +6 -3
- data/lib/reek/smells/prima_donna_method.rb +21 -12
- data/lib/reek/smells/repeated_conditional.rb +7 -4
- data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
- data/lib/reek/smells/smell_detector.rb +11 -5
- data/lib/reek/{core → smells}/smell_repository.rb +9 -5
- data/lib/reek/smells/smell_warning.rb +101 -0
- data/lib/reek/smells/too_many_instance_variables.rb +4 -2
- data/lib/reek/smells/too_many_methods.rb +4 -2
- data/lib/reek/smells/too_many_statements.rb +4 -2
- data/lib/reek/smells/uncommunicative_method_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_module_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_variable_name.rb +4 -2
- data/lib/reek/smells/unused_parameters.rb +4 -2
- data/lib/reek/smells/utility_function.rb +11 -22
- data/lib/reek/smells.rb +24 -11
- data/lib/reek/source/source_code.rb +69 -12
- data/lib/reek/source/source_locator.rb +34 -19
- data/lib/reek/spec/should_reek.rb +4 -3
- data/lib/reek/spec/should_reek_of.rb +2 -1
- data/lib/reek/spec/should_reek_only_of.rb +4 -3
- data/lib/reek/spec.rb +3 -3
- data/lib/reek/tree_dresser.rb +32 -0
- data/lib/reek/tree_walker.rb +182 -0
- data/lib/reek/version.rb +1 -1
- data/lib/reek.rb +3 -3
- data/reek.gemspec +18 -25
- data/spec/factories/factories.rb +4 -2
- data/spec/gem/updates_spec.rb +1 -1
- data/spec/gem/yard_spec.rb +2 -2
- data/spec/quality/reek_source_spec.rb +3 -3
- data/spec/reek/{source/sexp_node_spec.rb → ast/node_spec.rb} +4 -6
- data/spec/reek/{core → ast}/object_refs_spec.rb +4 -6
- data/spec/reek/{source → ast}/reference_collector_spec.rb +27 -19
- data/spec/reek/{source → ast}/sexp_extensions_spec.rb +7 -19
- data/spec/reek/{source → ast}/sexp_formatter_spec.rb +13 -8
- data/spec/reek/cli/option_interpreter_spec.rb +15 -0
- data/spec/reek/cli/options_spec.rb +19 -0
- data/spec/reek/cli/warning_collector_spec.rb +28 -0
- data/spec/reek/{source/code_comment_spec.rb → code_comment_spec.rb} +19 -17
- data/spec/reek/configuration/app_configuration_spec.rb +37 -26
- data/spec/reek/configuration/configuration_file_finder_spec.rb +67 -21
- data/spec/reek/{core → context}/code_context_spec.rb +18 -21
- data/spec/reek/{core → context}/method_context_spec.rb +33 -27
- data/spec/reek/{core → context}/module_context_spec.rb +5 -7
- data/spec/reek/context/root_context_spec.rb +14 -0
- data/spec/reek/context/singleton_method_context_spec.rb +17 -0
- data/spec/reek/examiner_spec.rb +9 -50
- data/spec/reek/{cli → report}/html_report_spec.rb +6 -9
- data/spec/reek/report/json_report_spec.rb +20 -0
- data/spec/reek/{cli → report}/text_report_spec.rb +16 -19
- data/spec/reek/report/xml_report_spec.rb +34 -0
- data/spec/reek/report/yaml_report_spec.rb +20 -0
- data/spec/reek/smells/attribute_spec.rb +93 -88
- data/spec/reek/smells/behaves_like_variable_detector.rb +1 -1
- data/spec/reek/smells/boolean_parameter_spec.rb +31 -5
- data/spec/reek/smells/class_variable_spec.rb +10 -10
- data/spec/reek/smells/control_parameter_spec.rb +5 -5
- data/spec/reek/smells/data_clump_spec.rb +6 -6
- data/spec/reek/smells/duplicate_method_call_spec.rb +7 -7
- data/spec/reek/smells/feature_envy_spec.rb +12 -13
- data/spec/reek/smells/irresponsible_module_spec.rb +28 -32
- data/spec/reek/smells/long_parameter_list_spec.rb +7 -7
- data/spec/reek/smells/long_yield_list_spec.rb +6 -6
- data/spec/reek/smells/module_initialize_spec.rb +4 -4
- data/spec/reek/smells/nested_iterators_spec.rb +6 -6
- data/spec/reek/smells/nil_check_spec.rb +6 -6
- data/spec/reek/smells/prima_donna_method_spec.rb +8 -5
- data/spec/reek/smells/repeated_conditional_spec.rb +18 -17
- data/spec/reek/{core → smells}/smell_configuration_spec.rb +5 -7
- data/spec/reek/smells/smell_detector_shared.rb +6 -7
- data/spec/reek/smells/smell_repository_spec.rb +29 -0
- data/spec/reek/{smell_warning_spec.rb → smells/smell_warning_spec.rb} +7 -9
- data/spec/reek/smells/too_many_instance_variables_spec.rb +5 -5
- data/spec/reek/smells/too_many_methods_spec.rb +12 -8
- data/spec/reek/smells/too_many_statements_spec.rb +10 -9
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +5 -5
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +8 -8
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +18 -6
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +10 -7
- data/spec/reek/smells/unused_parameters_spec.rb +4 -4
- data/spec/reek/smells/utility_function_spec.rb +65 -39
- data/spec/reek/source/source_code_spec.rb +10 -10
- data/spec/reek/source/source_locator_spec.rb +30 -0
- data/spec/reek/spec/should_reek_of_spec.rb +9 -25
- data/spec/reek/spec/should_reek_only_of_spec.rb +4 -7
- data/spec/reek/spec/should_reek_spec.rb +4 -32
- data/spec/reek/tree_dresser_spec.rb +16 -0
- data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -7
- data/spec/samples/checkstyle.xml +2 -0
- data/spec/samples/configuration/with_excluded_paths.reek +4 -0
- data/spec/samples/{overrides/masked/lower.reek → masked_by_dotfile/.reek} +3 -1
- data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
- data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
- data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
- data/spec/samples/{ruby20_syntax.rb → unusual_syntax.rb} +8 -0
- data/spec/spec_helper.rb +21 -10
- data/tasks/develop.rake +2 -2
- data/tasks/reek.rake +1 -1
- metadata +158 -141
- data/features/support/hooks.rb +0 -15
- data/lib/reek/cli/report/formatter.rb +0 -69
- data/lib/reek/cli/report/heading_formatter.rb +0 -45
- data/lib/reek/cli/report/location_formatter.rb +0 -34
- data/lib/reek/cli/report/report.rb +0 -131
- data/lib/reek/core/tree_walker.rb +0 -180
- data/lib/reek/smell_warning.rb +0 -87
- data/lib/reek/source/ast_node.rb +0 -38
- data/lib/reek/source/code_comment.rb +0 -37
- data/lib/reek/source/core_extras.rb +0 -46
- data/lib/reek/source/reference_collector.rb +0 -27
- data/lib/reek/source/sexp_formatter.rb +0 -22
- data/lib/reek/source/sexp_node.rb +0 -79
- data/lib/reek/source/source_file.rb +0 -16
- data/lib/reek/source/source_repository.rb +0 -45
- data/lib/reek/source/tree_dresser.rb +0 -24
- data/lib/reek/source.rb +0 -16
- data/spec/reek/cli/json_report_spec.rb +0 -20
- data/spec/reek/cli/option_interperter_spec.rb +0 -14
- data/spec/reek/cli/yaml_report_spec.rb +0 -23
- data/spec/reek/core/singleton_method_context_spec.rb +0 -9
- data/spec/reek/core/smell_repository_spec.rb +0 -17
- data/spec/reek/core/stop_context_spec.rb +0 -17
- data/spec/reek/core/warning_collector_spec.rb +0 -30
- data/spec/reek/source/object_source_spec.rb +0 -20
- data/spec/reek/source/tree_dresser_spec.rb +0 -18
- data/spec/samples/config/allow_duplication.reek +0 -3
- data/spec/samples/config/deeper_nested_iterators.reek +0 -3
- data/spec/samples/configuration_loading/minimal_dirty.rb +0 -4
- data/spec/samples/configuration_loading/reek-test-run-disable_smells.reek +0 -7
- data/spec/samples/configuration_loading/reek-test-run-enable_smells.reek +0 -7
- data/spec/samples/corrupt_config_file/corrupt.reek +0 -1
- data/spec/samples/corrupt_config_file/dirty.rb +0 -8
- data/spec/samples/demo/demo.rb +0 -8
- data/spec/samples/empty_config_file/dirty.rb +0 -8
- data/spec/samples/empty_config_file/empty.reek +0 -0
- data/spec/samples/inline_config/dirty.rb +0 -16
- data/spec/samples/inline_config/masked.reek +0 -7
- data/spec/samples/mask_some/dirty.rb +0 -8
- data/spec/samples/mask_some/some.reek +0 -8
- data/spec/samples/masked/dirty.rb +0 -8
- data/spec/samples/masked/masked.reek +0 -5
- data/spec/samples/minimal_smelly_and_masked/config.reek +0 -7
- data/spec/samples/minimal_smelly_and_masked/minimal_dirty.rb +0 -4
- data/spec/samples/mixed_results/clean_one.rb +0 -7
- data/spec/samples/mixed_results/clean_three.rb +0 -7
- data/spec/samples/mixed_results/clean_two.rb +0 -7
- data/spec/samples/mixed_results/dirty_one.rb +0 -8
- data/spec/samples/mixed_results/dirty_two.rb +0 -8
- data/spec/samples/not_quite_masked/dirty.rb +0 -8
- data/spec/samples/not_quite_masked/masked.reek +0 -5
- data/spec/samples/not_quite_masked/smelly.rb +0 -3
- data/spec/samples/one_smelly_file/dirty.rb +0 -3
- data/spec/samples/overrides/masked/dirty.rb +0 -8
- data/spec/samples/overrides/upper.reek +0 -5
- data/spec/samples/overrides_defaults/camel_case.rb +0 -14
- data/spec/samples/overrides_defaults/config.reek +0 -6
- data/spec/samples/ruby21_syntax.rb +0 -5
- data/spec/samples/standard_smelly/dirty.rb +0 -8
- data/spec/samples/standard_smelly/minimal_dirty.rb +0 -4
- data/spec/samples/three_smelly_files/dirty_one.rb +0 -3
- data/spec/samples/three_smelly_files/dirty_three.rb +0 -5
- data/spec/samples/three_smelly_files/dirty_two.rb +0 -4
- /data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Reek
|
|
2
|
+
module Report
|
|
3
|
+
#
|
|
4
|
+
# Formats the location of a warning as an empty string.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
7
|
+
module BlankLocationFormatter
|
|
8
|
+
def self.format(_warning)
|
|
9
|
+
''
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# Formats the location of a warning as an array of line numbers.
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
module DefaultLocationFormatter
|
|
18
|
+
def self.format(warning)
|
|
19
|
+
"#{warning.lines.inspect}:"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Formats the location of a warning as a combination of source file name
|
|
25
|
+
# and line number. In this format, it is not possible to show more than
|
|
26
|
+
# one line number, so the first number is displayed.
|
|
27
|
+
#
|
|
28
|
+
# @api private
|
|
29
|
+
module SingleLineLocationFormatter
|
|
30
|
+
def self.format(warning)
|
|
31
|
+
"#{warning.source}:#{warning.lines.first}: "
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'rainbow'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative 'formatter'
|
|
4
|
+
require_relative 'heading_formatter'
|
|
5
|
+
|
|
6
|
+
module Reek
|
|
7
|
+
module Report
|
|
8
|
+
#
|
|
9
|
+
# A report that contains the smells and smell counts following source code analysis.
|
|
10
|
+
#
|
|
11
|
+
# @abstract Subclass and override {#show} to create a concrete report class.
|
|
12
|
+
class Base
|
|
13
|
+
# @api private
|
|
14
|
+
NO_WARNINGS_COLOR = :green
|
|
15
|
+
# @api private
|
|
16
|
+
WARNINGS_COLOR = :red
|
|
17
|
+
|
|
18
|
+
def initialize(options = {})
|
|
19
|
+
@examiners = []
|
|
20
|
+
@total_smell_count = 0
|
|
21
|
+
@options = options
|
|
22
|
+
@warning_formatter = options.fetch :warning_formatter, SimpleWarningFormatter.new
|
|
23
|
+
@report_formatter = options.fetch :report_formatter, Formatter
|
|
24
|
+
@sort_by_issue_count = options.fetch :sort_by_issue_count, false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Add Examiner to report on. The report will output results for all
|
|
28
|
+
# added examiners.
|
|
29
|
+
#
|
|
30
|
+
# @param [Reek::Examiner] examiner object to report on
|
|
31
|
+
def add_examiner(examiner)
|
|
32
|
+
@total_smell_count += examiner.smells_count
|
|
33
|
+
@examiners << examiner
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Render the report results on STDOUT
|
|
38
|
+
def show
|
|
39
|
+
raise NotImplementedError
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @api private
|
|
43
|
+
def smells?
|
|
44
|
+
@total_smell_count > 0
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @api private
|
|
48
|
+
def smells
|
|
49
|
+
@examiners.map(&:smells).flatten
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# Generates a sorted, text summary of smells in examiners
|
|
55
|
+
#
|
|
56
|
+
class TextReport < Base
|
|
57
|
+
def show
|
|
58
|
+
sort_examiners if smells?
|
|
59
|
+
display_summary
|
|
60
|
+
display_total_smell_count
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def smell_summaries
|
|
66
|
+
@examiners.map { |ex| summarize_single_examiner(ex) }.reject(&:empty?)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def display_summary
|
|
70
|
+
smell_summaries.each { |smell| puts smell }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def display_total_smell_count
|
|
74
|
+
return unless @examiners.size > 1
|
|
75
|
+
print total_smell_count_message
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def summarize_single_examiner(examiner)
|
|
79
|
+
result = heading_formatter.header(examiner)
|
|
80
|
+
if examiner.smelly?
|
|
81
|
+
formatted_list = @report_formatter.format_list(examiner.smells,
|
|
82
|
+
@warning_formatter)
|
|
83
|
+
result += ":\n#{formatted_list}"
|
|
84
|
+
end
|
|
85
|
+
result
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def sort_examiners
|
|
89
|
+
@examiners.sort_by!(&:smells_count).reverse! if @sort_by_issue_count
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def total_smell_count_message
|
|
93
|
+
colour = smells? ? WARNINGS_COLOR : NO_WARNINGS_COLOR
|
|
94
|
+
s = @total_smell_count == 1 ? '' : 's'
|
|
95
|
+
Rainbow("#{@total_smell_count} total warning#{s}\n").color(colour)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def heading_formatter
|
|
99
|
+
@heading_formatter ||=
|
|
100
|
+
@options.fetch(:heading_formatter, HeadingFormatter::Quiet).new(@report_formatter)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#
|
|
105
|
+
# Displays a list of smells in YAML format
|
|
106
|
+
# YAML with empty array for 0 smells
|
|
107
|
+
class YAMLReport < Base
|
|
108
|
+
def show
|
|
109
|
+
print smells.map(&:yaml_hash).to_yaml
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#
|
|
114
|
+
# Displays a list of smells in JSON format
|
|
115
|
+
# JSON with empty array for 0 smells
|
|
116
|
+
class JSONReport < Base
|
|
117
|
+
def show
|
|
118
|
+
print ::JSON.generate(
|
|
119
|
+
smells.map do |smell|
|
|
120
|
+
smell.yaml_hash(@warning_formatter)
|
|
121
|
+
end
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
#
|
|
127
|
+
# Saves the report as a HTML file
|
|
128
|
+
#
|
|
129
|
+
class HTMLReport < Base
|
|
130
|
+
require 'erb'
|
|
131
|
+
|
|
132
|
+
def show
|
|
133
|
+
path = File.expand_path('../../../../assets/html_output.html.erb',
|
|
134
|
+
__FILE__)
|
|
135
|
+
File.open('reek.html', 'w+') do |file|
|
|
136
|
+
file.puts ERB.new(File.read(path)).result(binding)
|
|
137
|
+
end
|
|
138
|
+
print("Html file saved\n")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#
|
|
143
|
+
# Generates a list of smells in XML format
|
|
144
|
+
#
|
|
145
|
+
class XMLReport < Base
|
|
146
|
+
require 'rexml/document'
|
|
147
|
+
|
|
148
|
+
def show
|
|
149
|
+
checkstyle = REXML::Element.new('checkstyle', document)
|
|
150
|
+
|
|
151
|
+
smells.group_by(&:source).each do |file, file_smells|
|
|
152
|
+
file_to_xml(file, file_smells, checkstyle)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
print_xml(checkstyle.parent)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def document
|
|
161
|
+
REXML::Document.new.tap do |doc|
|
|
162
|
+
doc << REXML::XMLDecl.new
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def file_to_xml(file, file_smells, parent)
|
|
167
|
+
REXML::Element.new('file', parent).tap do |element|
|
|
168
|
+
element.attributes['name'] = File.realpath(file)
|
|
169
|
+
smells_to_xml(file_smells, element)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def smells_to_xml(smells, parent)
|
|
174
|
+
smells.each do |smell|
|
|
175
|
+
smell_to_xml(smell, parent)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def smell_to_xml(smell, parent)
|
|
180
|
+
REXML::Element.new('error', parent).tap do |element|
|
|
181
|
+
attributes = [
|
|
182
|
+
['line', smell.lines.first],
|
|
183
|
+
['column', 0],
|
|
184
|
+
['severity', 'warning'],
|
|
185
|
+
['message', smell.message],
|
|
186
|
+
['source', smell.smell_type]
|
|
187
|
+
]
|
|
188
|
+
element.add_attributes(attributes)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def print_xml(document)
|
|
193
|
+
formatter = REXML::Formatters::Default.new
|
|
194
|
+
puts formatter.write(document, '')
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require_relative 'smell_configuration'
|
|
2
|
+
require_relative 'smell_detector'
|
|
3
|
+
require_relative 'smell_warning'
|
|
4
4
|
|
|
5
5
|
module Reek
|
|
6
6
|
module Smells
|
|
@@ -13,16 +13,27 @@ module Reek
|
|
|
13
13
|
# +attr_reader+, +attr_writer+ and +attr_accessor+ -- including those
|
|
14
14
|
# that are private.
|
|
15
15
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
16
|
+
# See {file:docs/Attribute.md} for details.
|
|
17
|
+
# @api private
|
|
18
18
|
#
|
|
19
|
+
# TODO: Catch attributes declared "by hand"
|
|
19
20
|
class Attribute < SmellDetector
|
|
21
|
+
ATTR_DEFN_METHODS = [:attr, :attr_reader, :attr_writer, :attr_accessor]
|
|
22
|
+
VISIBILITY_MODIFIERS = [:private, :public, :protected]
|
|
23
|
+
|
|
24
|
+
def initialize(*args)
|
|
25
|
+
@visiblity_tracker = {}
|
|
26
|
+
@visiblity_mode = :public
|
|
27
|
+
@result = Set.new
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
|
|
20
31
|
def self.contexts # :nodoc:
|
|
21
32
|
[:class, :module]
|
|
22
33
|
end
|
|
23
34
|
|
|
24
35
|
def self.default_config
|
|
25
|
-
super.merge(
|
|
36
|
+
super.merge(SmellConfiguration::ENABLED_KEY => false)
|
|
26
37
|
end
|
|
27
38
|
|
|
28
39
|
#
|
|
@@ -43,14 +54,33 @@ module Reek
|
|
|
43
54
|
private
|
|
44
55
|
|
|
45
56
|
def attributes_in(module_ctx)
|
|
46
|
-
result = Set.new
|
|
47
|
-
attr_defn_methods = [:attr, :attr_reader, :attr_writer, :attr_accessor]
|
|
48
57
|
module_ctx.local_nodes(:send) do |call_node|
|
|
49
|
-
if
|
|
50
|
-
call_node
|
|
58
|
+
if visibility_modifier?(call_node)
|
|
59
|
+
track_visibility(call_node)
|
|
60
|
+
elsif ATTR_DEFN_METHODS.include?(call_node.method_name)
|
|
61
|
+
call_node.arg_names.each do |arg|
|
|
62
|
+
@visiblity_tracker[arg] = @visiblity_mode
|
|
63
|
+
@result << [arg, call_node.line]
|
|
64
|
+
end
|
|
51
65
|
end
|
|
52
66
|
end
|
|
53
|
-
result
|
|
67
|
+
@result.select { |args| recorded_public_methods.include?(args[0]) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def visibility_modifier?(call_node)
|
|
71
|
+
VISIBILITY_MODIFIERS.include?(call_node.method_name)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def track_visibility(call_node)
|
|
75
|
+
if call_node.arg_names.any?
|
|
76
|
+
call_node.arg_names.each { |arg| @visiblity_tracker[arg] = call_node.method_name }
|
|
77
|
+
else
|
|
78
|
+
@visiblity_mode = call_node.method_name
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def recorded_public_methods
|
|
83
|
+
@visiblity_tracker.select { |_, visbility| visbility == :public }
|
|
54
84
|
end
|
|
55
85
|
end
|
|
56
86
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -11,6 +11,8 @@ module Reek
|
|
|
11
11
|
# Currently Reek can only detect a Boolean parameter when it has a
|
|
12
12
|
# default initializer.
|
|
13
13
|
#
|
|
14
|
+
# See {file:docs/Boolean-Parameter.md} for details.
|
|
15
|
+
# @api private
|
|
14
16
|
class BooleanParameter < SmellDetector
|
|
15
17
|
def self.smell_category
|
|
16
18
|
'ControlCouple'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'set'
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
require_relative 'smell_detector'
|
|
3
|
+
require_relative 'smell_warning'
|
|
4
4
|
|
|
5
5
|
module Reek
|
|
6
6
|
module Smells
|
|
@@ -12,6 +12,8 @@ module Reek
|
|
|
12
12
|
# In particular, class variables can make it hard to set up tests (because
|
|
13
13
|
# the context of the test includes all global state).
|
|
14
14
|
#
|
|
15
|
+
# See {file:docs/Class-Variable.md} for details.
|
|
16
|
+
# @api private
|
|
15
17
|
class ClassVariable < SmellDetector
|
|
16
18
|
def self.contexts # :nodoc:
|
|
17
19
|
[:class, :module]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -40,6 +40,8 @@ module Reek
|
|
|
40
40
|
# that remains next to where the caller invokes it in
|
|
41
41
|
# the source code.
|
|
42
42
|
#
|
|
43
|
+
# See {file:docs/Control-Parameter.md} for details.
|
|
44
|
+
# @api private
|
|
43
45
|
class ControlParameter < SmellDetector
|
|
44
46
|
def self.smell_category
|
|
45
47
|
'ControlCouple'
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'reek/source'
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
4
3
|
|
|
5
4
|
module Reek
|
|
6
5
|
module Smells
|
|
@@ -16,6 +15,8 @@ module Reek
|
|
|
16
15
|
# Currently Reek looks for a group of two or more parameters with
|
|
17
16
|
# the same names that are expected by three or more methods of a class.
|
|
18
17
|
#
|
|
18
|
+
# See {file:docs/Data-Clump.md} for details.
|
|
19
|
+
# @api private
|
|
19
20
|
class DataClump < SmellDetector
|
|
20
21
|
#
|
|
21
22
|
# The name of the config field that sets the maximum allowed
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -16,6 +16,8 @@ module Reek
|
|
|
16
16
|
# @other.thing + @other.thing
|
|
17
17
|
# end
|
|
18
18
|
#
|
|
19
|
+
# See {file:docs/Duplicate-Method-Call.md} for details.
|
|
20
|
+
# @api private
|
|
19
21
|
class DuplicateMethodCall < SmellDetector
|
|
20
22
|
# The name of the config field that sets the maximum number of
|
|
21
23
|
# identical calls to be permitted within any single method.
|
|
@@ -70,7 +72,7 @@ module Reek
|
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
def call
|
|
73
|
-
@call ||= @call_node.
|
|
75
|
+
@call ||= @call_node.format_to_ruby
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
def occurs
|
|
@@ -106,8 +108,8 @@ module Reek
|
|
|
106
108
|
|
|
107
109
|
def collect_calls(result)
|
|
108
110
|
context.each_node(:send, [:mlhs]) do |call_node|
|
|
109
|
-
next if call_node
|
|
110
|
-
next if
|
|
111
|
+
next if initializer_call? call_node
|
|
112
|
+
next if simple_method_call? call_node
|
|
111
113
|
result[call_node].record(call_node)
|
|
112
114
|
end
|
|
113
115
|
context.local_nodes(:block) do |call_node|
|
|
@@ -119,6 +121,14 @@ module Reek
|
|
|
119
121
|
found_call.occurs > @max_allowed_calls && !allow_calls?(found_call.call)
|
|
120
122
|
end
|
|
121
123
|
|
|
124
|
+
def simple_method_call?(call_node)
|
|
125
|
+
!call_node.receiver && call_node.args.empty?
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def initializer_call?(call_node)
|
|
129
|
+
call_node.method_name == :new
|
|
130
|
+
end
|
|
131
|
+
|
|
122
132
|
def allow_calls?(method)
|
|
123
133
|
@allow_calls.any? { |allow| /#{allow}/ =~ method }
|
|
124
134
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -30,6 +30,11 @@ module Reek
|
|
|
30
30
|
# Currently +FeatureEnvy+ reports any method that refers to self less
|
|
31
31
|
# often than it refers to (ie. send messages to) some other object.
|
|
32
32
|
#
|
|
33
|
+
# If the method doesn't reference self at all, +UtilityFunction+ is
|
|
34
|
+
# reported instead.
|
|
35
|
+
#
|
|
36
|
+
# See {file:docs/Feature-Envy.md} for details.
|
|
37
|
+
# @api private
|
|
33
38
|
class FeatureEnvy < SmellDetector
|
|
34
39
|
def self.smell_category
|
|
35
40
|
'LowCohesion'
|
|
@@ -42,6 +47,7 @@ module Reek
|
|
|
42
47
|
# @return [Array<SmellWarning>]
|
|
43
48
|
#
|
|
44
49
|
def examine_context(method_ctx)
|
|
50
|
+
return [] unless method_ctx.references_self?
|
|
45
51
|
method_ctx.envious_receivers.map do |ref, occurs|
|
|
46
52
|
target = ref.to_s
|
|
47
53
|
SmellWarning.new self,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'reek/source/code_comment'
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
4
3
|
|
|
5
4
|
module Reek
|
|
6
5
|
module Smells
|
|
@@ -8,12 +7,14 @@ module Reek
|
|
|
8
7
|
# It is considered good practice to annotate every class and module
|
|
9
8
|
# with a brief comment outlining its responsibilities.
|
|
10
9
|
#
|
|
10
|
+
# See {file:docs/Irresponsible-Module.md} for details.
|
|
11
|
+
# @api private
|
|
11
12
|
class IrresponsibleModule < SmellDetector
|
|
12
13
|
def self.contexts # :nodoc:
|
|
13
14
|
[:class]
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def
|
|
17
|
+
def descriptive # :nodoc:
|
|
17
18
|
@descriptive ||= {}
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -23,13 +24,17 @@ module Reek
|
|
|
23
24
|
# @return [Array<SmellWarning>]
|
|
24
25
|
#
|
|
25
26
|
def examine_context(ctx)
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
return [] if descriptive?(ctx)
|
|
28
|
+
expression = ctx.exp
|
|
28
29
|
[SmellWarning.new(self,
|
|
29
30
|
context: ctx.full_name,
|
|
30
|
-
lines: [
|
|
31
|
+
lines: [expression.line],
|
|
31
32
|
message: 'has no descriptive comment',
|
|
32
|
-
parameters: { name:
|
|
33
|
+
parameters: { name: expression.text_name })]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def descriptive?(ctx)
|
|
37
|
+
descriptive[ctx.full_name] ||= ctx.descriptively_commented?
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require_relative 'smell_configuration'
|
|
2
|
+
require_relative 'smell_detector'
|
|
3
|
+
require_relative 'smell_warning'
|
|
4
4
|
|
|
5
5
|
module Reek
|
|
6
6
|
module Smells
|
|
@@ -12,6 +12,8 @@ module Reek
|
|
|
12
12
|
# Currently +LongParameterList+ reports any method or block with too
|
|
13
13
|
# many parameters.
|
|
14
14
|
#
|
|
15
|
+
# See {file:docs/Long-Parameter-List.md} for details.
|
|
16
|
+
# @api private
|
|
15
17
|
class LongParameterList < SmellDetector
|
|
16
18
|
# The name of the config field that sets the maximum number of
|
|
17
19
|
# parameters permitted in any method or block.
|
|
@@ -21,7 +23,7 @@ module Reek
|
|
|
21
23
|
def self.default_config
|
|
22
24
|
super.merge(
|
|
23
25
|
MAX_ALLOWED_PARAMS_KEY => DEFAULT_MAX_ALLOWED_PARAMS,
|
|
24
|
-
|
|
26
|
+
SmellConfiguration::OVERRIDES_KEY => {
|
|
25
27
|
'initialize' => { MAX_ALLOWED_PARAMS_KEY => 5 }
|
|
26
28
|
}
|
|
27
29
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -7,6 +7,8 @@ module Reek
|
|
|
7
7
|
# A variant on LongParameterList that checks the number of items
|
|
8
8
|
# passed to a block by a +yield+ call.
|
|
9
9
|
#
|
|
10
|
+
# See {file:docs/Long-Yield-List.md} for details.
|
|
11
|
+
# @api private
|
|
10
12
|
class LongYieldList < SmellDetector
|
|
11
13
|
# The name of the config field that sets the maximum number of
|
|
12
14
|
# parameters permitted in any method or block.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -8,6 +8,8 @@ module Reek
|
|
|
8
8
|
# hard to tell initialization order and parameters so having 'initialize'
|
|
9
9
|
# in a module is usually a bad idea
|
|
10
10
|
#
|
|
11
|
+
# See {file:docs/Module-Initialize.md} for details.
|
|
12
|
+
# @api private
|
|
11
13
|
class ModuleInitialize < SmellDetector
|
|
12
14
|
def self.contexts # :nodoc:
|
|
13
15
|
[:module]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -8,6 +8,8 @@ module Reek
|
|
|
8
8
|
#
|
|
9
9
|
# +NestedIterators+ reports failing methods only once.
|
|
10
10
|
#
|
|
11
|
+
# See {file:docs/Nested-Iterators.md} for details.
|
|
12
|
+
# @api private
|
|
11
13
|
class NestedIterators < SmellDetector
|
|
12
14
|
# The name of the config field that sets the maximum depth
|
|
13
15
|
# of nested iterators to be permitted within any single method.
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
6
6
|
# Checking for nil is a special kind of type check, and therefore a case of
|
|
7
7
|
# SimulatedPolymorphism.
|
|
8
|
+
#
|
|
9
|
+
# See {file:docs/Nil-Check.md} for details.
|
|
10
|
+
# @api private
|
|
8
11
|
class NilCheck < SmellDetector
|
|
9
12
|
def self.smell_category
|
|
10
13
|
'SimulatedPolymorphism'
|
|
@@ -19,7 +22,7 @@ module Reek
|
|
|
19
22
|
SmellWarning.new self,
|
|
20
23
|
context: ctx.full_name,
|
|
21
24
|
lines: [node.line],
|
|
22
|
-
message: 'performs a nil-check
|
|
25
|
+
message: 'performs a nil-check'
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -22,6 +22,8 @@ module Reek
|
|
|
22
22
|
#
|
|
23
23
|
# Such a method is called PrimaDonnaMethod and is reported as a smell.
|
|
24
24
|
#
|
|
25
|
+
# See {file:docs/Prima-Donna-Method.md} for details.
|
|
26
|
+
# @api private
|
|
25
27
|
class PrimaDonnaMethod < SmellDetector
|
|
26
28
|
def self.contexts # :nodoc:
|
|
27
29
|
[:class]
|
|
@@ -29,18 +31,25 @@ module Reek
|
|
|
29
31
|
|
|
30
32
|
def examine_context(ctx)
|
|
31
33
|
ctx.node_instance_methods.map do |method_sexp|
|
|
32
|
-
|
|
34
|
+
check_for_smells(method_sexp, ctx)
|
|
35
|
+
end.compact
|
|
36
|
+
end
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
sexp_item.name.to_s == method_sexp.name_without_bang
|
|
36
|
-
end
|
|
37
|
-
next if version_without_bang
|
|
38
|
+
private
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
def check_for_smells(method_sexp, ctx)
|
|
41
|
+
return unless method_sexp.ends_with_bang?
|
|
42
|
+
|
|
43
|
+
version_without_bang = ctx.node_instance_methods.find do |sexp_item|
|
|
44
|
+
sexp_item.name.to_s == method_sexp.name_without_bang
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
return if version_without_bang
|
|
48
|
+
|
|
49
|
+
SmellWarning.new self,
|
|
50
|
+
context: ctx.full_name,
|
|
51
|
+
lines: [ctx.exp.line],
|
|
52
|
+
message: "has prima donna method `#{method_sexp.name}`"
|
|
44
53
|
end
|
|
45
54
|
end
|
|
46
55
|
end
|