rubocop 0.31.0 → 0.35.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/CHANGELOG.md +315 -0
- data/README.md +199 -38
- data/config/default.yml +91 -12
- data/config/disabled.yml +45 -4
- data/config/enabled.yml +107 -9
- data/lib/rubocop/ast_node.rb +48 -0
- data/lib/rubocop/cli.rb +11 -1
- data/lib/rubocop/comment_config.rb +4 -1
- data/lib/rubocop/config.rb +26 -17
- data/lib/rubocop/config_loader.rb +61 -14
- data/lib/rubocop/cop/commissioner.rb +7 -12
- data/lib/rubocop/cop/cop.rb +43 -20
- data/lib/rubocop/cop/lint/block_alignment.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +69 -0
- data/lib/rubocop/cop/lint/debugger.rb +9 -48
- data/lib/rubocop/cop/lint/def_end_alignment.rb +8 -4
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +42 -23
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
- data/lib/rubocop/cop/lint/duplicated_key.rb +37 -0
- data/lib/rubocop/cop/lint/end_alignment.rb +33 -13
- data/lib/rubocop/cop/lint/eval.rb +6 -2
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +175 -0
- data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +10 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +31 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +19 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/lint/unneeded_disable.rb +72 -0
- data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
- data/lib/rubocop/cop/metrics/abc_size.rb +17 -6
- data/lib/rubocop/cop/metrics/class_length.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -3
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
- data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -2
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +28 -4
- data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
- data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
- data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_naming.rb +14 -3
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -3
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +10 -1
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
- data/lib/rubocop/cop/mixin/if_node.rb +10 -0
- data/lib/rubocop/cop/mixin/method_preference.rb +28 -0
- data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
- data/lib/rubocop/cop/mixin/on_method_def.rb +4 -5
- data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +4 -7
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
- data/lib/rubocop/cop/mixin/surrounding_space.rb +5 -4
- data/lib/rubocop/cop/offense.rb +16 -3
- data/lib/rubocop/cop/performance/case_when_splat.rb +160 -0
- data/lib/rubocop/cop/performance/count.rb +35 -30
- data/lib/rubocop/cop/performance/detect.rb +16 -3
- data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
- data/lib/rubocop/cop/performance/flat_map.rb +3 -3
- data/lib/rubocop/cop/performance/sample.rb +103 -59
- data/lib/rubocop/cop/performance/size.rb +2 -1
- data/lib/rubocop/cop/performance/string_replacement.rb +187 -0
- data/lib/rubocop/cop/rails/action_filter.rb +31 -5
- data/lib/rubocop/cop/rails/date.rb +15 -14
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +46 -18
- data/lib/rubocop/cop/style/alias.rb +1 -0
- data/lib/rubocop/cop/style/align_hash.rb +8 -15
- data/lib/rubocop/cop/style/align_parameters.rb +19 -7
- data/lib/rubocop/cop/style/and_or.rb +42 -13
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +2 -1
- data/lib/rubocop/cop/style/block_comments.rb +4 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +69 -24
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +40 -12
- data/lib/rubocop/cop/style/case_indentation.rb +18 -4
- data/lib/rubocop/cop/style/collection_methods.rb +2 -20
- data/lib/rubocop/cop/style/command_literal.rb +2 -10
- data/lib/rubocop/cop/style/comment_annotation.rb +29 -8
- data/lib/rubocop/cop/style/copyright.rb +5 -3
- data/lib/rubocop/cop/style/documentation.rb +21 -12
- data/lib/rubocop/cop/style/dot_position.rb +6 -0
- data/lib/rubocop/cop/style/double_negation.rb +4 -15
- data/lib/rubocop/cop/style/each_with_object.rb +17 -4
- data/lib/rubocop/cop/style/else_alignment.rb +2 -1
- data/lib/rubocop/cop/style/empty_else.rb +25 -0
- data/lib/rubocop/cop/style/empty_line_between_defs.rb +39 -14
- data/lib/rubocop/cop/style/encoding.rb +10 -4
- data/lib/rubocop/cop/style/extra_spacing.rb +126 -5
- data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
- data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
- data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
- data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
- data/lib/rubocop/cop/style/first_parameter_indentation.rb +5 -3
- data/lib/rubocop/cop/style/for.rb +2 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +5 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -5
- data/lib/rubocop/cop/style/indent_hash.rb +67 -37
- data/lib/rubocop/cop/style/indentation_width.rb +36 -10
- data/lib/rubocop/cop/style/initial_indentation.rb +37 -0
- data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
- data/lib/rubocop/cop/style/method_call_parentheses.rb +28 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
- data/lib/rubocop/cop/style/multiline_operation_indentation.rb +21 -24
- data/lib/rubocop/cop/style/mutable_constant.rb +35 -0
- data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
- data/lib/rubocop/cop/style/next.rb +50 -15
- data/lib/rubocop/cop/style/non_nil_check.rb +12 -8
- data/lib/rubocop/cop/style/one_line_conditional.rb +8 -4
- data/lib/rubocop/cop/style/option_hash.rb +64 -0
- data/lib/rubocop/cop/style/optional_arguments.rb +49 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +218 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +3 -66
- data/lib/rubocop/cop/style/predicate_name.rb +7 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +2 -13
- data/lib/rubocop/cop/style/redundant_freeze.rb +37 -0
- data/lib/rubocop/cop/style/redundant_return.rb +32 -3
- data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
- data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +81 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +30 -22
- data/lib/rubocop/cop/style/send.rb +18 -0
- data/lib/rubocop/cop/style/signal_exception.rb +24 -11
- data/lib/rubocop/cop/style/single_line_methods.rb +8 -9
- data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
- data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +61 -0
- data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
- data/lib/rubocop/cop/style/string_methods.rb +32 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +11 -10
- data/lib/rubocop/cop/style/symbol_literal.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +62 -13
- data/lib/rubocop/cop/style/trailing_blank_lines.rb +9 -1
- data/lib/rubocop/cop/style/trailing_comma.rb +17 -7
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
- data/lib/rubocop/cop/style/trivial_accessors.rb +10 -1
- data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
- data/lib/rubocop/cop/style/variable_name.rb +5 -0
- data/lib/rubocop/cop/style/while_until_do.rb +1 -1
- data/lib/rubocop/cop/style/word_array.rb +15 -2
- data/lib/rubocop/cop/team.rb +25 -5
- data/lib/rubocop/cop/util.rb +7 -2
- data/lib/rubocop/cop/variable_force/locatable.rb +6 -6
- data/lib/rubocop/cop/variable_force.rb +10 -10
- data/lib/rubocop/formatter/base_formatter.rb +1 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +70 -8
- data/lib/rubocop/formatter/formatter_set.rb +27 -1
- data/lib/rubocop/formatter/progress_formatter.rb +10 -2
- data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
- data/lib/rubocop/node_pattern.rb +390 -0
- data/lib/rubocop/options.rb +148 -81
- data/lib/rubocop/processed_source.rb +7 -2
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +60 -0
- data/lib/rubocop/result_cache.rb +123 -0
- data/lib/rubocop/runner.rb +85 -22
- data/lib/rubocop/target_finder.rb +4 -4
- data/lib/rubocop/token.rb +2 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/warning.rb +11 -0
- data/lib/rubocop.rb +32 -3
- data/relnotes/v0.32.0.md +139 -0
- data/relnotes/v0.32.1.md +122 -0
- data/relnotes/v0.33.0.md +157 -0
- data/relnotes/v0.34.0.md +182 -0
- data/relnotes/v0.34.1.md +129 -0
- data/relnotes/v0.34.2.md +139 -0
- data/relnotes/v0.35.0.md +210 -0
- data/rubocop.gemspec +4 -4
- metadata +50 -12
- data/lib/rubocop/cop/performance/parallel_assignment.rb +0 -79
data/lib/rubocop/options.rb
CHANGED
|
@@ -3,86 +3,30 @@
|
|
|
3
3
|
require 'optparse'
|
|
4
4
|
|
|
5
5
|
module RuboCop
|
|
6
|
-
# This module contains help texts for command line options.
|
|
7
|
-
module OptionsHelp
|
|
8
|
-
TEXT = {
|
|
9
|
-
only: 'Run only the given cop(s).',
|
|
10
|
-
only_guide_cops: ['Run only cops for rules that link to a',
|
|
11
|
-
'style guide.'],
|
|
12
|
-
except: 'Disable the given cop(s).',
|
|
13
|
-
require: 'Require Ruby file.',
|
|
14
|
-
config: 'Specify configuration file.',
|
|
15
|
-
auto_gen_config: ['Generate a configuration file acting as a',
|
|
16
|
-
'TODO list.'],
|
|
17
|
-
force_exclusion: ['Force excluding files specified in the',
|
|
18
|
-
'configuration `Exclude` even if they are',
|
|
19
|
-
'explicitly passed as arguments.'],
|
|
20
|
-
format: ['Choose an output formatter. This option',
|
|
21
|
-
'can be specified multiple times to enable',
|
|
22
|
-
'multiple formatters at the same time.',
|
|
23
|
-
' [p]rogress (default)',
|
|
24
|
-
' [s]imple',
|
|
25
|
-
' [c]lang',
|
|
26
|
-
' [d]isabled cops via inline comments',
|
|
27
|
-
' [fu]ubar',
|
|
28
|
-
' [e]macs',
|
|
29
|
-
' [j]son',
|
|
30
|
-
' [h]tml',
|
|
31
|
-
' [fi]les',
|
|
32
|
-
' [o]ffenses',
|
|
33
|
-
' custom formatter class name'],
|
|
34
|
-
out: ['Write output to a file instead of STDOUT.',
|
|
35
|
-
'This option applies to the previously',
|
|
36
|
-
'specified --format, or the default format',
|
|
37
|
-
'if no format is specified.'],
|
|
38
|
-
fail_level: ['Minimum severity (A/R/C/W/E/F) for exit',
|
|
39
|
-
'with error code.'],
|
|
40
|
-
show_cops: ['Shows the given cops, or all cops by',
|
|
41
|
-
'default, and their configurations for the',
|
|
42
|
-
'current directory.'],
|
|
43
|
-
fail_fast: ['Inspect files in order of modification',
|
|
44
|
-
'time and stop after the first file',
|
|
45
|
-
'containing offenses.'],
|
|
46
|
-
debug: 'Display debug info.',
|
|
47
|
-
display_cop_names: 'Display cop names in offense messages.',
|
|
48
|
-
display_style_guide: 'Display style guide URLs in offense messages.',
|
|
49
|
-
rails: 'Run extra Rails cops.',
|
|
50
|
-
lint: 'Run only lint cops.',
|
|
51
|
-
auto_correct: 'Auto-correct offenses.',
|
|
52
|
-
no_color: 'Disable color output.',
|
|
53
|
-
version: 'Display version.',
|
|
54
|
-
verbose_version: 'Display verbose version.'
|
|
55
|
-
}
|
|
56
|
-
end
|
|
57
|
-
|
|
58
6
|
# This class handles command line options.
|
|
59
7
|
class Options
|
|
60
8
|
DEFAULT_FORMATTER = 'progress'
|
|
61
9
|
EXITING_OPTIONS = [:version, :verbose_version, :show_cops]
|
|
10
|
+
DEFAULT_MAXIMUM_EXCLUSION_ITEMS = 15
|
|
62
11
|
|
|
63
12
|
def initialize
|
|
64
13
|
@options = {}
|
|
14
|
+
@validator = OptionsValidator.new(@options)
|
|
65
15
|
end
|
|
66
16
|
|
|
67
17
|
def parse(args)
|
|
68
18
|
define_options(args).parse!(args)
|
|
19
|
+
# The --no-color CLI option sets `color: false` so we don't want the
|
|
20
|
+
# `no_color` key, which is created automatically.
|
|
21
|
+
@options.delete(:no_color)
|
|
69
22
|
|
|
70
|
-
validate_compatibility
|
|
71
|
-
|
|
72
|
-
[@options, args]
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Cop name validation must be done later than option parsing, so it's not
|
|
76
|
-
# called from within this class.
|
|
77
|
-
def self.validate_cop_list(names)
|
|
78
|
-
return unless names
|
|
23
|
+
@validator.validate_compatibility
|
|
79
24
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
next if Cop::Cop.all.any? { |c| c.cop_name == name } ||
|
|
83
|
-
namespaces.include?(name)
|
|
84
|
-
fail ArgumentError, "Unrecognized cop or namespace: #{name}."
|
|
25
|
+
if @options[:stdin] && !args.one?
|
|
26
|
+
fail ArgumentError, '-s/--stdin requires exactly one path.'
|
|
85
27
|
end
|
|
28
|
+
|
|
29
|
+
[@options, args]
|
|
86
30
|
end
|
|
87
31
|
|
|
88
32
|
private
|
|
@@ -91,6 +35,7 @@ module RuboCop
|
|
|
91
35
|
OptionParser.new do |opts|
|
|
92
36
|
opts.banner = 'Usage: rubocop [options] [file1, file2, ...]'
|
|
93
37
|
|
|
38
|
+
add_list_options(opts)
|
|
94
39
|
add_only_options(opts)
|
|
95
40
|
add_configuration_options(opts, args)
|
|
96
41
|
add_formatting_options(opts)
|
|
@@ -103,11 +48,6 @@ module RuboCop
|
|
|
103
48
|
end
|
|
104
49
|
end
|
|
105
50
|
|
|
106
|
-
def validate_compatibility
|
|
107
|
-
return unless (incompat = @options.keys & EXITING_OPTIONS).size > 1
|
|
108
|
-
fail ArgumentError, "Incompatible cli options: #{incompat.inspect}"
|
|
109
|
-
end
|
|
110
|
-
|
|
111
51
|
def add_only_options(opts)
|
|
112
52
|
add_cop_selection_csv_option('except', opts)
|
|
113
53
|
add_cop_selection_csv_option('only', opts)
|
|
@@ -116,9 +56,14 @@ module RuboCop
|
|
|
116
56
|
|
|
117
57
|
def add_cop_selection_csv_option(option, opts)
|
|
118
58
|
option(opts, "--#{option} [COP1,COP2,...]") do |list|
|
|
119
|
-
@options[:"#{option}"] =
|
|
120
|
-
|
|
121
|
-
|
|
59
|
+
@options[:"#{option}"] =
|
|
60
|
+
if list.empty?
|
|
61
|
+
['']
|
|
62
|
+
else
|
|
63
|
+
list.split(',').map do |c|
|
|
64
|
+
Cop::Cop.qualified_cop_name(c, "--#{option} option")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
122
67
|
end
|
|
123
68
|
end
|
|
124
69
|
|
|
@@ -126,12 +71,15 @@ module RuboCop
|
|
|
126
71
|
option(opts, '-c', '--config FILE')
|
|
127
72
|
|
|
128
73
|
option(opts, '--auto-gen-config') do
|
|
129
|
-
validate_auto_gen_config_option(args)
|
|
130
74
|
@options[:formatters] = [[DEFAULT_FORMATTER],
|
|
131
75
|
[Formatter::DisabledConfigFormatter,
|
|
132
76
|
ConfigLoader::AUTO_GENERATED_FILE]]
|
|
133
77
|
end
|
|
134
78
|
|
|
79
|
+
option(opts, '--exclude-limit COUNT') do
|
|
80
|
+
@validator.validate_exclude_limit_option(args)
|
|
81
|
+
end
|
|
82
|
+
|
|
135
83
|
option(opts, '--force-exclusion')
|
|
136
84
|
end
|
|
137
85
|
|
|
@@ -164,11 +112,16 @@ module RuboCop
|
|
|
164
112
|
|
|
165
113
|
def add_boolean_flags(opts)
|
|
166
114
|
option(opts, '-F', '--fail-fast')
|
|
115
|
+
option(opts, '-C', '--cache FLAG')
|
|
167
116
|
option(opts, '-d', '--debug')
|
|
168
117
|
option(opts, '-D', '--display-cop-names')
|
|
118
|
+
option(opts, '-E', '--extra-details')
|
|
169
119
|
option(opts, '-S', '--display-style-guide')
|
|
170
120
|
option(opts, '-R', '--rails')
|
|
171
|
-
option(opts, '-l', '--lint')
|
|
121
|
+
option(opts, '-l', '--lint') do
|
|
122
|
+
@options[:only] ||= []
|
|
123
|
+
@options[:only] << 'Lint'
|
|
124
|
+
end
|
|
172
125
|
option(opts, '-a', '--auto-correct')
|
|
173
126
|
|
|
174
127
|
@options[:color] = true
|
|
@@ -176,6 +129,11 @@ module RuboCop
|
|
|
176
129
|
|
|
177
130
|
option(opts, '-v', '--version')
|
|
178
131
|
option(opts, '-V', '--verbose-version')
|
|
132
|
+
option(opts, '-s', '--stdin') { @options[:stdin] = $stdin.read }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def add_list_options(opts)
|
|
136
|
+
option(opts, '-L', '--list-target-files')
|
|
179
137
|
end
|
|
180
138
|
|
|
181
139
|
# Sets a value in the @options hash, based on the given long option and its
|
|
@@ -193,14 +151,123 @@ module RuboCop
|
|
|
193
151
|
# e.g. [..., '--auto-correct', ...] to :auto_correct.
|
|
194
152
|
def long_opt_symbol(args)
|
|
195
153
|
long_opt = args.find { |arg| arg.start_with?('--') }
|
|
196
|
-
long_opt[2..-1].sub(/ .*/, '').
|
|
154
|
+
long_opt[2..-1].sub(/ .*/, '').tr('-', '_').to_sym
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Validates option arguments and the options' compatibilty with each other.
|
|
159
|
+
class OptionsValidator
|
|
160
|
+
def initialize(options)
|
|
161
|
+
@options = options
|
|
197
162
|
end
|
|
198
163
|
|
|
199
|
-
|
|
200
|
-
|
|
164
|
+
# Cop name validation must be done later than option parsing, so it's not
|
|
165
|
+
# called from within Options.
|
|
166
|
+
def self.validate_cop_list(names)
|
|
167
|
+
return unless names
|
|
168
|
+
|
|
169
|
+
namespaces = Cop::Cop.all.types.map { |t| t.to_s.capitalize }
|
|
170
|
+
names.each do |name|
|
|
171
|
+
next if Cop::Cop.all.any? { |c| c.cop_name == name }
|
|
172
|
+
next if namespaces.include?(name)
|
|
173
|
+
next if %w(Syntax Lint/Syntax).include?(name)
|
|
201
174
|
|
|
202
|
-
|
|
203
|
-
|
|
175
|
+
fail ArgumentError, "Unrecognized cop or namespace: #{name}."
|
|
176
|
+
end
|
|
204
177
|
end
|
|
178
|
+
|
|
179
|
+
def validate_compatibility
|
|
180
|
+
if @options.key?(:only) &&
|
|
181
|
+
(@options[:only] & %w(Lint/UnneededDisable UnneededDisable)).any?
|
|
182
|
+
fail ArgumentError, 'Lint/UnneededDisable can not be used with --only.'
|
|
183
|
+
end
|
|
184
|
+
if @options.key?(:except) &&
|
|
185
|
+
(@options[:except] & %w(Lint/Syntax Syntax)).any?
|
|
186
|
+
fail ArgumentError, 'Syntax checking can not be turned off.'
|
|
187
|
+
end
|
|
188
|
+
if @options.key?(:cache) && !%w(true false).include?(@options[:cache])
|
|
189
|
+
fail ArgumentError, '-C/--cache argument must be true or false'
|
|
190
|
+
end
|
|
191
|
+
return if (incompat = @options.keys & Options::EXITING_OPTIONS).size <= 1
|
|
192
|
+
fail ArgumentError, "Incompatible cli options: #{incompat.inspect}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def validate_exclude_limit_option(args)
|
|
196
|
+
if @options[:exclude_limit] !~ /^\d+$/
|
|
197
|
+
# Emulate OptionParser's behavior to make failures consistent regardless
|
|
198
|
+
# of option order.
|
|
199
|
+
fail OptionParser::MissingArgument
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# --exclude-limit is valid if there's a parsed or yet unparsed
|
|
203
|
+
# --auto-gen-config.
|
|
204
|
+
return if @options[:auto_gen_config] || args.include?('--auto-gen-config')
|
|
205
|
+
|
|
206
|
+
fail ArgumentError,
|
|
207
|
+
'--exclude-limit can only be used with --auto-gen-config.'
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# This module contains help texts for command line options.
|
|
212
|
+
module OptionsHelp
|
|
213
|
+
MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
|
|
214
|
+
TEXT = {
|
|
215
|
+
only: 'Run only the given cop(s).',
|
|
216
|
+
only_guide_cops: ['Run only cops for rules that link to a',
|
|
217
|
+
'style guide.'],
|
|
218
|
+
except: 'Disable the given cop(s).',
|
|
219
|
+
require: 'Require Ruby file.',
|
|
220
|
+
config: 'Specify configuration file.',
|
|
221
|
+
auto_gen_config: ['Generate a configuration file acting as a',
|
|
222
|
+
'TODO list.'],
|
|
223
|
+
exclude_limit: ['Used together with --auto-gen-config to',
|
|
224
|
+
'set the limit for how many Exclude',
|
|
225
|
+
"properties to generate. Default is #{MAX_EXCL}."],
|
|
226
|
+
force_exclusion: ['Force excluding files specified in the',
|
|
227
|
+
'configuration `Exclude` even if they are',
|
|
228
|
+
'explicitly passed as arguments.'],
|
|
229
|
+
format: ['Choose an output formatter. This option',
|
|
230
|
+
'can be specified multiple times to enable',
|
|
231
|
+
'multiple formatters at the same time.',
|
|
232
|
+
' [p]rogress (default)',
|
|
233
|
+
' [s]imple',
|
|
234
|
+
' [c]lang',
|
|
235
|
+
' [d]isabled cops via inline comments',
|
|
236
|
+
' [fu]ubar',
|
|
237
|
+
' [e]macs',
|
|
238
|
+
' [j]son',
|
|
239
|
+
' [h]tml',
|
|
240
|
+
' [fi]les',
|
|
241
|
+
' [o]ffenses',
|
|
242
|
+
' custom formatter class name'],
|
|
243
|
+
out: ['Write output to a file instead of STDOUT.',
|
|
244
|
+
'This option applies to the previously',
|
|
245
|
+
'specified --format, or the default format',
|
|
246
|
+
'if no format is specified.'],
|
|
247
|
+
fail_level: ['Minimum severity (A/R/C/W/E/F) for exit',
|
|
248
|
+
'with error code.'],
|
|
249
|
+
show_cops: ['Shows the given cops, or all cops by',
|
|
250
|
+
'default, and their configurations for the',
|
|
251
|
+
'current directory.'],
|
|
252
|
+
fail_fast: ['Inspect files in order of modification',
|
|
253
|
+
'time and stop after the first file',
|
|
254
|
+
'containing offenses.'],
|
|
255
|
+
cache: ["Use result caching (FLAG=true) or don't",
|
|
256
|
+
'(FLAG=false), default determined by',
|
|
257
|
+
'configuration parameter AllCops: UseCache.'],
|
|
258
|
+
debug: 'Display debug info.',
|
|
259
|
+
display_cop_names: 'Display cop names in offense messages.',
|
|
260
|
+
display_style_guide: 'Display style guide URLs in offense messages.',
|
|
261
|
+
extra_details: 'Display extra details in offense messages.',
|
|
262
|
+
rails: 'Run extra Rails cops.',
|
|
263
|
+
lint: 'Run only lint cops.',
|
|
264
|
+
list_target_files: 'List all files RuboCop will inspect.',
|
|
265
|
+
auto_correct: 'Auto-correct offenses.',
|
|
266
|
+
no_color: 'Disable color output.',
|
|
267
|
+
version: 'Display version.',
|
|
268
|
+
verbose_version: 'Display verbose version.',
|
|
269
|
+
stdin: ['Pipe source from STDIN.',
|
|
270
|
+
'This is useful for editor integration.']
|
|
271
|
+
}
|
|
205
272
|
end
|
|
206
273
|
end
|
|
@@ -13,7 +13,12 @@ module RuboCop
|
|
|
13
13
|
:parser_error, :raw_source
|
|
14
14
|
|
|
15
15
|
def self.from_file(path)
|
|
16
|
-
|
|
16
|
+
file = File.read(path)
|
|
17
|
+
new(file, path)
|
|
18
|
+
rescue Errno::ENOENT
|
|
19
|
+
abort("#{Rainbow('rubocop: No such file or directory').red} -- #{path}")
|
|
20
|
+
rescue => ex
|
|
21
|
+
abort("#{Rainbow("rubocop: #{ex.message}").red} -- #{path}")
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def initialize(source, path = nil)
|
|
@@ -41,7 +46,7 @@ module RuboCop
|
|
|
41
46
|
def lines
|
|
42
47
|
@lines ||= begin
|
|
43
48
|
all_lines = raw_source.lines.map(&:chomp)
|
|
44
|
-
last_token_line = tokens.any? ? tokens.last.pos.line : all_lines.
|
|
49
|
+
last_token_line = tokens.any? ? tokens.last.pos.line : all_lines.size
|
|
45
50
|
result = []
|
|
46
51
|
all_lines.each_with_index do |line, ix|
|
|
47
52
|
break if ix >= last_token_line && line == '__END__'
|
data/lib/rubocop/rake_task.rb
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
# Common methods and behaviors for dealing with remote config files.
|
|
7
|
+
class RemoteConfig
|
|
8
|
+
CACHE_LIFETIME = 24 * 60 * 60
|
|
9
|
+
|
|
10
|
+
def initialize(url)
|
|
11
|
+
@uri = URI.parse(url)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def file
|
|
15
|
+
return cache_path unless cache_path_expired?
|
|
16
|
+
|
|
17
|
+
http = Net::HTTP.new(@uri.hostname, @uri.port)
|
|
18
|
+
http.use_ssl = true if @uri.instance_of? URI::HTTPS
|
|
19
|
+
|
|
20
|
+
request = Net::HTTP::Get.new(@uri.request_uri)
|
|
21
|
+
if cache_path_exists?
|
|
22
|
+
request['If-Modified-Since'] = File.stat(cache_path).mtime.rfc2822
|
|
23
|
+
end
|
|
24
|
+
response = http.request(request)
|
|
25
|
+
|
|
26
|
+
cache_path.tap do |f|
|
|
27
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
28
|
+
open f, 'w' do |io|
|
|
29
|
+
io.write response.body
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def cache_path
|
|
38
|
+
".rubocop-#{cache_name_from_uri}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cache_path_exists?
|
|
42
|
+
@cache_path_exists ||= File.exist?(cache_path)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cache_path_expired?
|
|
46
|
+
return true unless cache_path_exists?
|
|
47
|
+
|
|
48
|
+
@cache_path_expired ||= begin
|
|
49
|
+
file_age = (Time.now - File.stat(cache_path).mtime).to_f
|
|
50
|
+
(file_age / CACHE_LIFETIME) > 1
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def cache_name_from_uri
|
|
55
|
+
uri = @uri.clone
|
|
56
|
+
uri.query = nil
|
|
57
|
+
uri.to_s.gsub!(/[^0-9A-Za-z]/, '-')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'digest/md5'
|
|
4
|
+
require 'find'
|
|
5
|
+
require 'tmpdir'
|
|
6
|
+
require 'etc'
|
|
7
|
+
|
|
8
|
+
module RuboCop
|
|
9
|
+
# Provides functionality for caching rubocop runs.
|
|
10
|
+
class ResultCache
|
|
11
|
+
# Include the user name in the path as a simple means of avoiding write
|
|
12
|
+
# collisions.
|
|
13
|
+
def initialize(file, options, config_store, cache_root = nil)
|
|
14
|
+
cache_root ||= ResultCache.cache_root(config_store)
|
|
15
|
+
@path = File.join(cache_root, rubocop_checksum, RUBY_VERSION,
|
|
16
|
+
relevant_options(options),
|
|
17
|
+
file_checksum(file, config_store))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def valid?
|
|
21
|
+
File.exist?(@path)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def load
|
|
25
|
+
Marshal.load(IO.binread(@path))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def save(offenses, disabled_line_ranges, comments)
|
|
29
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
|
30
|
+
preliminary_path = "#{@path}_#{rand(1_000_000_000)}"
|
|
31
|
+
File.open(preliminary_path, 'wb') do |f|
|
|
32
|
+
# The Hash[x.sort] call is a trick that converts a Hash with a default
|
|
33
|
+
# block to a Hash without a default block. Thus making it possible to
|
|
34
|
+
# dump.
|
|
35
|
+
f.write(Marshal.dump([offenses, Hash[disabled_line_ranges.sort],
|
|
36
|
+
comments]))
|
|
37
|
+
end
|
|
38
|
+
# The preliminary path is used so that if there are multiple RuboCop
|
|
39
|
+
# processes trying to save data for the same inspected file
|
|
40
|
+
# simultaneously, the only problem we run in to is a competition who gets
|
|
41
|
+
# to write to the final file. The contents are the same, so no corruption
|
|
42
|
+
# of data should occur.
|
|
43
|
+
FileUtils.mv(preliminary_path, @path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Remove old files so that the cache doesn't grow too big. When the
|
|
47
|
+
# threshold MaxFilesInCache has been exceeded, the oldest 50% all the files
|
|
48
|
+
# in the cache are removed. The reason for removing so much is that
|
|
49
|
+
# cleaning should be done relatively seldom, since there is a slight risk
|
|
50
|
+
# that some other RuboCop process was just about to read the file, when
|
|
51
|
+
# there's parallel execution and the cache is shared.
|
|
52
|
+
def self.cleanup(config_store, verbose, cache_root = nil)
|
|
53
|
+
return if inhibit_cleanup # OPTIMIZE: For faster testing
|
|
54
|
+
cache_root ||= cache_root(config_store)
|
|
55
|
+
return unless File.exist?(cache_root)
|
|
56
|
+
|
|
57
|
+
files, dirs = Find.find(cache_root).partition { |path| File.file?(path) }
|
|
58
|
+
if files.length > config_store.for('.')['AllCops']['MaxFilesInCache'] &&
|
|
59
|
+
files.length > 1
|
|
60
|
+
# Add 1 to half the number of files, so that we remove the file if
|
|
61
|
+
# there's only 1 left.
|
|
62
|
+
remove_count = 1 + files.length / 2
|
|
63
|
+
if verbose
|
|
64
|
+
puts "Removing the #{remove_count} oldest files from #{cache_root}"
|
|
65
|
+
end
|
|
66
|
+
sorted = files.sort_by { |path| File.mtime(path) }
|
|
67
|
+
begin
|
|
68
|
+
sorted[0, remove_count].each { |path| File.delete(path) }
|
|
69
|
+
dirs.each { |dir| Dir.rmdir(dir) if Dir["#{dir}/*"].empty? }
|
|
70
|
+
rescue Errno::ENOENT
|
|
71
|
+
# This can happen if parallel RuboCop invocations try to remove the
|
|
72
|
+
# same files. No problem.
|
|
73
|
+
puts $ERROR_INFO if verbose
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def self.cache_root(config_store)
|
|
81
|
+
root = config_store.for('.')['AllCops']['CacheRootDirectory']
|
|
82
|
+
root = File.join(Dir.tmpdir, Etc.getlogin) if root == '/tmp'
|
|
83
|
+
File.join(root, 'rubocop_cache')
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def file_checksum(file, config_store)
|
|
87
|
+
Digest::MD5.hexdigest(Dir.pwd + file + IO.read(file) +
|
|
88
|
+
config_store.for(file).to_s)
|
|
89
|
+
rescue Errno::ENOENT
|
|
90
|
+
# Spurious files that come and go should not cause a crash, at least not
|
|
91
|
+
# here.
|
|
92
|
+
'_'
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class << self
|
|
96
|
+
attr_accessor :source_checksum, :inhibit_cleanup
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The checksum of the rubocop program running the inspection.
|
|
100
|
+
def rubocop_checksum
|
|
101
|
+
ResultCache.source_checksum ||=
|
|
102
|
+
begin
|
|
103
|
+
lib_root = File.join(File.dirname(__FILE__), '..')
|
|
104
|
+
bin_root = File.join(lib_root, '..', 'bin')
|
|
105
|
+
source = Find.find(lib_root, bin_root).sort.map do |path|
|
|
106
|
+
IO.read(path) if File.file?(path)
|
|
107
|
+
end
|
|
108
|
+
Digest::MD5.hexdigest(source.join)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
NON_CHANGING = [:color, :format, :formatters, :out, :debug, :fail_level,
|
|
113
|
+
:cache, :fail_fast, :stdin]
|
|
114
|
+
|
|
115
|
+
# Return the options given at invocation, minus the ones that have no
|
|
116
|
+
# effect on which offenses and disabled line ranges are found, and thus
|
|
117
|
+
# don't affect caching.
|
|
118
|
+
def relevant_options(options)
|
|
119
|
+
options = options.reject { |key, _| NON_CHANGING.include?(key) }
|
|
120
|
+
options.to_s.gsub(/[^a-z]+/i, '_')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|