rubocop 0.40.0 → 0.41.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +7 -1014
- data/config/default.yml +61 -5
- data/config/disabled.yml +6 -0
- data/config/enabled.yml +63 -4
- data/lib/rubocop.rb +17 -1
- data/lib/rubocop/ast_node.rb +56 -42
- data/lib/rubocop/ast_node/traversal.rb +3 -3
- data/lib/rubocop/cli.rb +14 -9
- data/lib/rubocop/comment_config.rb +85 -32
- data/lib/rubocop/config.rb +29 -8
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/corrector.rb +13 -0
- data/lib/rubocop/cop/lint/block_alignment.rb +25 -11
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +5 -2
- data/lib/rubocop/cop/lint/inherit_exception.rb +69 -0
- data/lib/rubocop/cop/lint/percent_string_array.rb +60 -0
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +57 -0
- data/lib/rubocop/cop/lint/shadowed_exception.rb +95 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -13
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +25 -19
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +16 -8
- data/lib/rubocop/cop/mixin/if_node.rb +1 -2
- data/lib/rubocop/cop/mixin/integer_node.rb +13 -0
- data/lib/rubocop/cop/mixin/match_range.rb +26 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +16 -7
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +18 -1
- data/lib/rubocop/cop/mixin/negative_conditional.rb +6 -4
- data/lib/rubocop/cop/mixin/percent_literal.rb +10 -0
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +24 -6
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +20 -7
- data/lib/rubocop/cop/mixin/string_literals_help.rb +2 -2
- data/lib/rubocop/cop/mixin/trailing_comma.rb +34 -20
- data/lib/rubocop/cop/performance/flat_map.rb +23 -10
- data/lib/rubocop/cop/performance/push_splat.rb +47 -0
- data/lib/rubocop/cop/performance/redundant_block_call.rb +24 -1
- data/lib/rubocop/cop/performance/redundant_merge.rb +3 -5
- data/lib/rubocop/cop/performance/sample.rb +15 -11
- data/lib/rubocop/cop/rails/exit.rb +62 -0
- data/lib/rubocop/cop/rails/output_safety.rb +45 -0
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +12 -4
- data/lib/rubocop/cop/rails/request_referer.rb +40 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +63 -28
- data/lib/rubocop/cop/rails/validation.rb +37 -23
- data/lib/rubocop/cop/style/alias.rb +10 -6
- data/lib/rubocop/cop/style/bare_percent_literals.rb +18 -7
- data/lib/rubocop/cop/style/block_delimiters.rb +15 -22
- data/lib/rubocop/cop/style/closing_parenthesis_indentation.rb +19 -8
- data/lib/rubocop/cop/style/comment_indentation.rb +13 -5
- data/lib/rubocop/cop/style/conditional_assignment.rb +111 -59
- data/lib/rubocop/cop/style/documentation.rb +7 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +43 -0
- data/lib/rubocop/cop/style/each_with_object.rb +25 -14
- data/lib/rubocop/cop/style/empty_else.rb +6 -10
- data/lib/rubocop/cop/style/extra_spacing.rb +20 -3
- data/lib/rubocop/cop/style/file_name.rb +16 -4
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +9 -2
- data/lib/rubocop/cop/style/if_unless_modifier.rb +20 -13
- data/lib/rubocop/cop/style/implicit_runtime_error.rb +32 -0
- data/lib/rubocop/cop/style/infinite_loop.rb +42 -5
- data/lib/rubocop/cop/style/lambda.rb +22 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +12 -4
- data/lib/rubocop/cop/style/module_function.rb +28 -6
- data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +49 -12
- data/lib/rubocop/cop/style/mutable_constant.rb +8 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/next.rb +43 -31
- data/lib/rubocop/cop/style/not.rb +33 -13
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +92 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +1 -4
- data/lib/rubocop/cop/style/parallel_assignment.rb +26 -8
- data/lib/rubocop/cop/style/{deprecated_hash_methods.rb → preferred_hash_methods.rb} +8 -8
- data/lib/rubocop/cop/style/redundant_parentheses.rb +29 -19
- data/lib/rubocop/cop/style/redundant_self.rb +13 -6
- data/lib/rubocop/cop/style/space_after_not.rb +7 -5
- data/lib/rubocop/cop/style/space_around_keyword.rb +6 -0
- data/lib/rubocop/cop/style/space_around_operators.rb +5 -1
- data/lib/rubocop/cop/style/space_before_first_arg.rb +21 -9
- data/lib/rubocop/cop/style/space_inside_array_percent_literal.rb +53 -0
- data/lib/rubocop/cop/style/space_inside_block_braces.rb +2 -2
- data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +26 -6
- data/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb +64 -0
- data/lib/rubocop/cop/style/string_literals.rb +37 -8
- data/lib/rubocop/cop/style/symbol_array.rb +21 -12
- data/lib/rubocop/cop/style/symbol_proc.rb +26 -19
- data/lib/rubocop/cop/style/word_array.rb +1 -5
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -6
- data/lib/rubocop/cop/team.rb +40 -27
- data/lib/rubocop/cop/util.rb +13 -42
- data/lib/rubocop/formatter/disabled_config_formatter.rb +37 -14
- data/lib/rubocop/formatter/html_formatter.rb +3 -7
- data/lib/rubocop/result_cache.rb +18 -4
- data/{spec/support → lib/rubocop/rspec}/cop_helper.rb +3 -0
- data/lib/rubocop/rspec/host_environment_simulation_helper.rb +33 -0
- data/lib/rubocop/rspec/shared_contexts.rb +75 -0
- data/lib/rubocop/rspec/shared_examples.rb +101 -0
- data/lib/rubocop/rspec/support.rb +9 -0
- data/lib/rubocop/runner.rb +2 -2
- data/lib/rubocop/string_interpreter.rb +58 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +27 -7
@@ -98,18 +98,13 @@ module RuboCop
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def nil_check(node, else_clause)
|
101
|
-
return unless else_clause && else_clause.
|
102
|
-
add_offense(node,
|
101
|
+
return unless else_clause && else_clause.nil_type?
|
102
|
+
add_offense(node, :else, MSG)
|
103
103
|
end
|
104
104
|
|
105
105
|
def both_check(node, else_clause)
|
106
|
-
|
107
|
-
|
108
|
-
if else_clause.nil?
|
109
|
-
add_offense(node, :else, MSG)
|
110
|
-
elsif else_clause.type == :nil
|
111
|
-
add_offense(node, :else, MSG)
|
112
|
-
end
|
106
|
+
empty_check(node, else_clause)
|
107
|
+
nil_check(node, else_clause)
|
113
108
|
end
|
114
109
|
|
115
110
|
def autocorrect(node)
|
@@ -119,8 +114,9 @@ module RuboCop
|
|
119
114
|
end_pos = if node.loc.end
|
120
115
|
node.loc.end.begin_pos
|
121
116
|
else
|
122
|
-
node.
|
117
|
+
node.parent.loc.end.begin_pos
|
123
118
|
end
|
119
|
+
|
124
120
|
range = Parser::Source::Range.new(node.source_range.source_buffer,
|
125
121
|
node.loc.else.begin_pos,
|
126
122
|
end_pos)
|
@@ -27,8 +27,15 @@ module RuboCop
|
|
27
27
|
MSG_UNALIGNED_ASGN = '`=` is not aligned with the %s assignment.'.freeze
|
28
28
|
|
29
29
|
def investigate(processed_source)
|
30
|
+
return if processed_source.ast.nil?
|
31
|
+
|
30
32
|
if force_equal_sign_alignment?
|
31
33
|
@asgn_tokens = processed_source.tokens.select { |t| equal_sign?(t) }
|
34
|
+
# we don't want to operate on equals signs which are part of an
|
35
|
+
# optarg in a method definition
|
36
|
+
# e.g.: def method(optarg = default_val); end
|
37
|
+
@asgn_tokens = remove_optarg_equals(@asgn_tokens, processed_source)
|
38
|
+
|
32
39
|
# Only attempt to align the first = on each line
|
33
40
|
@asgn_tokens = Set.new(@asgn_tokens.uniq { |t| t.pos.line })
|
34
41
|
@asgn_lines = @asgn_tokens.map { |t| t.pos.line }
|
@@ -94,13 +101,13 @@ module RuboCop
|
|
94
101
|
|
95
102
|
def check_other(t1, t2, ast)
|
96
103
|
return if t1.pos.line != t2.pos.line
|
97
|
-
return if t2.pos.begin_pos - 1 <= t1.pos.end_pos
|
98
104
|
return if allow_for_alignment? && aligned_tok?(t2)
|
99
105
|
|
100
106
|
start_pos = t1.pos.end_pos
|
101
|
-
return if ignored_ranges(ast).find { |r| r.include?(start_pos) }
|
102
|
-
|
103
107
|
end_pos = t2.pos.begin_pos - 1
|
108
|
+
return if end_pos <= start_pos
|
109
|
+
return if ignored_range?(ast, start_pos)
|
110
|
+
|
104
111
|
range = Parser::Source::Range.new(processed_source.buffer,
|
105
112
|
start_pos, end_pos)
|
106
113
|
# Unary + doesn't appear as a token and needs special handling.
|
@@ -117,6 +124,10 @@ module RuboCop
|
|
117
124
|
end
|
118
125
|
end
|
119
126
|
|
127
|
+
def ignored_range?(ast, start_pos)
|
128
|
+
ignored_ranges(ast).any? { |r| r.include?(start_pos) }
|
129
|
+
end
|
130
|
+
|
120
131
|
def unary_plus_non_offense?(range)
|
121
132
|
range.resize(range.size + 1).source =~ /^ ?\+$/
|
122
133
|
end
|
@@ -201,6 +212,12 @@ module RuboCop
|
|
201
212
|
spaces = leading.size - (leading =~ / *\Z/)
|
202
213
|
asgn_token.pos.last_column - spaces + 1
|
203
214
|
end
|
215
|
+
|
216
|
+
def remove_optarg_equals(asgn_tokens, processed_source)
|
217
|
+
optargs = processed_source.ast.each_node(:optarg)
|
218
|
+
optarg_eql = optargs.map { |o| o.loc.operator.begin_pos }.to_set
|
219
|
+
asgn_tokens.reject { |t| optarg_eql.include?(t.pos.begin_pos) }
|
220
|
+
end
|
204
221
|
end
|
205
222
|
end
|
206
223
|
end
|
@@ -125,11 +125,23 @@ module RuboCop
|
|
125
125
|
# We can't assume that the working directory, or any other, is the
|
126
126
|
# "starting point" to build a namespace
|
127
127
|
start = %w(lib spec test src)
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
128
|
+
start_index = nil
|
129
|
+
|
130
|
+
# To find the closest namespace root take the path components, and
|
131
|
+
# then work through them backwards until we find a candidate. This
|
132
|
+
# makes sure we work from the actual root in the case of a path like
|
133
|
+
# /home/user/src/project_name/lib.
|
134
|
+
components.reverse.each_with_index do |c, i|
|
135
|
+
if start.include?(c)
|
136
|
+
start_index = components.size - i
|
137
|
+
break
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if start_index.nil?
|
132
142
|
[to_module_name(components.last)]
|
143
|
+
else
|
144
|
+
components[start_index..-1].map { |c| to_module_name(c) }
|
133
145
|
end
|
134
146
|
end
|
135
147
|
|
@@ -18,7 +18,7 @@ module RuboCop
|
|
18
18
|
|
19
19
|
def investigate(processed_source)
|
20
20
|
return if style == :when_needed && target_ruby_version < 2.3
|
21
|
-
return if processed_source.
|
21
|
+
return if processed_source.tokens.empty?
|
22
22
|
|
23
23
|
return if frozen_string_literal_comment_exists?(processed_source)
|
24
24
|
|
@@ -96,12 +96,19 @@ module RuboCop
|
|
96
96
|
|
97
97
|
return false unless key.sym_type?
|
98
98
|
|
99
|
-
|
99
|
+
acceptable_19_syntax_symbol?(key.source)
|
100
100
|
end
|
101
101
|
|
102
|
-
def
|
102
|
+
def acceptable_19_syntax_symbol?(sym_name)
|
103
103
|
sym_name.sub!(/\A:/, '')
|
104
104
|
|
105
|
+
if cop_config['PreferHashRocketsForNonAlnumEndingSymbols']
|
106
|
+
# Prefer { :production? => false } over { production?: false } and
|
107
|
+
# similarly for other non-alnum final characters (except quotes,
|
108
|
+
# to prefer { "x y": 1 } over { :"x y" => 1 }).
|
109
|
+
return false unless sym_name =~ /[\p{Alnum}"']\z/
|
110
|
+
end
|
111
|
+
|
105
112
|
# Most hash keys can be matched against a simple regex.
|
106
113
|
return true if sym_name =~ /\A[_a-z]\w*[?!]?\z/i
|
107
114
|
|
@@ -52,19 +52,7 @@ module RuboCop
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def autocorrect(node)
|
55
|
-
|
56
|
-
|
57
|
-
oneline =
|
58
|
-
"#{body.source} #{node.loc.keyword.source} " + cond.source
|
59
|
-
first_line_comment = processed_source.comments.find do |c|
|
60
|
-
c.loc.line == node.loc.line
|
61
|
-
end
|
62
|
-
if first_line_comment
|
63
|
-
oneline << ' ' << first_line_comment.loc.expression.source
|
64
|
-
end
|
65
|
-
oneline = "(#{oneline})" if parenthesize?(node)
|
66
|
-
|
67
|
-
->(corrector) { corrector.replace(node.source_range, oneline) }
|
55
|
+
->(corrector) { corrector.replace(node.source_range, oneline(node)) }
|
68
56
|
end
|
69
57
|
|
70
58
|
private
|
@@ -73,6 +61,25 @@ module RuboCop
|
|
73
61
|
def nested_conditional?(node)
|
74
62
|
node.children[1, 2].any? { |child| child && child.type == :if }
|
75
63
|
end
|
64
|
+
|
65
|
+
def oneline(node)
|
66
|
+
cond, body, _else = if_node_parts(node)
|
67
|
+
|
68
|
+
expr = "#{body.source} #{node.loc.keyword.source} " + cond.source
|
69
|
+
if (comment_after = first_line_comment(node))
|
70
|
+
expr << ' ' << comment_after
|
71
|
+
end
|
72
|
+
expr = "(#{expr})" if parenthesize?(node)
|
73
|
+
|
74
|
+
expr
|
75
|
+
end
|
76
|
+
|
77
|
+
def first_line_comment(node)
|
78
|
+
comment =
|
79
|
+
processed_source.comments.find { |c| c.loc.line == node.loc.line }
|
80
|
+
|
81
|
+
comment ? comment.loc.expression.source : nil
|
82
|
+
end
|
76
83
|
end
|
77
84
|
end
|
78
85
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RuboCop
|
5
|
+
module Cop
|
6
|
+
module Style
|
7
|
+
# This cop checks for `raise` or `fail` statements which do not specify an
|
8
|
+
# explicit exception class. (This raises a `RuntimeError`. Some projects
|
9
|
+
# might prefer to use exception classes which more precisely identify the
|
10
|
+
# nature of the error.)
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# @bad
|
14
|
+
# raise 'Error message here'
|
15
|
+
#
|
16
|
+
# @good
|
17
|
+
# raise ArgumentError, 'Error message here'
|
18
|
+
class ImplicitRuntimeError < Cop
|
19
|
+
def_node_matcher :implicit_runtime_error_raise_or_fail,
|
20
|
+
'(send nil ${:raise :fail} {str dstr})'
|
21
|
+
|
22
|
+
def on_send(node)
|
23
|
+
implicit_runtime_error_raise_or_fail(node) do |method|
|
24
|
+
add_offense(node, :expression, "Use `#{method}` with an explicit " \
|
25
|
+
'exception class and message, ' \
|
26
|
+
'rather than just a message.')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -21,7 +21,6 @@ module RuboCop
|
|
21
21
|
|
22
22
|
def on_while(node)
|
23
23
|
condition, = *node
|
24
|
-
|
25
24
|
return unless condition.truthy_literal?
|
26
25
|
|
27
26
|
add_offense(node, :keyword)
|
@@ -29,13 +28,49 @@ module RuboCop
|
|
29
28
|
|
30
29
|
def on_until(node)
|
31
30
|
condition, = *node
|
32
|
-
|
33
31
|
return unless condition.falsey_literal?
|
34
32
|
|
35
33
|
add_offense(node, :keyword)
|
36
34
|
end
|
37
35
|
|
36
|
+
alias on_while_post on_while
|
37
|
+
alias on_until_post on_until
|
38
|
+
|
38
39
|
def autocorrect(node)
|
40
|
+
if node.while_post_type? || node.until_post_type?
|
41
|
+
_, body = *node
|
42
|
+
return lambda do |corrector|
|
43
|
+
corrector.replace(body.loc.begin, 'loop do')
|
44
|
+
corrector.remove(body.loc.end.end.join(node.source_range.end))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if node.modifier_form?
|
49
|
+
range = node.source_range
|
50
|
+
replacement = modifier_replacement(node)
|
51
|
+
else
|
52
|
+
range = non_modifier_range(node)
|
53
|
+
replacement = 'loop do'
|
54
|
+
end
|
55
|
+
|
56
|
+
->(corrector) { corrector.replace(range, replacement) }
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def modifier_replacement(node)
|
62
|
+
_, body = *node
|
63
|
+
if node.single_line?
|
64
|
+
'loop { ' + body.source + ' }'
|
65
|
+
else
|
66
|
+
indentation = body.loc.expression.source_line[/\A(\s*)/]
|
67
|
+
"loop do\n" + indentation +
|
68
|
+
body.source.gsub(/^/, ' ' * configured_indentation_width) +
|
69
|
+
"\n#{indentation}end"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def non_modifier_range(node)
|
39
74
|
condition_node, = *node
|
40
75
|
start_range = node.loc.keyword.begin
|
41
76
|
end_range = if node.loc.begin
|
@@ -43,9 +78,11 @@ module RuboCop
|
|
43
78
|
else
|
44
79
|
condition_node.source_range.end
|
45
80
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
81
|
+
start_range.join(end_range)
|
82
|
+
end
|
83
|
+
|
84
|
+
def configured_indentation_width
|
85
|
+
config.for_cop('IndentationWidth')['Width']
|
49
86
|
end
|
50
87
|
end
|
51
88
|
end
|
@@ -137,6 +137,10 @@ module RuboCop
|
|
137
137
|
|
138
138
|
def autocorrect_literal_to_method(corrector, node)
|
139
139
|
block_method, args = *node
|
140
|
+
|
141
|
+
# Check for unparenthesized args' preceding and trailing whitespaces.
|
142
|
+
remove_unparenthesized_whitespaces(corrector, node)
|
143
|
+
|
140
144
|
# Avoid correcting to `lambdado` by inserting whitespace
|
141
145
|
# if none exists before or after the lambda arguments.
|
142
146
|
if needs_whitespace?(block_method, args, node)
|
@@ -181,6 +185,24 @@ module RuboCop
|
|
181
185
|
index = parent.children.index { |c| c.equal?(node) }
|
182
186
|
index >= 2
|
183
187
|
end
|
188
|
+
|
189
|
+
def remove_unparenthesized_whitespaces(corrector, node)
|
190
|
+
block_method, args = *node
|
191
|
+
return unless unparenthesized_literal_args?(args)
|
192
|
+
# First, remove leading whitespaces (beetween arrow and args)
|
193
|
+
corrector.remove_preceding(
|
194
|
+
args.source_range,
|
195
|
+
args.source_range.begin_pos - block_method.source_range.end_pos
|
196
|
+
)
|
197
|
+
|
198
|
+
# Then, remove trailing whitespaces (beetween args and 'do')
|
199
|
+
delta = node.loc.begin.begin_pos - args.source_range.end_pos - 1
|
200
|
+
corrector.remove_preceding(node.loc.begin, delta)
|
201
|
+
end
|
202
|
+
|
203
|
+
def unparenthesized_literal_args?(args)
|
204
|
+
args.source_range && args.source_range.begin && !parentheses?(args)
|
205
|
+
end
|
184
206
|
end
|
185
207
|
end
|
186
208
|
end
|
@@ -11,10 +11,8 @@ module RuboCop
|
|
11
11
|
include ConfigurableEnforcedStyle
|
12
12
|
|
13
13
|
def on_method_def(node, _method_name, args, _body)
|
14
|
-
if
|
15
|
-
|
16
|
-
args.multiline?)
|
17
|
-
if arguments?(args) && !parentheses?(args)
|
14
|
+
if require_parentheses?(args)
|
15
|
+
if arguments_without_parentheses?(args)
|
18
16
|
missing_parentheses(node, args)
|
19
17
|
else
|
20
18
|
correct_style_detected
|
@@ -46,6 +44,16 @@ module RuboCop
|
|
46
44
|
|
47
45
|
private
|
48
46
|
|
47
|
+
def require_parentheses?(args)
|
48
|
+
style == :require_parentheses ||
|
49
|
+
(style == :require_no_parentheses_except_multiline &&
|
50
|
+
args.multiline?)
|
51
|
+
end
|
52
|
+
|
53
|
+
def arguments_without_parentheses?(args)
|
54
|
+
arguments?(args) && !parentheses?(args)
|
55
|
+
end
|
56
|
+
|
49
57
|
def missing_parentheses(node, args)
|
50
58
|
add_offense(node, args.source_range,
|
51
59
|
'Use def with parentheses when there are parameters.') do
|
@@ -4,26 +4,48 @@
|
|
4
4
|
module RuboCop
|
5
5
|
module Cop
|
6
6
|
module Style
|
7
|
-
# This cops checks for use of `extend self` in a
|
7
|
+
# This cops checks for use of `extend self` or `module_function` in a
|
8
|
+
# module.
|
9
|
+
#
|
10
|
+
# Supported styles are: module_function, extend_self.
|
8
11
|
#
|
9
12
|
# @example
|
10
13
|
#
|
14
|
+
# # Good if EnforcedStyle is module_function
|
11
15
|
# module Test
|
12
|
-
#
|
16
|
+
# module_function
|
17
|
+
# ...
|
18
|
+
# end
|
13
19
|
#
|
20
|
+
# # Good if EnforcedStyle is extend_self
|
21
|
+
# module Test
|
22
|
+
# extend self
|
14
23
|
# ...
|
15
|
-
#
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# These offenses are not auto-corrected since there are different
|
27
|
+
# implications to each approach.
|
16
28
|
class ModuleFunction < Cop
|
17
|
-
|
29
|
+
include ConfigurableEnforcedStyle
|
30
|
+
|
31
|
+
MODULE_FUNCTION_MSG = 'Use `module_function` instead of `extend self`.'
|
32
|
+
.freeze
|
33
|
+
EXTEND_SELF_MSG = 'Use `extend self` instead of `module_function`.'
|
34
|
+
.freeze
|
18
35
|
|
19
|
-
|
36
|
+
MODULE_FUNCTION_NODE = s(:send, nil, :module_function)
|
37
|
+
EXTEND_SELF_NODE = s(:send, nil, :extend, s(:self))
|
20
38
|
|
21
39
|
def on_module(node)
|
22
40
|
_name, body = *node
|
23
41
|
return unless body && body.type == :begin
|
24
42
|
|
25
43
|
body.children.each do |body_node|
|
26
|
-
|
44
|
+
if style == :module_function && body_node == EXTEND_SELF_NODE
|
45
|
+
add_offense(body_node, :expression, MODULE_FUNCTION_MSG)
|
46
|
+
elsif style == :extend_self && body_node == MODULE_FUNCTION_NODE
|
47
|
+
add_offense(body_node, :expression, EXTEND_SELF_MSG)
|
48
|
+
end
|
27
49
|
end
|
28
50
|
end
|
29
51
|
end
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
57
57
|
|
58
58
|
@base = alignment_base(node, rhs, given_style)
|
59
59
|
correct_column = if @base
|
60
|
-
@base.column
|
60
|
+
@base.column + extra_indentation(given_style)
|
61
61
|
else
|
62
62
|
indentation(lhs) + correct_indentation(node)
|
63
63
|
end
|
@@ -65,20 +65,39 @@ module RuboCop
|
|
65
65
|
rhs if @column_delta != 0
|
66
66
|
end
|
67
67
|
|
68
|
+
def extra_indentation(given_style)
|
69
|
+
if given_style == :indented_relative_to_receiver
|
70
|
+
configured_indentation_width
|
71
|
+
else
|
72
|
+
0
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
68
76
|
def message(node, lhs, rhs)
|
69
|
-
what = operation_description(node, rhs)
|
70
77
|
if @base
|
71
|
-
|
78
|
+
base_source = @base.source[/[^\n]*/]
|
79
|
+
if style == :indented_relative_to_receiver
|
80
|
+
"Indent `#{rhs.source}` #{configured_indentation_width} spaces " \
|
81
|
+
"more than `#{base_source}` on line #{@base.line}."
|
82
|
+
else
|
83
|
+
"Align `#{rhs.source}` with `#{base_source}` on " \
|
72
84
|
"line #{@base.line}."
|
85
|
+
end
|
73
86
|
else
|
74
87
|
used_indentation = rhs.column - indentation(lhs)
|
88
|
+
what = operation_description(node, rhs)
|
75
89
|
"Use #{correct_indentation(node)} (not #{used_indentation}) " \
|
76
90
|
"spaces for indenting #{what} spanning multiple lines."
|
77
91
|
end
|
78
92
|
end
|
79
93
|
|
80
94
|
def alignment_base(node, rhs, given_style)
|
81
|
-
return nil
|
95
|
+
return nil if given_style == :indented
|
96
|
+
|
97
|
+
if given_style == :indented_relative_to_receiver
|
98
|
+
receiver_base = receiver_alignment_base(node)
|
99
|
+
return receiver_base if receiver_base
|
100
|
+
end
|
82
101
|
|
83
102
|
semantic_alignment_base(node, rhs) ||
|
84
103
|
syntactic_alignment_base(node, rhs)
|
@@ -111,17 +130,35 @@ module RuboCop
|
|
111
130
|
# a.b
|
112
131
|
# .c
|
113
132
|
def semantic_alignment_base(node, rhs)
|
114
|
-
return
|
115
|
-
|
133
|
+
return unless rhs.source.start_with?('.')
|
134
|
+
|
135
|
+
node = semantic_alignment_node(node)
|
136
|
+
return unless node
|
137
|
+
|
138
|
+
node.loc.dot.join(node.loc.selector)
|
139
|
+
end
|
140
|
+
|
141
|
+
# a
|
142
|
+
# .b
|
143
|
+
# .c
|
144
|
+
def receiver_alignment_base(node)
|
145
|
+
node = semantic_alignment_node(node)
|
146
|
+
return unless node
|
147
|
+
|
148
|
+
node.receiver.source_range
|
149
|
+
end
|
116
150
|
|
117
|
-
|
118
|
-
|
119
|
-
return nil unless node.parent.send_type?
|
151
|
+
def semantic_alignment_node(node)
|
152
|
+
return if argument_in_method_call(node)
|
120
153
|
|
121
|
-
|
122
|
-
|
154
|
+
# descend to root of method chain
|
155
|
+
node = node.receiver while node.receiver
|
156
|
+
# ascend to first call which has a dot
|
157
|
+
node = node.parent
|
158
|
+
node = node.parent until node.loc.dot
|
123
159
|
|
124
|
-
|
160
|
+
return if node.loc.dot.line != node.loc.line
|
161
|
+
node
|
125
162
|
end
|
126
163
|
|
127
164
|
def operation_rhs(node)
|