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,158 @@
|
|
|
1
|
+
Given(/^the smelly file 'demo.rb' from the example in the README$/) do
|
|
2
|
+
contents = <<-EOS.strip_heredoc
|
|
3
|
+
class Dirty
|
|
4
|
+
# This method smells of :reek:NestedIterators but ignores them
|
|
5
|
+
def awful(x, y, offset = 0, log = false)
|
|
6
|
+
puts @screen.title
|
|
7
|
+
@screen = widgets.map {|w| w.each {|key| key += 3 * x}}
|
|
8
|
+
puts @screen.contents
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
EOS
|
|
12
|
+
write_file('demo.rb', contents)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Given(/^a smelly file with inline masking called 'inline.rb'$/) do
|
|
16
|
+
write_file 'inline.rb', <<-EOS.strip_heredoc
|
|
17
|
+
# smells of :reek:NestedIterators but ignores them
|
|
18
|
+
class Dirty
|
|
19
|
+
def a
|
|
20
|
+
puts @s.title
|
|
21
|
+
@s = foo.map {|x| x.each {|key| key += 3}}
|
|
22
|
+
puts @s.title
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
EOS
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Given(/^the "(.*?)" sample file exists$/) do |file_name|
|
|
29
|
+
full_path = File.expand_path file_name, 'spec/samples'
|
|
30
|
+
in_current_dir { FileUtils.cp full_path, file_name }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Given(/^a directory called 'clean_files' containing some clean files$/) do
|
|
34
|
+
contents = <<-EOS.strip_heredoc
|
|
35
|
+
# clean class for testing purposes
|
|
36
|
+
class Clean
|
|
37
|
+
def assign
|
|
38
|
+
puts @sub.title
|
|
39
|
+
@sub.map {|para| para.name }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
EOS
|
|
43
|
+
write_file 'clean_files/clean_one.rb', contents
|
|
44
|
+
write_file 'clean_files/clean_two.rb', contents
|
|
45
|
+
write_file 'clean_files/clean_three.rb', contents
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Given(/^a directory called 'smelly' containing two smelly files$/) do
|
|
49
|
+
write_file('smelly/dirty_one.rb', <<-EOS.strip_heredoc)
|
|
50
|
+
class Dirty
|
|
51
|
+
def a; end
|
|
52
|
+
end
|
|
53
|
+
EOS
|
|
54
|
+
write_file('smelly/dirty_two.rb', <<-EOS.strip_heredoc)
|
|
55
|
+
class Dirty
|
|
56
|
+
def a; end
|
|
57
|
+
def b; end
|
|
58
|
+
end
|
|
59
|
+
EOS
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Given(/^a smelly file called 'smelly.rb'( in a subdirectory)?$/) do |in_subdir|
|
|
63
|
+
file_name = in_subdir ? 'subdir/smelly.rb' : 'smelly.rb'
|
|
64
|
+
write_file file_name, <<-EOS.strip_heredoc
|
|
65
|
+
# smelly class for testing purposes
|
|
66
|
+
class Smelly
|
|
67
|
+
def m
|
|
68
|
+
puts @foo.bar
|
|
69
|
+
puts @foo.bar
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
EOS
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
Given(/^a file with smelly variable names called 'camel_case.rb'$/) do
|
|
76
|
+
write_file('camel_case.rb', <<-EOS.strip_heredoc)
|
|
77
|
+
# Class containing camelCase variable which would normally smell
|
|
78
|
+
class CamelCase
|
|
79
|
+
def initialize
|
|
80
|
+
# These next two would normally smell if it weren't for overridden config values
|
|
81
|
+
camelCaseVariable = []
|
|
82
|
+
anotherOne = 1
|
|
83
|
+
|
|
84
|
+
# this next one should still smell
|
|
85
|
+
x1 = 0
|
|
86
|
+
|
|
87
|
+
# this next one should not smell
|
|
88
|
+
should_not_smell = true
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
EOS
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
Given(/^an empty configuration file called 'empty.reek'$/) do
|
|
95
|
+
write_file('empty.reek', '')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
Given(/^a corrupt configuration file called 'corrupt.reek'$/) do
|
|
99
|
+
write_file('corrupt.reek', 'This is not a configuration file')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
Given(/^a masking configuration file called 'config.reek'$/) do
|
|
103
|
+
write_file('config.reek', <<-EOS.strip_heredoc)
|
|
104
|
+
---
|
|
105
|
+
DuplicateMethodCall:
|
|
106
|
+
enabled: false
|
|
107
|
+
UncommunicativeMethodName:
|
|
108
|
+
enabled: false
|
|
109
|
+
EOS
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
Given(/^a configuration file masking some duplication smells called 'config.reek'$/) do
|
|
113
|
+
write_file('config.reek', <<-EOS.strip_heredoc)
|
|
114
|
+
---
|
|
115
|
+
DuplicateMethodCall:
|
|
116
|
+
allow_calls:
|
|
117
|
+
- puts\\(@foo.bar\\)
|
|
118
|
+
EOS
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
Given(/^a configuration file allowing camel case variables$/) do
|
|
122
|
+
write_file('config.reek', <<-EOS.strip_heredoc)
|
|
123
|
+
---
|
|
124
|
+
UncommunicativeVariableName:
|
|
125
|
+
enabled: true
|
|
126
|
+
reject:
|
|
127
|
+
- !ruby/regexp /^.$/
|
|
128
|
+
- !ruby/regexp /[0-9]$/
|
|
129
|
+
EOS
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
When(/^I run "reek (.*?)" in the subdirectory$/) do |args|
|
|
133
|
+
cd 'subdir'
|
|
134
|
+
reek(args)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
Given(/^a masking configuration file in the HOME directory$/) do
|
|
138
|
+
set_env('HOME', File.expand_path(File.join(current_dir, 'home')))
|
|
139
|
+
write_file('home/config.reek', <<-EOS.strip_heredoc)
|
|
140
|
+
---
|
|
141
|
+
DuplicateMethodCall:
|
|
142
|
+
enabled: false
|
|
143
|
+
UncommunicativeMethodName:
|
|
144
|
+
enabled: false
|
|
145
|
+
EOS
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
Given(/^an enabling configuration file in the subdirectory$/) do
|
|
149
|
+
write_file('subdir/config.reek', <<-EOS.strip_heredoc)
|
|
150
|
+
---
|
|
151
|
+
IrresponsibleModule:
|
|
152
|
+
enabled: true
|
|
153
|
+
UncommunicativeModuleName:
|
|
154
|
+
enabled: true
|
|
155
|
+
UncommunicativeMethodName:
|
|
156
|
+
enabled: true
|
|
157
|
+
EOS
|
|
158
|
+
end
|
data/features/support/env.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require '
|
|
3
|
-
require '
|
|
4
|
-
require 'open3'
|
|
5
|
-
require 'reek/cli/application'
|
|
1
|
+
require_relative '../../lib/reek/cli/application'
|
|
2
|
+
require 'aruba/cucumber'
|
|
3
|
+
require 'active_support/core_ext/string/strip'
|
|
6
4
|
|
|
7
5
|
begin
|
|
8
6
|
require 'pry-byebug'
|
|
@@ -13,39 +11,30 @@ end
|
|
|
13
11
|
# Provides runner methods used in the cucumber steps.
|
|
14
12
|
#
|
|
15
13
|
class ReekWorld
|
|
16
|
-
def run(cmd)
|
|
17
|
-
stderr_file = Tempfile.new('reek-world')
|
|
18
|
-
stderr_file.close
|
|
19
|
-
@last_stdout = `#{cmd} 2> #{stderr_file.path}`
|
|
20
|
-
@last_exit_status = $CHILD_STATUS.exitstatus
|
|
21
|
-
@last_stderr = IO.read(stderr_file.path)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
14
|
def reek(args)
|
|
25
|
-
|
|
15
|
+
run_simple("reek --no-color #{args}", false)
|
|
26
16
|
end
|
|
27
17
|
|
|
28
18
|
def reek_with_pipe(stdin, args)
|
|
29
|
-
|
|
19
|
+
run_interactive("reek --no-color #{args}")
|
|
20
|
+
type(stdin)
|
|
21
|
+
close_input
|
|
30
22
|
end
|
|
31
23
|
|
|
32
24
|
def rake(name, task_def)
|
|
33
|
-
header =
|
|
34
|
-
|
|
35
|
-
require 'reek/rake/task'
|
|
25
|
+
header = <<-EOS.strip_heredoc
|
|
26
|
+
require 'reek/rake/task'
|
|
36
27
|
|
|
37
|
-
EOS
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
rakefile.close
|
|
41
|
-
run("rake -f #{rakefile.path} #{name}")
|
|
42
|
-
lines = @last_stdout.split("\n")
|
|
43
|
-
if lines.length > 0 && lines[0] =~ /^\(/
|
|
44
|
-
@last_stdout = lines[1..-1].join("\n")
|
|
45
|
-
end
|
|
28
|
+
EOS
|
|
29
|
+
write_file 'Rakefile', header + task_def
|
|
30
|
+
run_simple("rake #{name}", false)
|
|
46
31
|
end
|
|
47
32
|
end
|
|
48
33
|
|
|
49
34
|
World do
|
|
50
35
|
ReekWorld.new
|
|
51
36
|
end
|
|
37
|
+
|
|
38
|
+
Before do
|
|
39
|
+
@aruba_timeout_seconds = 30
|
|
40
|
+
end
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'reek/source/sexp_extensions'
|
|
1
|
+
require_relative 'node'
|
|
2
|
+
require_relative 'sexp_extensions'
|
|
4
3
|
|
|
5
4
|
module Reek
|
|
6
|
-
module
|
|
7
|
-
# Maps AST node types to sublasses of
|
|
5
|
+
module AST
|
|
6
|
+
# Maps AST node types to sublasses of ASTNode extended with the relevant
|
|
8
7
|
# utility modules.
|
|
9
|
-
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class ASTNodeClassMap
|
|
10
11
|
def initialize
|
|
11
12
|
@klass_map = {}
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def klass_for(type)
|
|
15
|
-
@klass_map[type] ||=
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
klass.send :include, SexpNode
|
|
20
|
-
end
|
|
16
|
+
@klass_map[type] ||= Class.new(Node).tap do |klass|
|
|
17
|
+
extension = extension_map[type]
|
|
18
|
+
klass.send :include, extension if extension
|
|
19
|
+
end
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def extension_map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'parser'
|
|
2
|
+
|
|
3
|
+
module Reek
|
|
4
|
+
# @api private
|
|
5
|
+
module AST
|
|
6
|
+
# Base class for AST nodes extended with utility methods. Contains some
|
|
7
|
+
# methods to ease the transition from Sexp to AST::Node.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class Node < ::Parser::AST::Node
|
|
11
|
+
attr_reader :parent
|
|
12
|
+
|
|
13
|
+
def initialize(type, children = [], options = {})
|
|
14
|
+
@comments = options.fetch(:comments, [])
|
|
15
|
+
@parent = options.fetch(:parent, nil)
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def full_comment
|
|
20
|
+
@comments.map(&:text).join("\n")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def leading_comment
|
|
24
|
+
line = location.line
|
|
25
|
+
comment_lines = @comments.select do |comment|
|
|
26
|
+
comment.location.line < line
|
|
27
|
+
end
|
|
28
|
+
comment_lines.map(&:text).join("\n")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @deprecated
|
|
32
|
+
def [](index)
|
|
33
|
+
elements[index]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def line
|
|
37
|
+
loc.line
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @deprecated
|
|
41
|
+
def first
|
|
42
|
+
type
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Carries out a depth-first traversal of this syntax tree, yielding
|
|
47
|
+
# every Sexp of type `target_type`. The traversal ignores any node
|
|
48
|
+
# whose type is listed in the Array `ignoring`.
|
|
49
|
+
# Takes a block as well.
|
|
50
|
+
#
|
|
51
|
+
# target_type - the type to look for, e.g. :send, :block
|
|
52
|
+
# ignoring - types to ignore, e.g. [:casgn, :class, :module]
|
|
53
|
+
# blk - block to execute for every hit
|
|
54
|
+
#
|
|
55
|
+
# Examples:
|
|
56
|
+
# context.each_node(:send, [:mlhs]) do |call_node| .... end
|
|
57
|
+
# context.each_node(:lvar).any? { |it| it.var_name == 'something' }
|
|
58
|
+
#
|
|
59
|
+
# Returns an array with all matching nodes.
|
|
60
|
+
def each_node(target_type, ignoring = [], &blk)
|
|
61
|
+
if block_given?
|
|
62
|
+
look_for_type(target_type, ignoring, &blk)
|
|
63
|
+
else
|
|
64
|
+
result = []
|
|
65
|
+
look_for_type(target_type, ignoring) { |exp| result << exp }
|
|
66
|
+
result
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Carries out a depth-first traversal of this syntax tree, yielding
|
|
72
|
+
# every Sexp of type `target_type`. The traversal ignores any node
|
|
73
|
+
# whose type is listed in the Array `ignoring`, including the top node.
|
|
74
|
+
# Takes a block as well.
|
|
75
|
+
#
|
|
76
|
+
# target_types - the types to look for, e.g. [:send, :block]
|
|
77
|
+
# ignoring - types to ignore, e.g. [:casgn, :class, :module]
|
|
78
|
+
# blk - block to execute for every hit
|
|
79
|
+
#
|
|
80
|
+
# Examples:
|
|
81
|
+
# exp.find_nodes([:block]).flat_map do |elem| ... end
|
|
82
|
+
#
|
|
83
|
+
# Returns an array with all matching nodes.
|
|
84
|
+
def find_nodes(target_types, ignoring = [])
|
|
85
|
+
result = []
|
|
86
|
+
look_for_types(target_types, ignoring) { |exp| result << exp }
|
|
87
|
+
result
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def contains_nested_node?(target_type)
|
|
91
|
+
look_for_type(target_type) { |_elem| return true }
|
|
92
|
+
false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def format_to_ruby
|
|
96
|
+
SexpFormatter.format(self)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
protected
|
|
100
|
+
|
|
101
|
+
# See ".each_node" for documentation.
|
|
102
|
+
def look_for_type(target_type, ignoring = [], &blk)
|
|
103
|
+
each_sexp do |elem|
|
|
104
|
+
elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
|
|
105
|
+
end
|
|
106
|
+
blk.call(self) if type == target_type
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# See ".find_nodes" for documentation.
|
|
110
|
+
def look_for_types(target_types, ignoring = [], &blk)
|
|
111
|
+
return if ignoring.include?(type)
|
|
112
|
+
if target_types.include? type
|
|
113
|
+
blk.call(self)
|
|
114
|
+
else
|
|
115
|
+
each_sexp do |elem|
|
|
116
|
+
elem.look_for_types(target_types, ignoring, &blk)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def each_sexp
|
|
124
|
+
children.each { |elem| yield elem if elem.is_a? ::Parser::AST::Node }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def elements
|
|
128
|
+
[type, *children]
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Reek
|
|
2
|
+
module AST
|
|
3
|
+
#
|
|
4
|
+
# Locates references to the current object within a portion
|
|
5
|
+
# of an abstract syntax tree.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
class ReferenceCollector
|
|
9
|
+
STOP_NODES = [:class, :module, :def, :defs]
|
|
10
|
+
|
|
11
|
+
def initialize(ast)
|
|
12
|
+
@ast = ast
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def num_refs_to_self
|
|
16
|
+
(explicit_self_calls + implicit_self_calls).size
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def explicit_self_calls
|
|
22
|
+
[:self, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
|
|
23
|
+
@ast.each_node(node_type, STOP_NODES)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def implicit_self_calls
|
|
28
|
+
@ast.each_node(:send, STOP_NODES).reject(&:receiver)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative 'reference_collector'
|
|
2
2
|
|
|
3
3
|
module Reek
|
|
4
|
-
module
|
|
4
|
+
module AST
|
|
5
5
|
#
|
|
6
|
-
# Extension modules providing utility methods to
|
|
6
|
+
# Extension modules providing utility methods to ASTNode objects, depending
|
|
7
7
|
# on their type.
|
|
8
8
|
#
|
|
9
|
+
# @api private
|
|
9
10
|
module SexpExtensions
|
|
10
11
|
# Base module for utility methods for argument nodes.
|
|
11
12
|
module ArgNodeBase
|
|
@@ -37,6 +38,10 @@ module Reek
|
|
|
37
38
|
def anonymous_splat?
|
|
38
39
|
false
|
|
39
40
|
end
|
|
41
|
+
|
|
42
|
+
def components
|
|
43
|
+
[self]
|
|
44
|
+
end
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
# Utility methods for :arg nodes.
|
|
@@ -96,6 +101,24 @@ module Reek
|
|
|
96
101
|
include ArgNodeBase
|
|
97
102
|
end
|
|
98
103
|
|
|
104
|
+
# Base module for utility methods for nodes that can contain argument
|
|
105
|
+
# nodes nested through :mlhs nodes.
|
|
106
|
+
module NestedAssignables
|
|
107
|
+
def components
|
|
108
|
+
children.flat_map(&:components)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Utility methods for :args nodes.
|
|
113
|
+
module ArgsNode
|
|
114
|
+
include NestedAssignables
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Utility methods for :mlhs nodes.
|
|
118
|
+
module MlhsNode
|
|
119
|
+
include NestedAssignables
|
|
120
|
+
end
|
|
121
|
+
|
|
99
122
|
# Base module for utility methods for :and and :or nodes.
|
|
100
123
|
module LogicOperatorBase
|
|
101
124
|
def condition() self[1] end
|
|
@@ -202,7 +225,7 @@ module Reek
|
|
|
202
225
|
end
|
|
203
226
|
|
|
204
227
|
def parameters
|
|
205
|
-
argslist.
|
|
228
|
+
argslist.components
|
|
206
229
|
end
|
|
207
230
|
|
|
208
231
|
def parameter_names
|
|
@@ -239,6 +262,15 @@ module Reek
|
|
|
239
262
|
prefix = outer == '' ? '' : "#{outer}#"
|
|
240
263
|
"#{prefix}#{name}"
|
|
241
264
|
end
|
|
265
|
+
|
|
266
|
+
def depends_on_instance?
|
|
267
|
+
ReferenceCollector.new(self).num_refs_to_self > 0
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def singleton_method?
|
|
271
|
+
# This catches the case where methods are defined within the "class << self" syntax.
|
|
272
|
+
parent.type == :sclass if parent
|
|
273
|
+
end
|
|
242
274
|
end
|
|
243
275
|
|
|
244
276
|
# Utility methods for :defs nodes.
|
|
@@ -254,7 +286,11 @@ module Reek
|
|
|
254
286
|
include MethodNodeBase
|
|
255
287
|
def full_name(outer)
|
|
256
288
|
prefix = outer == '' ? '' : "#{outer}#"
|
|
257
|
-
"#{prefix}#{
|
|
289
|
+
"#{prefix}#{SexpFormatter.format(receiver)}.#{name}"
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def depends_on_instance?
|
|
293
|
+
false
|
|
258
294
|
end
|
|
259
295
|
end
|
|
260
296
|
|
|
@@ -296,7 +332,7 @@ module Reek
|
|
|
296
332
|
def name() self[1] end
|
|
297
333
|
|
|
298
334
|
def simple_name
|
|
299
|
-
name.is_a?(AST::Node) ? name.simple_name : name
|
|
335
|
+
name.is_a?(::Parser::AST::Node) ? name.simple_name : name
|
|
300
336
|
end
|
|
301
337
|
|
|
302
338
|
def full_name(outer)
|
|
@@ -305,7 +341,7 @@ module Reek
|
|
|
305
341
|
end
|
|
306
342
|
|
|
307
343
|
def text_name
|
|
308
|
-
|
|
344
|
+
SexpFormatter.format(name)
|
|
309
345
|
end
|
|
310
346
|
end
|
|
311
347
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative '../cli/silencer'
|
|
2
|
+
Reek::CLI::Silencer.silently do
|
|
3
|
+
require 'unparser'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
module Reek
|
|
7
|
+
module AST
|
|
8
|
+
#
|
|
9
|
+
# Formats snippets of syntax tree back into Ruby source code.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
class SexpFormatter
|
|
13
|
+
# Formats the given sexp.
|
|
14
|
+
#
|
|
15
|
+
# @param [AST::Node, #to_s] sexp - The expression to format
|
|
16
|
+
#
|
|
17
|
+
# @return [String] a formatted string representation.
|
|
18
|
+
#
|
|
19
|
+
# :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
|
|
20
|
+
def self.format(sexp)
|
|
21
|
+
return sexp.to_s unless sexp.is_a? ::Parser::AST::Node
|
|
22
|
+
lines = Unparser.unparse(sexp).split "\n"
|
|
23
|
+
case lines.length
|
|
24
|
+
when 1 then lines.first
|
|
25
|
+
when 2 then lines.join('; ')
|
|
26
|
+
else [lines.first, lines.last].join(' ... ')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/reek/cli/application.rb
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require_relative 'options'
|
|
2
|
+
require_relative 'reek_command'
|
|
3
|
+
require_relative '../configuration/app_configuration'
|
|
4
4
|
|
|
5
5
|
module Reek
|
|
6
|
-
module
|
|
6
|
+
module CLI
|
|
7
7
|
#
|
|
8
8
|
# Represents an instance of a Reek application.
|
|
9
9
|
# This is the entry point for all invocations of Reek from the
|
|
10
10
|
# command line.
|
|
11
11
|
#
|
|
12
|
+
# @api private
|
|
12
13
|
class Application
|
|
13
14
|
STATUS_SUCCESS = 0
|
|
14
15
|
STATUS_ERROR = 1
|
data/lib/reek/cli/command.rb
CHANGED
data/lib/reek/cli/input.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative '../source/source_locator'
|
|
2
2
|
|
|
3
3
|
module Reek
|
|
4
|
-
module
|
|
4
|
+
module CLI
|
|
5
5
|
#
|
|
6
6
|
# CLI Input utility
|
|
7
7
|
#
|
|
8
|
+
# @api private
|
|
8
9
|
module Input
|
|
9
10
|
def sources
|
|
10
11
|
if no_source_files_given?
|
|
@@ -31,15 +32,15 @@ module Reek
|
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def working_directory_as_source
|
|
34
|
-
Source::SourceLocator.new(['.']).
|
|
35
|
+
Source::SourceLocator.new(['.']).sources
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def sources_from_argv
|
|
38
|
-
Source::SourceLocator.new(@argv).
|
|
39
|
+
Source::SourceLocator.new(@argv).sources
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def source_from_pipe
|
|
42
|
-
[$stdin
|
|
43
|
+
[$stdin]
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
end
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
require 'forwardable'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
require_relative 'input'
|
|
3
|
+
require_relative '../report/report'
|
|
4
|
+
require_relative '../report/formatter'
|
|
5
|
+
require_relative '../report/heading_formatter'
|
|
6
6
|
|
|
7
7
|
module Reek
|
|
8
|
-
module
|
|
8
|
+
module CLI
|
|
9
9
|
#
|
|
10
10
|
# Interprets the options set from the command line
|
|
11
11
|
#
|
|
12
|
+
# @api private
|
|
12
13
|
class OptionInterpreter
|
|
13
|
-
include
|
|
14
|
+
include Input
|
|
14
15
|
|
|
15
16
|
extend Forwardable
|
|
16
17
|
|
|
@@ -30,14 +31,17 @@ module Reek
|
|
|
30
31
|
heading_formatter: heading_formatter)
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
# TODO: Move report type mapping into Report
|
|
33
35
|
def report_class
|
|
34
36
|
case @options.report_format
|
|
35
37
|
when :yaml
|
|
36
|
-
Report::
|
|
38
|
+
Report::YAMLReport
|
|
37
39
|
when :json
|
|
38
|
-
Report::
|
|
40
|
+
Report::JSONReport
|
|
39
41
|
when :html
|
|
40
|
-
Report::
|
|
42
|
+
Report::HTMLReport
|
|
43
|
+
when :xml
|
|
44
|
+
Report::XMLReport
|
|
41
45
|
else # :text
|
|
42
46
|
Report::TextReport
|
|
43
47
|
end
|