rubocop 0.73.0 → 0.74.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/console +1 -0
- data/config/default.yml +2 -1
- data/lib/rubocop.rb +3 -0
- data/lib/rubocop/ast/node.rb +1 -7
- data/lib/rubocop/config.rb +17 -537
- data/lib/rubocop/config_obsoletion.rb +201 -0
- data/lib/rubocop/config_validator.rb +239 -0
- data/lib/rubocop/cop/layout/extra_spacing.rb +14 -53
- data/lib/rubocop/cop/layout/indentation_width.rb +19 -5
- data/lib/rubocop/cop/layout/space_around_operators.rb +42 -23
- data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +22 -40
- data/lib/rubocop/cop/lint/debugger.rb +0 -2
- data/lib/rubocop/cop/lint/empty_interpolation.rb +4 -4
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +56 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +7 -8
- data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +6 -6
- data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +6 -1
- data/lib/rubocop/cop/metrics/line_length.rb +6 -0
- data/lib/rubocop/cop/mixin/documentation_comment.rb +0 -2
- data/lib/rubocop/cop/mixin/interpolation.rb +27 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +87 -0
- data/lib/rubocop/cop/mixin/surrounding_space.rb +7 -5
- data/lib/rubocop/cop/style/commented_keyword.rb +8 -28
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -3
- data/lib/rubocop/cop/style/constant_visibility.rb +13 -2
- data/lib/rubocop/cop/style/guard_clause.rb +39 -10
- data/lib/rubocop/cop/style/lambda.rb +0 -2
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +4 -6
- data/lib/rubocop/cop/style/variable_interpolation.rb +6 -16
- data/lib/rubocop/path_util.rb +1 -1
- data/lib/rubocop/processed_source.rb +4 -0
- data/lib/rubocop/rspec/expect_offense.rb +4 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +5 -2
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
# This class handles obsolete configuration.
|
5
|
+
class ConfigObsoletion
|
6
|
+
RENAMED_COPS = {
|
7
|
+
'Style/SingleSpaceBeforeFirstArg' => 'Layout/SpaceBeforeFirstArg',
|
8
|
+
'Style/MethodCallParentheses' => 'Style/MethodCallWithoutArgsParentheses',
|
9
|
+
'Style/DeprecatedHashMethods' => 'Style/PreferredHashMethods',
|
10
|
+
'Style/OpMethod' => 'Naming/BinaryOperatorParameterName',
|
11
|
+
'Layout/FirstParameterIndentation' => 'Layout/IndentFirstArgument',
|
12
|
+
'Layout/IndentArray' => 'Layout/IndentFirstArrayElement',
|
13
|
+
'Layout/IndentHash' => 'Layout/IndentFirstHashElement'
|
14
|
+
}.map do |old_name, new_name|
|
15
|
+
[old_name, "The `#{old_name}` cop has been renamed to `#{new_name}`."]
|
16
|
+
end
|
17
|
+
|
18
|
+
MOVED_COPS = {
|
19
|
+
'Security' => 'Lint/Eval',
|
20
|
+
'Naming' => %w[Style/ClassAndModuleCamelCase Style/ConstantName
|
21
|
+
Style/FileName Style/MethodName Style/PredicateName
|
22
|
+
Style/VariableName Style/VariableNumber
|
23
|
+
Style/AccessorMethodName Style/AsciiIdentifiers],
|
24
|
+
'Layout' => %w[Lint/BlockAlignment Lint/EndAlignment
|
25
|
+
Lint/DefEndAlignment],
|
26
|
+
'Lint' => 'Style/FlipFlop'
|
27
|
+
}.map do |new_department, old_names|
|
28
|
+
Array(old_names).map do |old_name|
|
29
|
+
[old_name, "The `#{old_name}` cop has been moved to " \
|
30
|
+
"`#{new_department}/#{old_name.split('/').last}`."]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
REMOVED_COPS = {
|
35
|
+
'Rails/DefaultScope' => nil,
|
36
|
+
'Layout/SpaceAfterControlKeyword' => 'Layout/SpaceAroundKeyword',
|
37
|
+
'Layout/SpaceBeforeModifierKeyword' => 'Layout/SpaceAroundKeyword',
|
38
|
+
'Style/SpaceAfterControlKeyword' => 'Layout/SpaceAroundKeyword',
|
39
|
+
'Style/SpaceBeforeModifierKeyword' => 'Layout/SpaceAroundKeyword',
|
40
|
+
'Style/TrailingComma' => 'Style/TrailingCommaInArguments, ' \
|
41
|
+
'Style/TrailingCommaInArrayLiteral, and/or ' \
|
42
|
+
'Style/TrailingCommaInHashLiteral',
|
43
|
+
'Style/TrailingCommaInLiteral' => 'Style/TrailingCommaInArrayLiteral ' \
|
44
|
+
'and/or ' \
|
45
|
+
'Style/TrailingCommaInHashLiteral',
|
46
|
+
'Lint/RescueWithoutErrorClass' => 'Style/RescueStandardError'
|
47
|
+
}.map do |old_name, other_cops|
|
48
|
+
if other_cops
|
49
|
+
more = ". Please use #{other_cops} instead".gsub(%r{[A-Z]\w+/\w+},
|
50
|
+
'`\&`')
|
51
|
+
end
|
52
|
+
[old_name, "The `#{old_name}` cop has been removed#{more}."]
|
53
|
+
end
|
54
|
+
|
55
|
+
REMOVED_COPS_WITH_REASON = {
|
56
|
+
'Lint/InvalidCharacterLiteral' => 'it was never being actually triggered',
|
57
|
+
'Lint/SpaceBeforeFirstArg' =>
|
58
|
+
'it was a duplicate of `Layout/SpaceBeforeFirstArg`. Please use ' \
|
59
|
+
'`Layout/SpaceBeforeFirstArg` instead'
|
60
|
+
}.map do |cop_name, reason|
|
61
|
+
[cop_name, "The `#{cop_name}` cop has been removed since #{reason}."]
|
62
|
+
end
|
63
|
+
|
64
|
+
SPLIT_COPS = {
|
65
|
+
'Style/MethodMissing' =>
|
66
|
+
'The `Style/MethodMissing` cop has been split into ' \
|
67
|
+
'`Style/MethodMissingSuper` and `Style/MissingRespondToMissing`.'
|
68
|
+
}.to_a
|
69
|
+
|
70
|
+
OBSOLETE_COPS = Hash[*(RENAMED_COPS + MOVED_COPS + REMOVED_COPS +
|
71
|
+
REMOVED_COPS_WITH_REASON + SPLIT_COPS).flatten]
|
72
|
+
|
73
|
+
OBSOLETE_PARAMETERS = [
|
74
|
+
{
|
75
|
+
cops: %w[Layout/SpaceAroundOperators Style/SpaceAroundOperators],
|
76
|
+
parameters: 'MultiSpaceAllowedForOperators',
|
77
|
+
alternative: 'If your intention was to allow extra spaces for ' \
|
78
|
+
'alignment, please use AllowForAlignment: true instead.'
|
79
|
+
},
|
80
|
+
{
|
81
|
+
cops: 'Style/Encoding',
|
82
|
+
parameters: %w[EnforcedStyle SupportedStyles
|
83
|
+
AutoCorrectEncodingComment],
|
84
|
+
alternative: 'Style/Encoding no longer supports styles. ' \
|
85
|
+
'The "never" behavior is always assumed.'
|
86
|
+
},
|
87
|
+
{
|
88
|
+
cops: 'Style/IfUnlessModifier',
|
89
|
+
parameters: 'MaxLineLength',
|
90
|
+
alternative: '`Style/IfUnlessModifier: MaxLineLength` has been ' \
|
91
|
+
'removed. Use `Metrics/LineLength: Max` instead'
|
92
|
+
},
|
93
|
+
{
|
94
|
+
cops: 'Style/WhileUntilModifier',
|
95
|
+
parameters: 'MaxLineLength',
|
96
|
+
alternative: '`Style/WhileUntilModifier: MaxLineLength` has been ' \
|
97
|
+
'removed. Use `Metrics/LineLength: Max` instead'
|
98
|
+
},
|
99
|
+
{
|
100
|
+
cops: 'AllCops',
|
101
|
+
parameters: 'RunRailsCops',
|
102
|
+
alternative: "Use the following configuration instead:\n" \
|
103
|
+
"Rails:\n Enabled: true"
|
104
|
+
},
|
105
|
+
{
|
106
|
+
cops: 'Layout/CaseIndentation',
|
107
|
+
parameters: 'IndentWhenRelativeTo',
|
108
|
+
alternative: '`IndentWhenRelativeTo` has been renamed to ' \
|
109
|
+
'`EnforcedStyle`'
|
110
|
+
},
|
111
|
+
{
|
112
|
+
cops: %w[Lint/BlockAlignment Layout/BlockAlignment Lint/EndAlignment
|
113
|
+
Layout/EndAlignment Lint/DefEndAlignment
|
114
|
+
Layout/DefEndAlignment],
|
115
|
+
parameters: 'AlignWith',
|
116
|
+
alternative: '`AlignWith` has been renamed to `EnforcedStyleAlignWith`'
|
117
|
+
},
|
118
|
+
{
|
119
|
+
cops: 'Rails/UniqBeforePluck',
|
120
|
+
parameters: 'EnforcedMode',
|
121
|
+
alternative: '`EnforcedMode` has been renamed to `EnforcedStyle`'
|
122
|
+
}
|
123
|
+
].freeze
|
124
|
+
|
125
|
+
OBSOLETE_ENFORCED_STYLES = [
|
126
|
+
{
|
127
|
+
cop: 'Layout/IndentationConsistency',
|
128
|
+
parameter: 'EnforcedStyle',
|
129
|
+
enforced_style: 'rails',
|
130
|
+
alternative: '`EnforcedStyle: rails` has been renamed to ' \
|
131
|
+
'`EnforcedStyle: indented_internal_methods`'
|
132
|
+
}
|
133
|
+
].freeze
|
134
|
+
|
135
|
+
def initialize(config)
|
136
|
+
@config = config
|
137
|
+
end
|
138
|
+
|
139
|
+
def reject_obsolete_cops_and_parameters
|
140
|
+
messages = [obsolete_cops, obsolete_parameters,
|
141
|
+
obsolete_enforced_style].flatten.compact
|
142
|
+
return if messages.empty?
|
143
|
+
|
144
|
+
raise ValidationError, messages.join("\n")
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def obsolete_cops
|
150
|
+
OBSOLETE_COPS.map do |cop_name, message|
|
151
|
+
next unless @config.key?(cop_name) ||
|
152
|
+
@config.key?(Cop::Badge.parse(cop_name).cop_name)
|
153
|
+
|
154
|
+
message + "\n(obsolete configuration found in " \
|
155
|
+
"#{smart_loaded_path}, please update it)"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def obsolete_enforced_style
|
160
|
+
OBSOLETE_ENFORCED_STYLES.map do |params|
|
161
|
+
obsolete_enforced_style_message(params[:cop], params[:parameter],
|
162
|
+
params[:enforced_style],
|
163
|
+
params[:alternative])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def obsolete_enforced_style_message(cop, param, enforced_style, alternative)
|
168
|
+
style = @config[cop]&.detect { |key, _| key.start_with?(param) }
|
169
|
+
|
170
|
+
return unless style && style[1] == enforced_style
|
171
|
+
|
172
|
+
"obsolete `#{param}: #{enforced_style}` (for #{cop}) found in " \
|
173
|
+
"#{smart_loaded_path}\n#{alternative}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def obsolete_parameters
|
177
|
+
OBSOLETE_PARAMETERS.map do |params|
|
178
|
+
obsolete_parameter_message(params[:cops], params[:parameters],
|
179
|
+
params[:alternative])
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def obsolete_parameter_message(cops, parameters, alternative)
|
184
|
+
Array(cops).map do |cop|
|
185
|
+
obsolete_parameters = Array(parameters).select do |param|
|
186
|
+
@config[cop]&.key?(param)
|
187
|
+
end
|
188
|
+
next if obsolete_parameters.empty?
|
189
|
+
|
190
|
+
obsolete_parameters.map do |parameter|
|
191
|
+
"obsolete parameter #{parameter} (for #{cop}) found in " \
|
192
|
+
"#{smart_loaded_path}\n#{alternative}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def smart_loaded_path
|
198
|
+
PathUtil.smart_path(@config.loaded_path)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
# Handles validation of configuration, for example cop names, parameter
|
7
|
+
# names, and Ruby versions.
|
8
|
+
class ConfigValidator
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
COMMON_PARAMS = %w[Exclude Include Severity inherit_mode
|
12
|
+
AutoCorrect StyleGuide Details].freeze
|
13
|
+
INTERNAL_PARAMS = %w[Description StyleGuide VersionAdded
|
14
|
+
VersionChanged Reference Safe SafeAutoCorrect].freeze
|
15
|
+
|
16
|
+
# 2.3 is the oldest officially supported Ruby version.
|
17
|
+
DEFAULT_RUBY_VERSION = 2.3
|
18
|
+
KNOWN_RUBIES = [2.3, 2.4, 2.5, 2.6, 2.7].freeze
|
19
|
+
OBSOLETE_RUBIES = {
|
20
|
+
1.9 => '0.50', 2.0 => '0.50', 2.1 => '0.58', 2.2 => '0.69'
|
21
|
+
}.freeze
|
22
|
+
RUBY_VERSION_FILENAME = '.ruby-version'
|
23
|
+
|
24
|
+
def_delegators :@config,
|
25
|
+
:smart_loaded_path, :for_all_cops, :find_file_upwards,
|
26
|
+
:base_dir_for_path_parameters, :bundler_lock_file_path
|
27
|
+
|
28
|
+
def initialize(config)
|
29
|
+
@config = config
|
30
|
+
@config_obsoletion = ConfigObsoletion.new(config)
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate
|
34
|
+
# Don't validate RuboCop's own files. Avoids infinite recursion.
|
35
|
+
base_config_path = File.expand_path(File.join(ConfigLoader::RUBOCOP_HOME,
|
36
|
+
'config'))
|
37
|
+
return if File.expand_path(@config.loaded_path)
|
38
|
+
.start_with?(base_config_path)
|
39
|
+
|
40
|
+
valid_cop_names, invalid_cop_names = @config.keys.partition do |key|
|
41
|
+
ConfigLoader.default_configuration.key?(key)
|
42
|
+
end
|
43
|
+
|
44
|
+
@config_obsoletion.reject_obsolete_cops_and_parameters
|
45
|
+
|
46
|
+
warn_about_unrecognized_cops(invalid_cop_names)
|
47
|
+
check_target_ruby
|
48
|
+
validate_parameter_names(valid_cop_names)
|
49
|
+
validate_enforced_styles(valid_cop_names)
|
50
|
+
validate_syntax_cop
|
51
|
+
reject_mutually_exclusive_defaults
|
52
|
+
end
|
53
|
+
|
54
|
+
def target_ruby_version
|
55
|
+
@target_ruby_version ||= begin
|
56
|
+
if for_all_cops['TargetRubyVersion']
|
57
|
+
@target_ruby_version_source = :rubocop_yml
|
58
|
+
|
59
|
+
for_all_cops['TargetRubyVersion'].to_f
|
60
|
+
elsif target_ruby_version_from_version_file
|
61
|
+
@target_ruby_version_source = :ruby_version_file
|
62
|
+
|
63
|
+
target_ruby_version_from_version_file
|
64
|
+
elsif target_ruby_version_from_bundler_lock_file
|
65
|
+
@target_ruby_version_source = :bundler_lock_file
|
66
|
+
|
67
|
+
target_ruby_version_from_bundler_lock_file
|
68
|
+
else
|
69
|
+
DEFAULT_RUBY_VERSION
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_section_presence(name)
|
75
|
+
return unless @config.key?(name) && @config[name].nil?
|
76
|
+
|
77
|
+
raise ValidationError,
|
78
|
+
"empty section #{name} found in #{smart_loaded_path}"
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def check_target_ruby
|
84
|
+
return if KNOWN_RUBIES.include?(target_ruby_version)
|
85
|
+
|
86
|
+
msg = if OBSOLETE_RUBIES.include?(target_ruby_version)
|
87
|
+
"RuboCop found unsupported Ruby version #{target_ruby_version} " \
|
88
|
+
"in #{target_ruby_source}. #{target_ruby_version}-compatible " \
|
89
|
+
'analysis was dropped after version ' \
|
90
|
+
"#{OBSOLETE_RUBIES[target_ruby_version]}."
|
91
|
+
else
|
92
|
+
'RuboCop found unknown Ruby version ' \
|
93
|
+
"#{target_ruby_version.inspect} in #{target_ruby_source}."
|
94
|
+
end
|
95
|
+
|
96
|
+
msg += "\nSupported versions: #{KNOWN_RUBIES.join(', ')}"
|
97
|
+
|
98
|
+
raise ValidationError, msg
|
99
|
+
end
|
100
|
+
|
101
|
+
def warn_about_unrecognized_cops(invalid_cop_names)
|
102
|
+
invalid_cop_names.each do |name|
|
103
|
+
# There could be a custom cop with this name. If so, don't warn
|
104
|
+
next if Cop::Cop.registry.contains_cop_matching?([name])
|
105
|
+
|
106
|
+
# Special case for inherit_mode, which is a directive that we keep in
|
107
|
+
# the configuration (even though it's not a cop), because it's easier
|
108
|
+
# to do so than to pass the value around to various methods.
|
109
|
+
next if name == 'inherit_mode'
|
110
|
+
|
111
|
+
warn Rainbow("Warning: unrecognized cop #{name} found in " \
|
112
|
+
"#{smart_loaded_path}").yellow
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def validate_syntax_cop
|
117
|
+
syntax_config = @config['Lint/Syntax']
|
118
|
+
default_config = ConfigLoader.default_configuration['Lint/Syntax']
|
119
|
+
|
120
|
+
return unless syntax_config &&
|
121
|
+
default_config.merge(syntax_config) != default_config
|
122
|
+
|
123
|
+
raise ValidationError,
|
124
|
+
"configuration for Syntax cop found in #{smart_loaded_path}\n" \
|
125
|
+
'It\'s not possible to disable this cop.'
|
126
|
+
end
|
127
|
+
|
128
|
+
def validate_parameter_names(valid_cop_names)
|
129
|
+
valid_cop_names.each do |name|
|
130
|
+
validate_section_presence(name)
|
131
|
+
default_config = ConfigLoader.default_configuration[name]
|
132
|
+
|
133
|
+
@config[name].each_key do |param|
|
134
|
+
next if COMMON_PARAMS.include?(param) || default_config.key?(param)
|
135
|
+
|
136
|
+
message =
|
137
|
+
"Warning: #{name} does not support #{param} parameter.\n\n" \
|
138
|
+
"Supported parameters are:\n\n" \
|
139
|
+
" - #{(default_config.keys - INTERNAL_PARAMS).join("\n - ")}\n"
|
140
|
+
|
141
|
+
warn Rainbow(message).yellow.to_s
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def validate_enforced_styles(valid_cop_names)
|
147
|
+
valid_cop_names.each do |name|
|
148
|
+
styles = @config[name].select { |key, _| key.start_with?('Enforced') }
|
149
|
+
|
150
|
+
styles.each do |style_name, style|
|
151
|
+
supported_key = RuboCop::Cop::Util.to_supported_styles(style_name)
|
152
|
+
valid = ConfigLoader.default_configuration[name][supported_key]
|
153
|
+
|
154
|
+
next unless valid
|
155
|
+
next if valid.include?(style)
|
156
|
+
next if validate_support_and_has_list(name, style, valid)
|
157
|
+
|
158
|
+
msg = "invalid #{style_name} '#{style}' for #{name} found in " \
|
159
|
+
"#{smart_loaded_path}\n" \
|
160
|
+
"Valid choices are: #{valid.join(', ')}"
|
161
|
+
raise ValidationError, msg
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def validate_support_and_has_list(name, formats, valid)
|
167
|
+
ConfigLoader.default_configuration[name]['AllowMultipleStyles'] &&
|
168
|
+
formats.is_a?(Array) &&
|
169
|
+
formats.all? { |format| valid.include?(format) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def target_ruby_source
|
173
|
+
case @target_ruby_version_source
|
174
|
+
when :ruby_version_file
|
175
|
+
"`#{RUBY_VERSION_FILENAME}`"
|
176
|
+
when :bundler_lock_file
|
177
|
+
"`#{bundler_lock_file_path}`"
|
178
|
+
when :rubocop_yml
|
179
|
+
"`TargetRubyVersion` parameter (in #{smart_loaded_path})"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def ruby_version_file
|
184
|
+
@ruby_version_file ||=
|
185
|
+
find_file_upwards(RUBY_VERSION_FILENAME, base_dir_for_path_parameters)
|
186
|
+
end
|
187
|
+
|
188
|
+
def target_ruby_version_from_version_file
|
189
|
+
file = ruby_version_file
|
190
|
+
return unless file && File.file?(file)
|
191
|
+
|
192
|
+
@target_ruby_version_from_version_file ||=
|
193
|
+
File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
|
194
|
+
md[:version].to_f
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def target_ruby_version_from_bundler_lock_file
|
199
|
+
@target_ruby_version_from_bundler_lock_file ||=
|
200
|
+
read_ruby_version_from_bundler_lock_file
|
201
|
+
end
|
202
|
+
|
203
|
+
def read_ruby_version_from_bundler_lock_file
|
204
|
+
lock_file_path = bundler_lock_file_path
|
205
|
+
return nil unless lock_file_path
|
206
|
+
|
207
|
+
in_ruby_section = false
|
208
|
+
File.foreach(lock_file_path) do |line|
|
209
|
+
# If ruby is in Gemfile.lock or gems.lock, there should be two lines
|
210
|
+
# towards the bottom of the file that look like:
|
211
|
+
# RUBY VERSION
|
212
|
+
# ruby W.X.YpZ
|
213
|
+
# We ultimately want to match the "ruby W.X.Y.pZ" line, but there's
|
214
|
+
# extra logic to make sure we only start looking once we've seen the
|
215
|
+
# "RUBY VERSION" line.
|
216
|
+
in_ruby_section ||= line.match(/^\s*RUBY\s*VERSION\s*$/)
|
217
|
+
next unless in_ruby_section
|
218
|
+
|
219
|
+
# We currently only allow this feature to work with MRI ruby. If jruby
|
220
|
+
# (or something else) is used by the project, it's lock file will have a
|
221
|
+
# line that looks like:
|
222
|
+
# RUBY VERSION
|
223
|
+
# ruby W.X.YpZ (jruby x.x.x.x)
|
224
|
+
# The regex won't match in this situation.
|
225
|
+
result = line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)
|
226
|
+
return result.captures.first.to_f if result
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def reject_mutually_exclusive_defaults
|
231
|
+
disabled_by_default = for_all_cops['DisabledByDefault']
|
232
|
+
enabled_by_default = for_all_cops['EnabledByDefault']
|
233
|
+
return unless disabled_by_default && enabled_by_default
|
234
|
+
|
235
|
+
msg = 'Cops cannot be both enabled by default and disabled by default'
|
236
|
+
raise ValidationError, msg
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -41,12 +41,7 @@ module RuboCop
|
|
41
41
|
def investigate(processed_source)
|
42
42
|
return if processed_source.blank?
|
43
43
|
|
44
|
-
if force_equal_sign_alignment?
|
45
|
-
@asgn_tokens = assignment_tokens
|
46
|
-
@asgn_lines = @asgn_tokens.map(&:line)
|
47
|
-
# Don't attempt to correct the same = more than once
|
48
|
-
@corrected = Set.new
|
49
|
-
end
|
44
|
+
@corrected = Set.new if force_equal_sign_alignment?
|
50
45
|
|
51
46
|
processed_source.tokens.each_cons(2) do |token1, token2|
|
52
47
|
check_tokens(processed_source.ast, token1, token2)
|
@@ -65,24 +60,10 @@ module RuboCop
|
|
65
60
|
|
66
61
|
private
|
67
62
|
|
68
|
-
def assignment_tokens
|
69
|
-
tokens = processed_source.tokens.select(&:equal_sign?)
|
70
|
-
# we don't want to operate on equals signs which are part of an
|
71
|
-
# optarg in a method definition
|
72
|
-
# e.g.: def method(optarg = default_val); end
|
73
|
-
tokens = remove_optarg_equals(tokens, processed_source)
|
74
|
-
|
75
|
-
# Only attempt to align the first = on each line
|
76
|
-
Set.new(tokens.uniq(&:line))
|
77
|
-
end
|
78
|
-
|
79
63
|
def check_tokens(ast, token1, token2)
|
80
64
|
return if token2.type == :tNL
|
81
65
|
|
82
|
-
if force_equal_sign_alignment? &&
|
83
|
-
@asgn_tokens.include?(token2) &&
|
84
|
-
(@asgn_lines.include?(token2.line - 1) ||
|
85
|
-
@asgn_lines.include?(token2.line + 1))
|
66
|
+
if force_equal_sign_alignment? && assignment_tokens.include?(token2)
|
86
67
|
check_assignment(token2)
|
87
68
|
else
|
88
69
|
check_other(token1, token2, ast)
|
@@ -90,24 +71,12 @@ module RuboCop
|
|
90
71
|
end
|
91
72
|
|
92
73
|
def check_assignment(token)
|
93
|
-
|
94
|
-
message = ''
|
95
|
-
if should_aligned_with_preceding_line?(token)
|
96
|
-
assignment_line = processed_source.preceding_line(token)
|
97
|
-
message = format(MSG_UNALIGNED_ASGN, location: 'preceding')
|
98
|
-
else
|
99
|
-
assignment_line = processed_source.following_line(token)
|
100
|
-
message = format(MSG_UNALIGNED_ASGN, location: 'following')
|
101
|
-
end
|
102
|
-
return if aligned_assignment?(token.pos, assignment_line)
|
74
|
+
return unless aligned_with_preceding_assignment(token) == :no
|
103
75
|
|
76
|
+
message = format(MSG_UNALIGNED_ASGN, location: 'preceding')
|
104
77
|
add_offense(token.pos, location: token.pos, message: message)
|
105
78
|
end
|
106
79
|
|
107
|
-
def should_aligned_with_preceding_line?(token)
|
108
|
-
@asgn_lines.include?(token.line - 1)
|
109
|
-
end
|
110
|
-
|
111
80
|
def check_other(token1, token2, ast)
|
112
81
|
return false if allow_for_trailing_comments? &&
|
113
82
|
token2.text.start_with?('#')
|
@@ -188,8 +157,8 @@ module RuboCop
|
|
188
157
|
end
|
189
158
|
|
190
159
|
def align_equal_signs(range, corrector)
|
191
|
-
lines =
|
192
|
-
tokens =
|
160
|
+
lines = all_relevant_assignment_lines(range.line)
|
161
|
+
tokens = assignment_tokens.select { |t| lines.include?(t.line) }
|
193
162
|
|
194
163
|
columns = tokens.map { |t| align_column(t) }
|
195
164
|
align_to = columns.max
|
@@ -209,17 +178,15 @@ module RuboCop
|
|
209
178
|
end
|
210
179
|
end
|
211
180
|
|
212
|
-
def
|
213
|
-
|
214
|
-
|
215
|
-
range.line.downto(1) do |lineno|
|
216
|
-
@asgn_lines.include?(lineno) ? result << lineno : break
|
217
|
-
end
|
218
|
-
range.line.upto(processed_source.lines.size) do |lineno|
|
219
|
-
@asgn_lines.include?(lineno) ? result << lineno : break
|
220
|
-
end
|
181
|
+
def all_relevant_assignment_lines(line_number)
|
182
|
+
last_line_number = processed_source.lines.size
|
221
183
|
|
222
|
-
|
184
|
+
(
|
185
|
+
relevant_assignment_lines(line_number.downto(1)) +
|
186
|
+
relevant_assignment_lines(line_number.upto(last_line_number))
|
187
|
+
)
|
188
|
+
.uniq
|
189
|
+
.sort
|
223
190
|
end
|
224
191
|
|
225
192
|
def align_column(asgn_token)
|
@@ -231,12 +198,6 @@ module RuboCop
|
|
231
198
|
asgn_token.pos.last_column - spaces + 1
|
232
199
|
end
|
233
200
|
|
234
|
-
def remove_optarg_equals(asgn_tokens, processed_source)
|
235
|
-
optargs = processed_source.ast.each_node(:optarg)
|
236
|
-
optarg_eql = optargs.map { |o| o.loc.operator.begin_pos }.to_set
|
237
|
-
asgn_tokens.reject { |t| optarg_eql.include?(t.begin_pos) }
|
238
|
-
end
|
239
|
-
|
240
201
|
def allow_for_trailing_comments?
|
241
202
|
cop_config['AllowBeforeTrailingComments']
|
242
203
|
end
|