rubocop 0.4.0 → 0.8.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 (190) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +50 -0
  3. data/.rubocop.yml +5 -127
  4. data/.travis.yml +7 -1
  5. data/CHANGELOG.md +157 -0
  6. data/CONTRIBUTING.md +13 -6
  7. data/Gemfile +3 -8
  8. data/README.md +160 -9
  9. data/Rakefile +3 -17
  10. data/bin/rubocop +16 -10
  11. data/config/default.yml +46 -0
  12. data/config/disabled.yml +5 -0
  13. data/config/enabled.yml +322 -0
  14. data/lib/rubocop/cli.rb +248 -93
  15. data/lib/rubocop/config.rb +205 -0
  16. data/lib/rubocop/config_store.rb +37 -0
  17. data/lib/rubocop/cop/access_control.rb +41 -0
  18. data/lib/rubocop/cop/alias.rb +17 -0
  19. data/lib/rubocop/cop/align_parameters.rb +20 -95
  20. data/lib/rubocop/cop/and_or.rb +26 -0
  21. data/lib/rubocop/cop/ascii_comments.rb +13 -0
  22. data/lib/rubocop/cop/ascii_identifiers.rb +19 -0
  23. data/lib/rubocop/cop/avoid_class_vars.rb +15 -0
  24. data/lib/rubocop/cop/avoid_for.rb +17 -0
  25. data/lib/rubocop/cop/avoid_global_vars.rb +61 -0
  26. data/lib/rubocop/cop/avoid_perl_backrefs.rb +17 -0
  27. data/lib/rubocop/cop/avoid_perlisms.rb +47 -0
  28. data/lib/rubocop/cop/block_comments.rb +15 -0
  29. data/lib/rubocop/cop/blocks.rb +11 -47
  30. data/lib/rubocop/cop/case_indentation.rb +22 -0
  31. data/lib/rubocop/cop/class_and_module_camel_case.rb +20 -11
  32. data/lib/rubocop/cop/class_methods.rb +15 -0
  33. data/lib/rubocop/cop/collection_methods.rb +16 -16
  34. data/lib/rubocop/cop/colon_method_call.rb +20 -0
  35. data/lib/rubocop/cop/constant_name.rb +24 -0
  36. data/lib/rubocop/cop/cop.rb +34 -47
  37. data/lib/rubocop/cop/def_parentheses.rb +43 -35
  38. data/lib/rubocop/cop/empty_line_between_defs.rb +22 -0
  39. data/lib/rubocop/cop/empty_lines.rb +21 -13
  40. data/lib/rubocop/cop/empty_literal.rb +47 -0
  41. data/lib/rubocop/cop/encoding.rb +3 -3
  42. data/lib/rubocop/cop/end_of_line.rb +3 -3
  43. data/lib/rubocop/cop/ensure_return.rb +19 -0
  44. data/lib/rubocop/cop/eval.rb +19 -0
  45. data/lib/rubocop/cop/favor_join.rb +22 -0
  46. data/lib/rubocop/cop/favor_modifier.rb +38 -48
  47. data/lib/rubocop/cop/favor_percent_r.rb +19 -0
  48. data/lib/rubocop/cop/favor_sprintf.rb +21 -0
  49. data/lib/rubocop/cop/favor_unless_over_negated_if.rb +19 -17
  50. data/lib/rubocop/cop/handle_exceptions.rb +17 -0
  51. data/lib/rubocop/cop/hash_syntax.rb +29 -14
  52. data/lib/rubocop/cop/if_then_else.rb +32 -29
  53. data/lib/rubocop/cop/leading_comment_space.rb +17 -0
  54. data/lib/rubocop/cop/line_continuation.rb +15 -0
  55. data/lib/rubocop/cop/line_length.rb +4 -4
  56. data/lib/rubocop/cop/loop.rb +33 -0
  57. data/lib/rubocop/cop/method_and_variable_snake_case.rb +41 -17
  58. data/lib/rubocop/cop/method_length.rb +52 -0
  59. data/lib/rubocop/cop/new_lambda_literal.rb +8 -6
  60. data/lib/rubocop/cop/not.rb +21 -0
  61. data/lib/rubocop/cop/numeric_literals.rb +9 -7
  62. data/lib/rubocop/cop/offence.rb +12 -1
  63. data/lib/rubocop/cop/op_method.rb +26 -0
  64. data/lib/rubocop/cop/parameter_lists.rb +12 -6
  65. data/lib/rubocop/cop/parentheses_around_condition.rb +11 -11
  66. data/lib/rubocop/cop/percent_r.rb +19 -0
  67. data/lib/rubocop/cop/reduce_arguments.rb +29 -0
  68. data/lib/rubocop/cop/rescue_exception.rb +26 -0
  69. data/lib/rubocop/cop/rescue_modifier.rb +17 -0
  70. data/lib/rubocop/cop/semicolon.rb +31 -0
  71. data/lib/rubocop/cop/single_line_methods.rb +44 -0
  72. data/lib/rubocop/cop/space_after_comma_etc.rb +30 -10
  73. data/lib/rubocop/cop/space_after_control_keyword.rb +29 -0
  74. data/lib/rubocop/cop/string_literals.rb +9 -23
  75. data/lib/rubocop/cop/surrounding_space.rb +223 -83
  76. data/lib/rubocop/cop/symbol_array.rb +31 -0
  77. data/lib/rubocop/cop/symbol_name.rb +23 -0
  78. data/lib/rubocop/cop/syntax.rb +35 -5
  79. data/lib/rubocop/cop/tab.rb +3 -3
  80. data/lib/rubocop/cop/ternary_operator.rb +26 -24
  81. data/lib/rubocop/cop/trailing_whitespace.rb +3 -5
  82. data/lib/rubocop/cop/trivial_accessors.rb +26 -0
  83. data/lib/rubocop/cop/unless_else.rb +11 -7
  84. data/lib/rubocop/cop/util.rb +26 -0
  85. data/lib/rubocop/cop/variable_interpolation.rb +29 -0
  86. data/lib/rubocop/cop/when_then.rb +6 -14
  87. data/lib/rubocop/cop/word_array.rb +37 -0
  88. data/lib/rubocop/report/emacs_style.rb +2 -2
  89. data/lib/rubocop/report/plain_text.rb +1 -1
  90. data/lib/rubocop/version.rb +3 -1
  91. data/lib/rubocop.rb +48 -8
  92. data/rubocop.gemspec +32 -151
  93. data/spec/project_spec.rb +27 -0
  94. data/spec/rubocop/cli_spec.rb +573 -200
  95. data/spec/rubocop/config_spec.rb +409 -0
  96. data/spec/rubocop/config_store_spec.rb +66 -0
  97. data/spec/rubocop/cops/access_control_spec.rb +129 -0
  98. data/spec/rubocop/cops/alias_spec.rb +39 -0
  99. data/spec/rubocop/cops/align_parameters_spec.rb +66 -70
  100. data/spec/rubocop/cops/and_or_spec.rb +37 -0
  101. data/spec/rubocop/cops/ascii_comments_spec.rb +26 -0
  102. data/spec/rubocop/cops/ascii_identifiers_spec.rb +26 -0
  103. data/spec/rubocop/cops/avoid_class_vars_spec.rb +25 -0
  104. data/spec/rubocop/cops/avoid_for_spec.rb +35 -0
  105. data/spec/rubocop/cops/avoid_global_vars_spec.rb +32 -0
  106. data/spec/rubocop/cops/avoid_perl_backrefs_spec.rb +18 -0
  107. data/spec/rubocop/cops/avoid_perlisms_spec.rb +44 -0
  108. data/spec/rubocop/cops/block_comments_spec.rb +25 -0
  109. data/spec/rubocop/cops/blocks_spec.rb +33 -0
  110. data/spec/rubocop/cops/{indentation_spec.rb → case_indentation_spec.rb} +7 -7
  111. data/spec/rubocop/cops/class_and_module_camel_case_spec.rb +15 -5
  112. data/spec/rubocop/cops/class_methods_spec.rb +49 -0
  113. data/spec/rubocop/cops/collection_methods_spec.rb +9 -4
  114. data/spec/rubocop/cops/colon_method_call_spec.rb +53 -0
  115. data/spec/rubocop/cops/constant_name_spec.rb +42 -0
  116. data/spec/rubocop/cops/def_with_parentheses_spec.rb +13 -8
  117. data/spec/rubocop/cops/def_without_parentheses_spec.rb +11 -5
  118. data/spec/rubocop/cops/empty_line_between_defs_spec.rb +83 -0
  119. data/spec/rubocop/cops/empty_lines_spec.rb +14 -59
  120. data/spec/rubocop/cops/empty_literal_spec.rb +90 -0
  121. data/spec/rubocop/cops/encoding_spec.rb +11 -11
  122. data/spec/rubocop/cops/end_of_line_spec.rb +2 -2
  123. data/spec/rubocop/cops/ensure_return_spec.rb +35 -0
  124. data/spec/rubocop/cops/eval_spec.rb +39 -0
  125. data/spec/rubocop/cops/favor_join_spec.rb +35 -0
  126. data/spec/rubocop/cops/favor_modifier_spec.rb +16 -14
  127. data/spec/rubocop/cops/favor_percent_r_spec.rb +29 -0
  128. data/spec/rubocop/cops/favor_sprintf_spec.rb +51 -0
  129. data/spec/rubocop/cops/favor_unless_over_negated_if_spec.rb +4 -4
  130. data/spec/rubocop/cops/favor_until_over_negated_while_spec.rb +3 -3
  131. data/spec/rubocop/cops/handle_exceptions_spec.rb +34 -0
  132. data/spec/rubocop/cops/hash_syntax_spec.rb +11 -6
  133. data/spec/rubocop/cops/if_with_semicolon_spec.rb +7 -1
  134. data/spec/rubocop/cops/leading_comment_space_spec.rb +54 -0
  135. data/spec/rubocop/cops/line_continuation_spec.rb +24 -0
  136. data/spec/rubocop/cops/line_length_spec.rb +3 -2
  137. data/spec/rubocop/cops/loop_spec.rb +31 -0
  138. data/spec/rubocop/cops/method_and_variable_snake_case_spec.rb +55 -9
  139. data/spec/rubocop/cops/method_length_spec.rb +147 -0
  140. data/spec/rubocop/cops/multiline_if_then_spec.rb +15 -15
  141. data/spec/rubocop/cops/new_lambda_literal_spec.rb +5 -6
  142. data/spec/rubocop/cops/not_spec.rb +31 -0
  143. data/spec/rubocop/cops/numeric_literals_spec.rb +13 -13
  144. data/spec/rubocop/cops/offence_spec.rb +13 -0
  145. data/spec/rubocop/cops/one_line_conditional_spec.rb +1 -1
  146. data/spec/rubocop/cops/op_method_spec.rb +78 -0
  147. data/spec/rubocop/cops/parameter_lists_spec.rb +7 -7
  148. data/spec/rubocop/cops/parentheses_around_condition_spec.rb +41 -44
  149. data/spec/rubocop/cops/percent_r_spec.rb +29 -0
  150. data/spec/rubocop/cops/reduce_arguments_spec.rb +57 -0
  151. data/spec/rubocop/cops/rescue_exception_spec.rb +125 -0
  152. data/spec/rubocop/cops/rescue_modifier_spec.rb +37 -0
  153. data/spec/rubocop/cops/semicolon_spec.rb +88 -0
  154. data/spec/rubocop/cops/single_line_methods_spec.rb +50 -0
  155. data/spec/rubocop/cops/space_after_colon_spec.rb +3 -3
  156. data/spec/rubocop/cops/space_after_comma_spec.rb +14 -2
  157. data/spec/rubocop/cops/space_after_control_keyword_spec.rb +67 -0
  158. data/spec/rubocop/cops/space_after_semicolon_spec.rb +6 -1
  159. data/spec/rubocop/cops/space_around_braces_spec.rb +18 -3
  160. data/spec/rubocop/cops/space_around_equals_in_default_parameter_spec.rb +12 -2
  161. data/spec/rubocop/cops/space_around_operators_spec.rb +88 -26
  162. data/spec/rubocop/cops/space_inside_brackets_spec.rb +13 -7
  163. data/spec/rubocop/cops/space_inside_hash_literal_braces_spec.rb +79 -0
  164. data/spec/rubocop/cops/space_inside_parens_spec.rb +7 -3
  165. data/spec/rubocop/cops/string_literals_spec.rb +21 -6
  166. data/spec/rubocop/cops/symbol_array_spec.rb +41 -0
  167. data/spec/rubocop/cops/symbol_name_spec.rb +119 -0
  168. data/spec/rubocop/cops/syntax_spec.rb +28 -5
  169. data/spec/rubocop/cops/tab_spec.rb +2 -2
  170. data/spec/rubocop/cops/ternary_operator_spec.rb +13 -17
  171. data/spec/rubocop/cops/trailing_whitespace_spec.rb +3 -3
  172. data/spec/rubocop/cops/trivial_accessors_spec.rb +329 -0
  173. data/spec/rubocop/cops/unless_else_spec.rb +8 -8
  174. data/spec/rubocop/cops/variable_interpolation_spec.rb +49 -0
  175. data/spec/rubocop/cops/when_then_spec.rb +14 -14
  176. data/spec/rubocop/cops/word_array_spec.rb +47 -0
  177. data/spec/spec_helper.rb +30 -9
  178. data/spec/support/file_helper.rb +21 -0
  179. data/spec/support/isolated_environment.rb +27 -0
  180. metadata +235 -76
  181. data/.document +0 -5
  182. data/Gemfile.lock +0 -41
  183. data/VERSION +0 -1
  184. data/lib/rubocop/cop/ampersands_pipes_vs_and_or.rb +0 -25
  185. data/lib/rubocop/cop/grammar.rb +0 -135
  186. data/lib/rubocop/cop/indentation.rb +0 -44
  187. data/spec/rubocop/cops/ampersands_pipes_vs_and_or_spec.rb +0 -57
  188. data/spec/rubocop/cops/grammar_spec.rb +0 -71
  189. data/spec/rubocop/cops/multiline_blocks_spec.rb +0 -24
  190. data/spec/rubocop/cops/single_line_blocks_spec.rb +0 -22
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class SingleLineMethods < Cop
6
+ MSG = 'Avoid single-line method definitions.'
7
+
8
+ def allow_empty?
9
+ SingleLineMethods.config['AllowIfMethodIsEmpty']
10
+ end
11
+
12
+ def on_def(node)
13
+ check(node)
14
+
15
+ super
16
+ end
17
+
18
+ def on_defs(node)
19
+ check(node)
20
+
21
+ super
22
+ end
23
+
24
+ private
25
+
26
+ def check(node)
27
+ start_line = node.loc.keyword.line
28
+ end_line = node.loc.end.line
29
+
30
+ if node.type == :def
31
+ empty_body = node.children[2].type == :nil
32
+ else
33
+ empty_body = node.children[3].type == :nil
34
+ end
35
+
36
+ if start_line == end_line && !(allow_empty? && empty_body)
37
+ add_offence(:convention,
38
+ start_line,
39
+ MSG)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,40 +1,60 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # rubocop:disable SymbolName
4
+
3
5
  module Rubocop
