rubocop 0.14.1 → 0.15.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.

Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -3
  3. data/CHANGELOG.md +245 -198
  4. data/README.md +7 -0
  5. data/Rakefile +5 -1
  6. data/config/default.yml +27 -4
  7. data/config/enabled.yml +18 -4
  8. data/lib/rubocop.rb +13 -1
  9. data/lib/rubocop/cli.rb +83 -23
  10. data/lib/rubocop/config.rb +1 -1
  11. data/lib/rubocop/cop/cop.rb +31 -6
  12. data/lib/rubocop/cop/lint/block_alignment.rb +11 -8
  13. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +21 -14
  14. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -1
  15. data/lib/rubocop/cop/rails/output.rb +35 -0
  16. data/lib/rubocop/cop/style/{access_control.rb → access_modifier_indentation.rb} +18 -15
  17. data/lib/rubocop/cop/style/alias.rb +14 -2
  18. data/lib/rubocop/cop/style/align_hash.rb +174 -109
  19. data/lib/rubocop/cop/style/autocorrect_alignment.rb +38 -18
  20. data/lib/rubocop/cop/style/blocks.rb +4 -6
  21. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +3 -3
  22. data/lib/rubocop/cop/style/cyclomatic_complexity.rb +46 -0
  23. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +48 -0
  24. data/lib/rubocop/cop/style/empty_lines_around_body.rb +62 -0
  25. data/lib/rubocop/cop/style/end_of_line.rb +6 -2
  26. data/lib/rubocop/cop/style/favor_modifier.rb +11 -1
  27. data/lib/rubocop/cop/style/final_newline.rb +10 -4
  28. data/lib/rubocop/cop/style/hash_syntax.rb +32 -21
  29. data/lib/rubocop/cop/style/leading_comment_space.rb +9 -0
  30. data/lib/rubocop/cop/style/method_call_parentheses.rb +11 -1
  31. data/lib/rubocop/cop/style/numeric_literals.rb +11 -15
  32. data/lib/rubocop/cop/style/redundant_return.rb +7 -4
  33. data/lib/rubocop/cop/style/redundant_self.rb +3 -3
  34. data/lib/rubocop/cop/style/signal_exception.rb +4 -2
  35. data/lib/rubocop/cop/style/space_after_comma_etc.rb +7 -1
  36. data/lib/rubocop/cop/style/space_after_control_keyword.rb +6 -0
  37. data/lib/rubocop/cop/style/space_after_method_name.rb +7 -1
  38. data/lib/rubocop/cop/style/space_after_not.rb +6 -2
  39. data/lib/rubocop/cop/style/space_around_block_braces.rb +149 -0
  40. data/lib/rubocop/cop/style/space_around_equals_in_parameter_default.rb +33 -0
  41. data/lib/rubocop/cop/style/space_around_operators.rb +169 -0
  42. data/lib/rubocop/cop/style/space_before_modifier_keyword.rb +6 -0
  43. data/lib/rubocop/cop/style/space_inside.rb +35 -0
  44. data/lib/rubocop/cop/style/space_inside_brackets.rb +18 -0
  45. data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +99 -0
  46. data/lib/rubocop/cop/style/space_inside_parens.rb +18 -0
  47. data/lib/rubocop/cop/style/special_global_vars.rb +52 -25
  48. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  49. data/lib/rubocop/cop/style/surrounding_space.rb +1 -344
  50. data/lib/rubocop/cop/style/trailing_blank_lines.rb +17 -5
  51. data/lib/rubocop/cop/style/trailing_whitespace.rb +9 -5
  52. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -2
  53. data/lib/rubocop/cop/style/word_array.rb +16 -1
  54. data/lib/rubocop/cop/team.rb +5 -5
  55. data/lib/rubocop/cop/util.rb +1 -0
  56. data/lib/rubocop/formatter/offence_count_formatter.rb +0 -1
  57. data/lib/rubocop/options.rb +76 -111
  58. data/lib/rubocop/rake_task.rb +4 -2
  59. data/lib/rubocop/target_finder.rb +3 -3
  60. data/lib/rubocop/version.rb +1 -1
  61. data/spec/rubocop/cli_spec.rb +123 -13
  62. data/spec/rubocop/config_spec.rb +2 -2
  63. data/spec/rubocop/cop/lint/rescue_exception_spec.rb +10 -0
  64. data/spec/rubocop/cop/lint/useless_setter_call_spec.rb +13 -0
  65. data/spec/rubocop/cop/offence_spec.rb +2 -0
  66. data/spec/rubocop/cop/rails/output_spec.rb +40 -0
  67. data/spec/rubocop/cop/style/access_modifier_indentation_spec.rb +243 -0
  68. data/spec/rubocop/cop/style/alias_spec.rb +8 -0
  69. data/spec/rubocop/cop/style/align_array_spec.rb +12 -0
  70. data/spec/rubocop/cop/style/align_hash_spec.rb +15 -0
  71. data/spec/rubocop/cop/style/braces_around_hash_parameters_spec.rb +7 -4
  72. data/spec/rubocop/cop/style/cyclomatic_complexity_spec.rb +203 -0
  73. data/spec/rubocop/cop/style/empty_lines_around_access_modifier_spec.rb +56 -0
  74. data/spec/rubocop/cop/style/empty_lines_around_body_spec.rb +87 -0
  75. data/spec/rubocop/cop/style/end_of_line_spec.rb +17 -8
  76. data/spec/rubocop/cop/style/favor_modifier_spec.rb +34 -0
  77. data/spec/rubocop/cop/style/final_newline_spec.rb +5 -0
  78. data/spec/rubocop/cop/style/hash_syntax_spec.rb +22 -2
  79. data/spec/rubocop/cop/style/leading_comment_space_spec.rb +5 -0
  80. data/spec/rubocop/cop/style/method_call_parentheses_spec.rb +39 -4
  81. data/spec/rubocop/cop/style/numeric_literals_spec.rb +5 -0
  82. data/spec/rubocop/cop/style/signal_exception_spec.rb +11 -0
  83. data/spec/rubocop/cop/style/space_after_colon_spec.rb +7 -0
  84. data/spec/rubocop/cop/style/space_after_comma_spec.rb +5 -0
  85. data/spec/rubocop/cop/style/space_after_control_keyword_spec.rb +29 -8
  86. data/spec/rubocop/cop/style/space_after_method_name_spec.rb +15 -0
  87. data/spec/rubocop/cop/style/space_after_semicolon_spec.rb +5 -0
  88. data/spec/rubocop/cop/style/space_around_block_braces_spec.rb +68 -0
  89. data/spec/rubocop/cop/style/space_around_equals_in_default_parameter_spec.rb +5 -0
  90. data/spec/rubocop/cop/style/space_around_operators_spec.rb +43 -0
  91. data/spec/rubocop/cop/style/space_before_modifier_keyword_spec.rb +23 -0
  92. data/spec/rubocop/cop/style/space_inside_brackets_spec.rb +7 -0
  93. data/spec/rubocop/cop/style/space_inside_hash_literal_braces_spec.rb +65 -23
  94. data/spec/rubocop/cop/style/space_inside_parens_spec.rb +7 -0
  95. data/spec/rubocop/cop/style/special_global_vars_spec.rb +12 -2
  96. data/spec/rubocop/cop/style/string_literals_spec.rb +6 -0
  97. data/spec/rubocop/cop/style/symbol_array_spec.rb +5 -7
  98. data/spec/rubocop/cop/style/trailing_blank_lines_spec.rb +26 -1
  99. data/spec/rubocop/cop/style/trailing_whitespace_spec.rb +7 -0
  100. data/spec/rubocop/cop/style/trivial_accessors_spec.rb +8 -0
  101. data/spec/rubocop/cop/style/word_array_spec.rb +33 -2
  102. data/spec/rubocop/cop/team_spec.rb +4 -4
  103. data/spec/rubocop/formatter/json_formatter_spec.rb +1 -1
  104. data/spec/rubocop/options_spec.rb +5 -96
  105. data/spec/rubocop/processed_source_spec.rb +3 -3
  106. data/spec/spec_helper.rb +28 -23
  107. data/spec/support/mri_syntax_checker.rb +20 -16
  108. metadata +24 -5
  109. data/spec/rubocop/cop/style/access_control_spec.rb +0 -164
