rubocop 0.73.0 → 0.74.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/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
|