4
6
  module Cop
5
7
  module SpaceAfterCommaEtc
6
- ERROR_MESSAGE = 'Space missing after %s.'
8
+ MSG = 'Space missing after %s.'
7
9
 
8
- def inspect(file, source, tokens, sexp)
9
- tokens.each_index do |ix|
10
- t = tokens[ix]
11
- if kind(t) && !whitespace?(tokens[ix + 1])
12
- add_offence(:convention, t.pos.lineno, ERROR_MESSAGE % kind(t))
10
+ def inspect(source, tokens, ast, comments)
11
+ tokens.each_cons(2) do |t1, t2|
12
+ if kind(t1) && t1.pos.line == t2.pos.line &&
13
+ t2.pos.column == t1.pos.column + offset(t1)
14
+ add_offence(:convention, t1.pos.line, sprintf(MSG, kind(t1)))
13
15
  end
14
16
  end
15
17
  end
18
+
19
+ # The normal offset, i.e., the distance from the punctuation
20
+ # token where a space should be, is 1.
21
+ def offset(token)
22
+ 1
23
+ end
16
24
  end
17
25
 
18
26
  class SpaceAfterComma < Cop
19
27
  include SpaceAfterCommaEtc
28
+
20
29
  def kind(token)
21
- 'comma' if token.type == :on_comma
30
+ 'comma' if token.type == :tCOMMA
22
31
  end