@@ -28,6 +28,12 @@ module Rubocop
28
28
  def elsif?(node)
29
29
  node.loc.keyword.is?('elsif')
30
30
  end
31
+
32
+ def autocorrect(node)
33
+ @corrections << lambda do |corrector|
34
+ corrector.insert_before(node.loc.keyword, ' ')
35
+ end
36
+ end
31
37
  end
32
38
  end
33
39
  end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ # rubocop:disable SymbolName
4
+
5
+ module Rubocop
6
+ module Cop
7
+ module Style
8
+ # Common functionality for checking for spaces inside various
9
+ # kinds of parentheses.
10
+ module SpaceInside
11
+ include SurroundingSpace
12
+ MSG = 'Space inside %s detected.'
13
+
14
+ def investigate(processed_source)
15
+ @processed_source = processed_source
16
+ left, right, kind = specifics
17
+ processed_source.tokens.each_cons(2) do |t1, t2|
18
+ if t1.type == left || t2.type == right
19
+ if t2.pos.line == t1.pos.line && space_between?(t1, t2)
20
+ range = Parser::Source::Range.new(processed_source.buffer,
21
+ t1.pos.end_pos,
22
+ t2.pos.begin_pos)
23
+ convention(range, range, format(MSG, kind))
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def autocorrect(range)
30
+ @corrections << ->(corrector) { corrector.remove(range) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ # rubocop:disable SymbolName
4
+
5
+ module Rubocop
6
+ module Cop
7
+ module Style
8
+ # Checks for spaces inside square brackets.
9
+ class SpaceInsideBrackets < Cop
10
+ include SpaceInside
11
+
12
+ def specifics
13
+ [:tLBRACK, :tRBRACK, 'square brackets']
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+
3
+ # rubocop:disable SymbolName
4
+
5
+ module Rubocop
6
+ module Cop
7
+ module Style
8
+ # Checks that braces used for hash literals have or don't have
9
+ # surrounding space depending on configuration.
10
+ class SpaceInsideHashLiteralBraces < Cop
11
+ include SurroundingSpace
12
+ MSG = 'Space inside %s.'
13
+
14
+ def investigate(processed_source)
15
+ return unless processed_source.ast
16
+ tokens = processed_source.tokens
17
+
18
+ on_node(:hash, processed_source.ast) do |hash|
19
+ b_ix = index_of_first_token(hash)
20
+ if tokens[b_ix].type == :tLBRACE # Hash literal with braces?
21
+ e_ix = index_of_last_token(hash)
22
+ check(tokens[b_ix], tokens[b_ix + 1])
23
+ check(tokens[e_ix - 1], tokens[e_ix]) unless b_ix == e_ix - 1
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def check(t1, t2)
31
+ # No offence if line break inside.
32
+ return if t1.pos.line < t2.pos.line
33
+
34
+ is_empty_braces = t1.text == '{' && t2.text == '}'
35
+ expect_space = if is_empty_braces
36
+ cop_config['EnforcedStyleForEmptyBraces'] == 'space'
37
+ else
38
+ cop_config['EnforcedStyle'] == 'space'
39
+ end
40
+ if offence?(t1, t2, expect_space)
41
+ brace = (t1.text == '{' ? t1 : t2).pos
42
+ range = expect_space ? brace : space_range(brace)
43
+ convention(range, range,
44
+ message(brace, is_empty_braces, expect_space))
45
+ end
46
+ end
47
+
48
+ def offence?(t1, t2, expect_space)
49
+ has_space = space_between?(t1, t2)
50
+ expect_space ? !has_space : has_space
51
+ end
52
+
53
+ def message(brace, is_empty_braces, expect_space)
54
+ inside_what = if is_empty_braces
55
+ 'empty hash literal braces'
56
+ else
57
+ brace.source
58
+ end
59
+ problem = expect_space ? 'missing' : 'detected'
60
+ sprintf(MSG, "#{inside_what} #{problem}")
61
+ end
62
+
63
+ def autocorrect(range)
64
+ @corrections << lambda do |corrector|
65
+ case range.source
66
+ when /\s/ then corrector.remove(range)
67
+ when '{' then corrector.insert_after(range, ' ')
68
+ else corrector.insert_before(range, ' ')
69
+ end
70
+ end
71
+ end
72
+
73
+ def space_range(token_range)
74
+ if token_range.source == '{'
75
+ range_of_space_to_the_right(token_range)
76
+ else
77
+ range_of_space_to_the_left(token_range)
78
+ end
79
+ end
80
+
81
+ def range_of_space_to_the_right(range)
82
+ src = range.source_buffer.source
83
+ end_pos = range.end_pos
84
+ end_pos += 1 while src[end_pos] =~ /[ \t]/
85
+ Parser::Source::Range.new(range.source_buffer,
86
+ range.begin_pos + 1, end_pos)
87
+ end
88
+
89
+ def range_of_space_to_the_left(range)
90
+ src = range.source_buffer.source
91
+ begin_pos = range.begin_pos
92
+ begin_pos -= 1 while src[begin_pos - 1] =~ /[ \t]/
93
+ Parser::Source::Range.new(range.source_buffer, begin_pos,
94
+ range.end_pos - 1)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ # rubocop:disable SymbolName
4
+
5
+ module Rubocop
6
+ module Cop
7
+ module Style
8
+ # Checks for spaces inside ordinary round parentheses.
9
+ class SpaceInsideParens < Cop
10
+ include SpaceInside
11
+
12
+ def specifics
13
+ [:tLPAREN2, :tRPAREN, 'parentheses']
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -5,33 +5,43 @@ module Rubocop
5
5
  module Style
6
6
  # This cop looks for uses of Perl-style global variables.
7
7
  class SpecialGlobalVars < Cop
8
- MSG = 'Prefer %s over %s.'
8
+ MSG_BOTH = 'Prefer %s from the English library, or %s over %s.'
9
+ MSG_ENGLISH = 'Prefer %s from the English library over %s.'
10
+ MSG_REGULAR = 'Prefer %s over %s.'
9
11
 
10
12
  PREFERRED_VARS = {
11
- '$:' => '$LOAD_PATH',
12
- '$"' => '$LOADED_FEATURES',
13
- '$0' => '$PROGRAM_NAME',
14
- '$!' => '$ERROR_INFO from English library',
15
- '$@' => '$ERROR_POSITION from English library',
16
- '$;' => '$FS or $FIELD_SEPARATOR from English library',
17
- '$,' => '$OFS or $OUTPUT_FIELD_SEPARATOR from English library',
18
- '$/' => '$RS or $INPUT_RECORD_SEPARATOR from English library',
19
- '$\\' => '$ORS or $OUTPUT_RECORD_SEPARATOR from English library',
20
- '$.' => '$NR or $INPUT_LINE_NUMBER from English library',
21
- '$_' => '$LAST_READ_LINE from English library',
22
- '$>' => '$DEFAULT_OUTPUT from English library',
23
- '$<' => '$DEFAULT_INPUT from English library',
24
- '$$' => '$PID or $PROCESS_ID from English library',
25
- '$?' => '$CHILD_STATUS from English library',
26
- '$~' => '$LAST_MATCH_INFO from English library',
27
- '$=' => '$IGNORECASE from English library',
28
- '$*' => '$ARGV from English library or ARGV constant',
29
- '$&' => '$MATCH from English library',
30
- '$`' => '$PREMATCH from English library',
31
- '$\'' => '$POSTMATCH from English library',
32
- '$+' => '$LAST_PAREN_MATCH from English library'
13
+ '$:' => ['$LOAD_PATH'],
14
+ '$"' => ['$LOADED_FEATURES'],
15
+ '$0' => ['$PROGRAM_NAME'],
16
+ '$!' => ['$ERROR_INFO'],
17
+ '$@' => ['$ERROR_POSITION'],
18
+ '$;' => ['$FIELD_SEPARATOR', '$FS'],
19
+ '$,' => ['$OUTPUT_FIELD_SEPARATOR', '$OFS'],
20
+ '$/' => ['$INPUT_RECORD_SEPARATOR', '$RS'],
21
+ '$\\' => ['$OUTPUT_RECORD_SEPARATOR', '$ORS'],
22
+ '$.' => ['$INPUT_LINE_NUMBER', '$NR'],
23
+ '$_' => ['$LAST_READ_LINE'],
24
+ '$>' => ['$DEFAULT_OUTPUT'],
25
+ '$<' => ['$DEFAULT_INPUT'],
26
+ '$$' => ['$PROCESS_ID', '$PID'],
27
+ '$?' => ['$CHILD_STATUS'],
28
+ '$~' => ['$LAST_MATCH_INFO'],
29
+ '$=' => ['$IGNORECASE'],
30
+ '$*' => ['$ARGV', 'ARGV'],
31
+ '$&' => ['$MATCH'],
32
+ '$`' => ['$PREMATCH'],
33
+ '$\'' => ['$POSTMATCH'],
34
+ '$+' => ['$LAST_PAREN_MATCH']
33
35
  }.symbolize_keys
34
36
 
37
+ # Anything *not* in this set is provided by the English library.
38
+ NON_ENGLISH_VARS = Set.new([
39
+ '$LOAD_PATH',
40
+ '$LOADED_FEATURES',
41
+ '$PROGRAM_NAME',
42
+ 'ARGV'
43
+ ])
44
+
35
45
  def on_gvar(node)
36
46
  global_var, = *node
37
47
 
@@ -40,7 +50,24 @@ module Rubocop
40
50
 
41
51
  def message(node)
42
52
  global_var, = *node
43
- MSG.format(PREFERRED_VARS[global_var], global_var)
53
+
54
+ regular, english = PREFERRED_VARS[global_var].partition do |var|
55
+ NON_ENGLISH_VARS.include? var
56
+ end
57
+
58
+ # For now, we assume that lists are 2 items or less. Easy grammar!
59
+ regular_msg = regular.join(' or ')
60
+ english_msg = english.join(' or ')
61
+
62
+ if regular.length > 0 && english.length > 0
63
+ MSG_BOTH.format(english_msg, regular_msg, global_var)
64
+ elsif regular.length > 0
65
+ MSG_REGULAR.format(regular_msg, global_var)
66
+ elsif english.length > 0
67
+ MSG_ENGLISH.format(english_msg, global_var)
68
+ else
69
+ fail 'Bug in SpecialGlobalVars - global var w/o preferred vars!'
70
+ end
44
71
  end
45
72
 
46
73
  def autocorrect(node)
@@ -48,7 +75,7 @@ module Rubocop
48
75
  global_var, = *node
49
76
 
50
77
  corrector.replace(node.loc.expression,
51
- PREFERRED_VARS[global_var])
78
+ PREFERRED_VARS[global_var].first)
52
79
  end