23
32
  end
24
33
 
25
34
  class SpaceAfterSemicolon < Cop
26
35
  include SpaceAfterCommaEtc
36
+
27
37
  def kind(token)
28
- 'semicolon' if token.type == :on_semicolon
38
+ 'semicolon' if token.type == :tSEMI
29
39
  end
30
40
  end
31
41
 
32
42
  class SpaceAfterColon < Cop
33
43
  include SpaceAfterCommaEtc
44
+
45
+ # The colon following a label will not appear in the token
46
+ # array. Instad we get a tLABEL token, whose length we use to
47
+ # calculate where we expect a space.
48
+ def offset(token)
49
+ case token.type
50
+ when :tLABEL then token.text.length + 1
51
+ when :tCOLON then 1
52
+ end
53
+ end
54
+
34
55
  def kind(token)
35
56
  case token.type
36
- when :on_label then 'colon'
37
- when :on_op then 'colon' if token.text == ':'
57
+ when :tLABEL, :tCOLON then 'colon'
38
58
  end
39
59
  end
40
60
  end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class SpaceAfterControlKeyword < Cop
6
+ MSG = 'Use space after control keywords.'
7
+ # elsif and unless are handled by on_if.
8
+ KEYWORDS = %w(if case when while until)
9
+
10
+ def on_keyword(node)
11
+ return if node.loc.is_a?(Parser::Source::Map::Ternary)
12
+
13
+ exp = node.loc.expression
14
+ kw = node.loc.keyword
15
+ kw_offset = kw.begin_pos - exp.begin_pos
16
+ if exp.source[kw_offset..-1].start_with?(kw.source + '(')
17
+ add_offence(:convention, kw.line, MSG)
18
+ end
19
+ end
20
+
21
+ KEYWORDS.each do |keyword|
22
+ define_method(:"on_#{keyword}") do |node|
23
+ on_keyword(node)
24
+ super(node)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -3,32 +3,18 @@
3
3
  module Rubocop