53
80
  end
54
81
  end
@@ -21,7 +21,7 @@ module Rubocop
21
21
 
22
22
  def offence?(node)
23
23
  src = node.loc.expression.source
24
- return false if src =~ /^%q/i
24
+ return false if src =~ /^(%q|\?)/i
25
25
  src !~ if single_quotes_preferred?
26
26
  # regex matches IF there is a ' or there is a \\ in the
27
27
  # string that is not preceeded/followed by another \\
@@ -15,7 +15,7 @@ module Rubocop
15
15
  char_preceding_2nd_token =
16
16
  @processed_source[t2.pos.line - 1][t2.pos.column - 2]
17
17
  end
18
- t2.pos.line > t1.pos.line || char_preceding_2nd_token == ' '
18
+ t2.pos.line > t1.pos.line || char_preceding_2nd_token =~ /[ \t]/
19
19
  end
20
20
 
21
21
  def index_of_first_token(node)
@@ -41,349 +41,6 @@ module Rubocop
41
41
  end
42
42
  end
43
43
  end
44
-
45
- # Checks that operators have space around them, except for **
46
- # which should not have surrounding space.
47
- class SpaceAroundOperators < Cop
48
- include SurroundingSpace
49
- MSG_MISSING = "Surrounding space missing for operator '%s'."
50
- MSG_DETECTED = 'Space around operator ** detected.'
51
-
52
- BINARY_OPERATORS =
53
- [:tEQL, :tAMPER2, :tPIPE, :tCARET, :tPLUS, :tMINUS, :tSTAR2,
54
- :tDIVIDE, :tPERCENT, :tEH, :tCOLON, :tANDOP, :tOROP, :tMATCH,
55
- :tNMATCH, :tEQ, :tNEQ, :tGT, :tRSHFT, :tGEQ, :tLT,
56
- :tLSHFT, :tLEQ, :tASSOC, :tEQQ, :tCMP, :tOP_ASGN]
57
-
58
- def investigate(processed_source)
59
- return unless processed_source.ast
60
- @processed_source = processed_source
61
- tokens = processed_source.tokens
62
- tokens.each_cons(3) do |token_before, token, token_after|
63
- next if token_before.type == :kDEF # TODO: remove?
64
- next if token_before.type == :tDOT # Called as method.
65
- next if positions_not_to_check.include?(token.pos)
66
-
67
- case token.type
68
- when :tPOW
69
- if has_space?(token_before, token, token_after)
70
- convention(nil, token.pos, MSG_DETECTED)
71
- end
72
- when *BINARY_OPERATORS
73
- check_missing_space(token_before, token, token_after)
74
- end
75
- end
76
- end
77
-
78
- # Returns an array of positions marking the tokens that this cop
79
- # should not check, either because the token is not an operator
80
- # or because another cop does the check.
81
- def positions_not_to_check
82
- @positions_not_to_check ||= begin
83
- positions = []
84
- positions.concat(do_not_check_block_arg_pipes)
85
- positions.concat(do_not_check_param_default)
86
- positions.concat(do_not_check_class_lshift_self)
87
- positions.concat(do_not_check_def_things)
88
- positions.concat(do_not_check_singleton_operator_defs)
89
- positions
90
- end
91
- end
92
-
93
- def do_not_check_block_arg_pipes
94
- # each { |a| }
95
- # ^ ^
96
- positions = []
97
- on_node(:block, @processed_source.ast) do |b|
98
- on_node(:args, b) do |a|
99
- positions << a.loc.begin << a.loc.end if a.loc.begin
100
- end
101
- end
102
- positions
103
- end
104
-
105
- def do_not_check_param_default
106
- # func(a, b=nil)
107
- # ^
108
- positions = []
109
- tokens = @processed_source.tokens
110
- on_node(:optarg, @processed_source.ast) do |optarg|
111
- _arg, equals, _value = tokens[index_of_first_token(optarg),
112
- 3]
113
- positions << equals.pos
114
- end
115
- positions
116
- end
117
-
118
- def do_not_check_class_lshift_self
119
- # class <<self
120
- # ^
121
- positions = []
122
- tokens = @processed_source.tokens
123
- on_node(:sclass, @processed_source.ast) do |sclass|
124
- ix = index_of_first_token(sclass)
125
- if tokens[ix, 2].map(&:type) == [:kCLASS, :tLSHFT]
126
- positions << tokens[ix + 1].pos
127
- end
128
- end
129
- positions
130
- end
131
-
132
- def do_not_check_def_things
133
- # def +(other)
134
- # ^
135
- positions = []
136
- tokens = @processed_source.tokens
137
- on_node(:def, @processed_source.ast) do |def_node|
138
- # def each &block
139
- # ^
140
- # def each *args
141
- # ^
142
- on_node([:blockarg, :restarg], def_node) do |arg_node|
143
- positions << tokens[index_of_first_token(arg_node)].pos
144
- end
145
- positions << tokens[index_of_first_token(def_node) + 1].pos
146
- end
147
- positions
148
- end
149
-
150
- def do_not_check_singleton_operator_defs
151
- # def self.===(other)
152
- # ^
153
- positions = []
154
- tokens = @processed_source.tokens
155
- on_node(:defs, @processed_source.ast) do |defs_node|
156
- _receiver, name, _args = *defs_node
157
- ix = index_of_first_token(defs_node)
158
- name_token = tokens[ix..-1].find { |t| t.text == name.to_s }
159
- positions << name_token.pos
160
- end
161
- positions
162
- end
163
-
164
- def check_missing_space(token_before, token, token_after)
165
- unless has_space?(token_before, token, token_after)
166
- text = token.text.to_s + (token.type == :tOP_ASGN ? '=' : '')
167
- convention(nil, token.pos, MSG_MISSING.format(text))
168
- end
169
- end
170
-
171
- def has_space?(token_before, token, token_after)
172
- space_between?(token_before, token) && space_between?(token,
173
- token_after)
174
- end
175
- end
176
-
177
- # Checks that block braces have or don't have surrounding space depending
178
- # on configuration. For blocks taking parameters, it checks that the left
179
- # brace has or doesn't have trailing space depending on configuration.
180
- class SpaceAroundBlockBraces < Cop
181
- include SurroundingSpace
182
-
183
- def investigate(processed_source)
184
- return unless processed_source.ast
185
- @processed_source = processed_source
186
-
187
- processed_source.tokens.each_cons(2) do |t1, t2|
188
- next if ([t1.pos, t2.pos] - positions_not_to_check).size < 2
189
-
190
- type1, type2 = t1.type, t2.type
191
- if [:tLCURLY, :tRCURLY].include?(type2)
192
- check(t1, t2)
193
- elsif type1 == :tLCURLY
194
- if type2 == :tPIPE
195
- check_pipe(t1, t2)
196
- else
197
- check(t1, t2)
198
- end
199
- end
200
- end
201
- end
202
-
203
- def positions_not_to_check
204
- @positions_not_to_check ||= begin
205
- positions = []
206
- ast = @processed_source.ast
207
- tokens = @processed_source.tokens
208
-
209
- on_node(:hash, ast) do |hash|
210
- b_ix = index_of_first_token(hash)
211
- e_ix = index_of_last_token(hash)
212
- positions << tokens[b_ix].pos << tokens[e_ix].pos
213
- end
214
-
215
- # TODO: Check braces inside string/symbol/regexp/xstr
216
- # interpolation.
217
- on_node([:dstr, :dsym, :regexp, :xstr], ast) do |s|
218
- b_ix = index_of_first_token(s)
219
- e_ix = index_of_last_token(s)
220
- tokens[b_ix..e_ix].each do |t|
221
- positions << t.pos if t.type == :tRCURLY
222
- end
223
- end
224
-
225
- positions
226
- end
227
- end
228
-
229
- def check(t1, t2)
230
- if cop_config['EnforcedStyle'] == 'space_inside_braces'
231
- check_space_inside_braces(t1, t2)
232
- else
233
- check_no_space_inside_braces(t1, t2)
234
- end
235
- check_space_outside_left_brace(t1, t2)
236
- end
237
-
238
- def check_space_inside_braces(t1, t2)
239
- unless space_between?(t1, t2)
240
- if t1.text == '{'
241
- convention(nil, t1.pos, 'Space missing inside {.')
242
- elsif t2.text == '}'
243
- convention(nil, t2.pos, 'Space missing inside }.')
244
- end
245
- end
246
- end
247
-
248
- def check_no_space_inside_braces(t1, t2)
249
- if t1.text == '{' || t2.text == '}'
250
- if space_between?(t1, t2)
251
- if t1.text == '{'
252
- convention(nil, space_range(t1), 'Space inside { detected.')
253
- elsif t2.text == '}'
254
- convention(nil, space_range(t2), 'Space inside } detected.')
255
- end
256
- end
257
- end
258
- end
259
-
260
- def check_space_outside_left_brace(t1, t2)
261
- if t2.text == '{' && !space_between?(t1, t2)
262
- convention(nil, t2.pos, 'Space missing to the left of {.')
263
- end
264
- end
265
-
266
- def check_pipe(t1, t2)
267
- if cop_config['SpaceBeforeBlockParameters']
268
- unless space_between?(t1, t2)
269
- convention(nil, t1.pos, 'Space between { and | missing.')
270
- end
271
- elsif space_between?(t1, t2)
272
- convention(nil, space_range(t1), 'Space between { and | detected.')
273
- end
274
- end
275
-
276
- def space_range(token)
277
- src = @processed_source.buffer.source
278
- if token.text == '{'
279
- b = token.pos.begin_pos + 1
280
- e = b + 1
281
- e += 1 while src[e] =~ /\s/
282
- else
283
- e = token.pos.begin_pos
284
- b = e - 1
285
- b -= 1 while src[b - 1] =~ /\s/
286
- end
287
- Parser::Source::Range.new(@processed_source.buffer, b, e)
288
- end
289
- end
290
-
291
- # Common functionality for checking for spaces inside various
292
- # kinds of parentheses.
293
- module SpaceInside
294
- include SurroundingSpace
295
- MSG = 'Space inside %s detected.'
296
-
297
- def investigate(processed_source)
298
- @processed_source = processed_source
299
- left, right, kind = specifics
300
- processed_source.tokens.each_cons(2) do |t1, t2|
301
- if t1.type == left || t2.type == right
302
- if t2.pos.line == t1.pos.line && space_between?(t1, t2)
303
- range = Parser::Source::Range.new(processed_source.buffer,
304
- t1.pos.end_pos,
305
- t2.pos.begin_pos)
306
- convention(nil, range, format(MSG, kind))
307
- end
308
- end
309
- end
310
- end
311
- end
312
-
313
- # Checks for spaces inside ordinary round parentheses.
314
- class SpaceInsideParens < Cop
315
- include SpaceInside
316
-
317
- def specifics
318
- [:tLPAREN2, :tRPAREN, 'parentheses']
319
- end
320
- end
321
-
322
- # Checks for spaces inside square brackets.
323
- class SpaceInsideBrackets < Cop
324
- include SpaceInside
325
-
326
- def specifics
327
- [:tLBRACK, :tRBRACK, 'square brackets']
328
- end
329
- end
330
-
331
- # Checks that braces used for hash literals have or don't have
332
- # surrounding space depending on configuration.
333
- class SpaceInsideHashLiteralBraces < Cop
334
- include SurroundingSpace
335
- MSG = 'Space inside hash literal braces %s.'
336
-
337
- def investigate(processed_source)
338
- return unless processed_source.ast
339
- @processed_source = processed_source
340
- tokens = processed_source.tokens
341
-
342
- on_node(:hash, processed_source.ast) do |hash|
343
- b_ix = index_of_first_token(hash)
344
- e_ix = index_of_last_token(hash)
345
- if tokens[b_ix].type == :tLBRACE # Hash literal with braces?
346
- check(tokens[b_ix], tokens[b_ix + 1])
347
- check(tokens[e_ix - 1], tokens[e_ix])
348
- end
349
- end
350
- end
351
-
352
- def check(t1, t2)
353
- types = [t1, t2].map(&:type)
354
- braces = [:tLBRACE, :tRCURLY]
355
- return if types == braces || (braces - types).size == 2
356
- # No offence if line break inside.
357
- return if t1.pos.line < t2.pos.line
358
- has_space = space_between?(t1, t2)
359
- is_offence, word = if cop_config['EnforcedStyleIsWithSpaces']
360
- [!has_space, 'missing']
361
- else
362
- [has_space, 'detected']
363
- end
364
- brace_token = t1.text == '{' ? t1 : t2
365
- convention(nil, brace_token.pos, sprintf(MSG, word)) if is_offence
366
- end
367
- end
368
-
369
- # Checks that the equals signs in parameter default assignments
370
- # have surrounding space.
371
- class SpaceAroundEqualsInParameterDefault < Cop
372
- include SurroundingSpace
373
- MSG = 'Surrounding space missing in default value assignment.'
374
-
375
- def investigate(processed_source)
376
- return unless processed_source.ast
377
- @processed_source = processed_source
378
- on_node(:optarg, processed_source.ast) do |optarg|
379
- index = index_of_first_token(optarg)
380
- arg, equals, value = processed_source.tokens[index, 3]
381
- unless space_between?(arg, equals) && space_between?(equals, value)
382
- convention(nil, equals.pos)
383
- end
384
- end
385
- end
386
- end
387
44
  end
388
45
  end
389
46
  end