4
4
  module Cop
5
5
  class StringLiterals < Cop
6
- ERROR_MESSAGE = "Prefer single-quoted strings when you don't need " +
6
+ MSG = "Prefer single-quoted strings when you don't need " +
7
7
  'string interpolation or special symbols.'
8
8
 
9
- def inspect(file, source, tokens, sexp)
10
- state = :outside
11
- tokens.each do |t|
12
- state = case [state, t.type]
13
- when [:outside, :on_tstring_beg]
14
- :double_quote if t.text == '"'
9
+ def inspect(source, tokens, ast, comments)
10
+ on_node(:str, ast, :dstr) do |s|
11
+ text = s.to_a[0]
15
12
 
16
- when [:double_quote, :on_tstring_content]
17
- :valid_double_quote if t.text =~ /'|\\[ntrx]/
18
-
19
- when [:double_quote, :on_embexpr_beg]
20
- :embedded_expression
21
-
22
- when [:double_quote, :on_tstring_end]
23
- add_offence(:convention, t.pos.lineno, ERROR_MESSAGE)
24
- :outside
25
-
26
- when [:embedded_expression, :on_rbrace]
27
- :valid_double_quote
28
-
29
- when [:valid_double_quote, :on_tstring_end]
30
- :outside
31
- end || state
13
+ if text !~ /['\n\t\r]/ && s.loc.expression.source[0] == '"'
14
+ add_offence(:convention,
15
+ s.loc.line,
16
+ MSG)
17
+ end
32
18
  end
33
19
  end
34
20
  end
@@ -1,140 +1,280 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # rubocop:disable SymbolName
4
+
3
5
  module Rubocop
4
6
  module Cop
5
7
  module SurroundingSpace
6
- def inspect(file, source, tokens, sexp)
7
- @correlations.sort.each do |ix, grammar_path|
8
- check_missing_space(tokens, ix, grammar_path)
8
+ def space_between?(t1, t2)
9
+ char_preceding_2nd_token =
10
+ @source[t2.pos.line - 1][t2.pos.column - 1]
11
+ if char_preceding_2nd_token == '+' && t1.type != :tPLUS
12
+ # Special case. A unary plus is not present in the tokens.
13
+ char_preceding_2nd_token =
14
+ @source[t2.pos.line - 1][t2.pos.column - 2]
9
15
  end
10
- tokens.each_index { |ix| check_unwanted_space(tokens, ix) }
11
- end
12
-
13
- private
14
-
15
- def previous_non_space(tokens, ix)
16
- tokens[0...ix].reverse.find { |t| not whitespace?(t) }
16
+ t2.pos.line > t1.pos.line || char_preceding_2nd_token == ' '
17
17
  end
18
18
 
19
- def ok_without_spaces?(grammar_path)
20
- parent, child = grammar_path.values_at(-2, -1)
21
- return true if [:unary, :symbol, :defs, :def, :call].include?(parent)
22
- return true if [:**, :block_var].include?(child)
23
- parent == :command_call && child == :'::'
19
+ def index_of_first_token(node, tokens)
20
+ @token_table ||= build_token_table(tokens)
21
+ b = node.loc.expression.begin
22
+ @token_table[[b.line, b.column]]
24
23
  end
25
24
 
26
- def surrounded_by_whitespace?(nearby_tokens)
27
- left, _, right = nearby_tokens
28
- whitespace?(left) && whitespace?(right)
25
+ def index_of_last_token(node, tokens)
26
+ @token_table ||= build_token_table(tokens)
27
+ e = node.loc.expression.end
28
+ (0...e.column).to_a.reverse.find do |c|
29
+ ix = @token_table[[e.line, c]]
30
+ return ix if ix
31
+ end
29
32
  end
30
33
 
31
- # Default implementation for classes that don't need it.
32
- def check_missing_space(tokens, ix, grammar_path)
34
+ def build_token_table(tokens)
35
+ table = {}
36
+ tokens.each_with_index do |t, ix|
37
+ table[[t.pos.line, t.pos.column]] = ix
38
+ end
39
+ table
33
40
  end
34
41
  end
35
42
 
36
43
  class SpaceAroundOperators < Cop
37
44
  include SurroundingSpace
38
- ERROR_MESSAGE = 'Surrounding space missing for operator '
39
-
40
- def check_missing_space(tokens, ix, grammar_path)
41
- t = tokens[ix]
42
- if t.type == :on_op
43
- unless surrounded_by_whitespace?(tokens[ix - 1, 3])
44
- unless ok_without_spaces?(grammar_path)
45
- add_offence(:convention, t.pos.lineno,
46
- ERROR_MESSAGE + "'#{t.text}'.")
45
+ MSG_MISSING = "Surrounding space missing for operator '%s'."
46
+ MSG_DETECTED = 'Space around operator ** detected.'
47
+
48
+ BINARY_OPERATORS =
49
+ [:tEQL, :tAMPER2, :tPIPE, :tCARET, :tPLUS, :tMINUS, :tSTAR2,
50
+ :tDIVIDE, :tPERCENT, :tEH, :tCOLON, :tANDOP, :tOROP, :tMATCH,
51
+ :tNMATCH, :tEQ, :tNEQ, :tGT, :tRSHFT, :tGEQ, :tLT,
52
+ :tLSHFT, :tLEQ, :tASSOC, :tEQQ, :tCMP, :tOP_ASGN]
53
+
54
+ def inspect(source, tokens, sexp, comments)
55
+ @source = source
56
+ positions_not_to_check = get_positions_not_to_check(tokens, sexp)
57
+
58
+ tokens.each_cons(3) do |token_before, token, token_after|
59
+ next if token_before.type == :kDEF # TODO: remove?
60
+ next if positions_not_to_check.include?(token.pos)
61
+
62
+ case token.type
63
+ when :tPOW
64
+ if has_space?(token_before, token, token_after)
65
+ add_offence(:convention, token.pos.line, MSG_DETECTED)
47
66
  end
67
+ when *BINARY_OPERATORS
68
+ check_missing_space(token_before, token, token_after)
69
+ end
70
+ end
71
+ end
72
+
73
+ # Returns an array of positions marking the tokens that this cop
74
+ # should not check, either because the token is not an operator
75
+ # or because another cop does the check.
76
+ def get_positions_not_to_check(tokens, sexp)
77
+ positions_not_to_check = []
78
+ do_not_check_block_arg_pipes(sexp, positions_not_to_check)
79
+ do_not_check_param_default(tokens, sexp, positions_not_to_check)
80
+ do_not_check_class_lshift_self(tokens, sexp, positions_not_to_check)
81
+ do_not_check_def_things(tokens, sexp, positions_not_to_check)
82
+ do_not_check_singleton_operator_defs(tokens, sexp,
83
+ positions_not_to_check)
84
+ positions_not_to_check
85
+ end
86
+
87
+ def do_not_check_block_arg_pipes(sexp, positions_not_to_check)
88
+ # each { |a| }
89
+ # ^ ^
90
+ on_node(:block, sexp) do |b|
91
+ on_node(:args, b) do |a|
92
+ positions_not_to_check << a.loc.begin << a.loc.end if a.loc.begin
93
+ end
94
+ end
95
+ end
96
+
97
+ def do_not_check_param_default(tokens, sexp, positions_not_to_check)
98
+ # func(a, b=nil)
99
+ # ^
100
+ on_node(:optarg, sexp) do |optarg|
101
+ _arg, equals, _value = tokens[index_of_first_token(optarg, tokens),
102
+ 3]
103
+ positions_not_to_check << equals.pos
104
+ end
105
+ end
106
+
107
+ def do_not_check_class_lshift_self(tokens, sexp, positions_not_to_check)
108
+ # class <<self
109
+ # ^
110
+ on_node(:sclass, sexp) do |sclass|
111
+ ix = index_of_first_token(sclass, tokens)
112
+ if tokens[ix, 2].map(&:type) == [:kCLASS, :tLSHFT]
113
+ positions_not_to_check << tokens[ix + 1].pos
48
114
  end
49
115
  end
50
116
  end
51
117
 
52
- def check_unwanted_space(tokens, ix)
53
- prev, t, nxt = tokens.values_at(ix - 1, ix, ix + 1)
54
- if t.type == :on_op && t.text == '**' &&
55
- (whitespace?(prev) || whitespace?(nxt))
56
- add_offence(:convention, t.pos.lineno,
57
- "Space around operator #{t.text} detected.")
118
+ def do_not_check_def_things(tokens, sexp, positions_not_to_check)
119
+ # def +(other)
120
+ # ^
121
+ on_node(:def, sexp) do |def_node|
122
+ # def each &block
123
+ # ^
124
+ # def each *args
125
+ # ^
126
+ on_node([:blockarg, :restarg], def_node) do |arg_node|
127
+ positions_not_to_check << tokens[index_of_first_token(arg_node,
128
+ tokens)].pos
129
+ end
130
+ positions_not_to_check <<
131
+ tokens[index_of_first_token(def_node, tokens) + 1].pos
132
+ end
133
+ end
134
+
135
+ def do_not_check_singleton_operator_defs(tokens, sexp,
136
+ positions_not_to_check)
137
+ # def self.===(other)
138
+ # ^
139
+ on_node(:defs, sexp) do |defs_node|
140
+ _receiver, name, _args = *defs_node
141
+ ix = index_of_first_token(defs_node, tokens)
142
+ name_token = tokens[ix..-1].find { |t| t.text == name.to_s }
143
+ positions_not_to_check << name_token.pos
144
+ end
145
+ end
146
+
147
+ def check_missing_space(token_before, token, token_after)
148
+ unless has_space?(token_before, token, token_after)
149
+ text = token.text.to_s + (token.type == :tOP_ASGN ? '=' : '')
150
+ add_offence(:convention, token.pos.line, MSG_MISSING % text)
58
151
  end
59
152
  end
153
+
154
+ def has_space?(token_before, token, token_after)
155
+ space_between?(token_before, token) && space_between?(token,
156
+ token_after)
157
+ end
60
158
  end
61
159
 
62
160
  class SpaceAroundBraces < Cop
63
161
  include SurroundingSpace
162
+ MSG_LEFT = "Surrounding space missing for '{'."
163
+ MSG_RIGHT = "Space missing to the left of '}'."
64
164
 
65
- def check_unwanted_space(tokens, ix)
165
+ def inspect(source, tokens, sexp, comments)
166
+ @source = source
167
+ positions_not_to_check = get_positions_not_to_check(tokens, sexp)
168
+ tokens.each_cons(2) do |t1, t2|
169
+ next if ([t1.pos, t2.pos] - positions_not_to_check).size < 2
170
+
171
+ type1, type2 = t1.type, t2.type
172
+ # :tLBRACE in hash literals, :tLCURLY otherwise.
173
+ next if [:tLCURLY, :tLBRACE].include?(type1) && type2 == :tRCURLY
174
+ check(t1, t2, MSG_LEFT) if type1 == :tLCURLY || type2 == :tLCURLY
175
+ check(t1, t2, MSG_RIGHT) if type2 == :tRCURLY
176
+ end
66
177
  end
67
178
 
68
- def check_missing_space(tokens, ix, grammar_path)
69
- t = tokens[ix]
70
- case t.type
71
- when :on_lbrace
72
- unless surrounded_by_whitespace?(tokens[ix - 1, 3])
73
- add_offence(:convention, t.pos.lineno,
74
- "Surrounding space missing for '{'.")
75
- end
76
- when :on_rbrace
77
- unless whitespace?(tokens[ix - 1])
78
- add_offence(:convention, t.pos.lineno,
79
- "Space missing to the left of '}'.")
179
+ def get_positions_not_to_check(tokens, sexp)
180
+ positions_not_to_check = []
181
+
182
+ on_node(:hash, sexp) do |hash|
183
+ b_ix = index_of_first_token(hash, tokens)
184
+ e_ix = index_of_last_token(hash, tokens)
185
+ positions_not_to_check << tokens[b_ix].pos << tokens[e_ix].pos
186
+ end
187
+
188
+ # TODO: Check braces inside string/symbol/regexp/xstr interpolation.
189
+ on_node([:dstr, :dsym, :regexp, :xstr], sexp) do |s|
190
+ b_ix = index_of_first_token(s, tokens)
191
+ e_ix = index_of_last_token(s, tokens)
192
+ tokens[b_ix..e_ix].each do |t|
193
+ positions_not_to_check << t.pos if t.type == :tRCURLY
80
194
  end
81
195
  end
196
+
197
+ positions_not_to_check
198
+ end
199
+
200
+ def check(t1, t2, msg)
201
+ unless space_between?(t1, t2)
202
+ add_offence(:convention, t1.pos.line, msg)
203
+ end
82
204
  end
83
205
  end
84
206
 
85
207
  module SpaceInside
86
208
  include SurroundingSpace
209
+ MSG = 'Space inside %s detected.'
87
210
 
88
- Paren = Struct.new :left, :right, :kind
89
-
90
- def check_unwanted_space(tokens, ix)
91
- paren = get_paren
92
- prev, t, nxt = tokens.values_at(ix - 1, ix, ix + 1)
93
- offence_detected = case t.type
94
- when paren.left
95
- nxt.type == :on_sp
96
- when paren.right
97
- if prev.type == :on_sp
98
- prev_ns = previous_non_space(tokens, ix)
99
- prev_ns &&
100
- prev_ns.pos.lineno == tokens[ix].pos.lineno &&
101
- # Avoid double reporting
102
- prev_ns.type != paren.left
103
- end
104
- end
105
- if offence_detected
106
- add_offence(:convention, t.pos.lineno,
107
- "Space inside #{paren.kind} detected.")
211
+ def inspect(source, tokens, sexp, comments)
212
+ @source = source
213
+ left, right, kind = specifics
214
+ tokens.each_cons(2) do |t1, t2|
215
+ if t1.type == left || t2.type == right
216
+ if t2.pos.line == t1.pos.line && space_between?(t1, t2)
217
+ add_offence(:convention, t1.pos.line, MSG % kind)
218
+ end
219
+ end
108
220
  end
109
221
  end
110
222
  end
111
223
 
112
224
  class SpaceInsideParens < Cop
113
225
  include SpaceInside
114
- def get_paren
115
- Paren.new(:on_lparen, :on_rparen, 'parentheses')
226
+
227
+ def specifics
228
+ [:tLPAREN2, :tRPAREN, 'parentheses']
116
229
  end
117
230
  end
118
231
 
119
232
  class SpaceInsideBrackets < Cop
120
233
  include SpaceInside
121
- def get_paren
122
- Paren.new(:on_lbracket, :on_rbracket, 'square brackets')
234
+
235
+ def specifics
236
+ [:tLBRACK, :tRBRACK, 'square brackets']
237
+ end
238
+ end
239
+
240
+ class SpaceInsideHashLiteralBraces < Cop
241
+ include SurroundingSpace
242
+ MSG = 'Space inside hash literal braces %s.'
243
+
244
+ def inspect(source, tokens, sexp, comments)
245
+ @source = source
246
+ on_node(:hash, sexp) do |hash|
247
+ b_ix = index_of_first_token(hash, tokens)
248
+ e_ix = index_of_last_token(hash, tokens)
249
+ check(tokens[b_ix], tokens[b_ix + 1])
250
+ check(tokens[e_ix - 1], tokens[e_ix])
251
+ end
252
+ end
253
+
254
+ def check(t1, t2)
255
+ types = [t1, t2].map(&:type)
256
+ braces = [:tLBRACE, :tRCURLY]
257
+ return if types == braces || (braces - types).size == 2
258
+ has_space = space_between?(t1, t2)
259
+ is_offence, word = if self.class.config['EnforcedStyleIsWithSpaces']
260
+ [!has_space, 'missing']
261
+ else
262
+ [has_space, 'detected']
263
+ end
264
+ add_offence(:convention, t1.pos.line, MSG % word) if is_offence
123
265
  end
124
266
  end
125
267
 
126
268
  class SpaceAroundEqualsInParameterDefault < Cop
127
- def inspect(file, source, tokens, sexp)
128
- each(:params, sexp) do |s|
129
- (s[2] || []).each do |param, value|
130
- value_pos = all_positions(value).first or next
131
- if param[-1].lineno == value_pos.lineno
132
- if value_pos.column - (param[-1].column + param[1].length) <= 2
133
- add_offence(:convention, param[-1].lineno,
134
- 'Surrounding space missing in default value ' +
135
- 'assignment.')
136
- end
137
- end
269
+ include SurroundingSpace
270
+ MSG = 'Surrounding space missing in default value assignment.'
271
+
272
+ def inspect(source, tokens, sexp, comments)
273
+ @source = source
274
+ on_node(:optarg, sexp) do |optarg|
275
+ arg, equals, value = tokens[index_of_first_token(optarg, tokens), 3]
276
+ unless space_between?(arg, equals) && space_between?(equals, value)
277
+ add_offence(:convention, equals.pos.line, MSG)
138
278
  end
139
279
  end
140
280
  end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class SymbolArray < Cop
6
+ MSG = 'Use %i or %I for array of symbols.'
7
+
8
+ def inspect(source, tokens, ast, comments)
9
+ # %i and %I were introduced in Ruby 2.0
10
+ unless RUBY_VERSION < '2.0.0'
11
+ on_node(:array, ast) do |s|
12
+ next unless s.loc.begin && s.loc.begin.source == '['
13
+
14
+ array_elems = s.children
15
+
16
+ # no need to check empty arrays
17
+ next unless array_elems && array_elems.size > 1
18
+
19
+ symbol_array = array_elems.all? { |e| e.type == :sym }
20
+
21
+ if symbol_array
22
+ add_offence(:convention,
23
+ s.loc.line,
24
+ MSG)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ class SymbolName < Cop
6
+ MSG = 'Use snake_case for symbols.'
7
+ SNAKE_CASE = /^[\da-z_]+[!?=]?$/
8
+ CAMEL_CASE = /^[A-Z][A-Za-z\d]*$/
9
+
10
+ def allow_camel_case?
11
+ self.class.config['AllowCamelCase']
12
+ end
13
+
14
+ def on_sym(node)
15
+ sym_name = node.to_a[0]
16
+ return unless sym_name =~ /^[a-zA-Z]/
17
+ return if sym_name =~ SNAKE_CASE
18
+ return if allow_camel_case? && sym_name =~ CAMEL_CASE
19
+ add_offence(:convention, node.loc.line, MSG)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -5,13 +5,43 @@ require 'open3'
5
5
  module Rubocop
6
6
  module Cop
7
7
  class Syntax < Cop
8
- def inspect(file, source, tokens, sexp)
9
- _, stderr, _ =
10
- Open3.capture3('ruby -wc', stdin_data: source.join("\n"))
8
+ def inspect_file(file)
9
+ # Starting JRuby processes would be extremely slow
10
+ # We need to check if rbx returns nice warning messages
11
+ return unless RUBY_ENGINE == 'ruby'
12
+
13
+ stderr = nil
14
+
15
+ # it's extremely important to run the syntax check in a
16
+ # clean environment - otherwise it will be extremely slow
17
+ if defined? Bundler
18
+ Bundler.with_clean_env do
19
+ _, stderr, _ = Open3.capture3("ruby -wc #{file}")
20
+ end
21
+ else
22
+ _, stderr, _ = Open3.capture3("ruby -wc #{file}")
23
+ end
11
24
 
12
25
  stderr.each_line do |line|
13
- line_no, warning = line.match(/.+:(\d+): warning: (.+)/).captures
14
- add_offence(:warning, line_no.to_i, warning.capitalize) if line_no
26
+ # discard lines that are not containing relevant info
27
+ if line =~ /.+:(\d+): (.+)/
28
+ # Assignment to unused variables beginning with underscore
29
+ # is reported by Ruby 1.9, but not 2.0. Make 1.9 behave
30
+ # like 2.0.
31
+ unless line =~ /assigned but unused variable - _\w+/
32
+ line_no, severity, message = process_line(line)
33
+ add_offence(severity, line_no, message)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def process_line(line)
40
+ line_no, message = line.match(/.+:(\d+): (.+)/).captures
41
+ if message.start_with?('warning: ')
42
+ [line_no.to_i, :warning, message.sub(/warning: /, '').capitalize]
43
+ else
44
+ [line_no.to_i, :error, message.capitalize]
15
45
  end
16
46
  end
17
47
  end
@@ -3,11 +3,11 @@
3
3
  module Rubocop
4
4
  module Cop
5
5
  class Tab < Cop
6
- ERROR_MESSAGE = 'Tab detected.'
6
+ MSG = 'Tab detected.'
7
7
 
8
- def inspect(file, source, tokens, sexp)
8
+ def inspect(source, tokens, ast, comments)
9
9
  source.each_with_index do |line, index|
10
- add_offence(:convention, index + 1, ERROR_MESSAGE) if line =~ /^ *\t/
10
+ add_offence(:convention, index + 1, MSG) if line =~ /^ *\t/
11
11
  end
12
12
  end
13
13
  end