rubocop-ast 0.0.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -4
  3. data/lib/rubocop/ast.rb +12 -7
  4. data/lib/rubocop/ast/builder.rb +8 -1
  5. data/lib/rubocop/ast/ext/range.rb +28 -0
  6. data/lib/rubocop/ast/ext/set.rb +12 -0
  7. data/lib/rubocop/ast/node.rb +81 -10
  8. data/lib/rubocop/ast/node/array_node.rb +2 -8
  9. data/lib/rubocop/ast/node/block_node.rb +1 -1
  10. data/lib/rubocop/ast/node/break_node.rb +1 -6
  11. data/lib/rubocop/ast/node/case_match_node.rb +3 -9
  12. data/lib/rubocop/ast/node/case_node.rb +13 -9
  13. data/lib/rubocop/ast/node/const_node.rb +63 -0
  14. data/lib/rubocop/ast/node/def_node.rb +5 -24
  15. data/lib/rubocop/ast/node/defined_node.rb +2 -0
  16. data/lib/rubocop/ast/node/float_node.rb +1 -0
  17. data/lib/rubocop/ast/node/forward_args_node.rb +15 -0
  18. data/lib/rubocop/ast/node/hash_node.rb +21 -8
  19. data/lib/rubocop/ast/node/if_node.rb +7 -14
  20. data/lib/rubocop/ast/node/index_node.rb +48 -0
  21. data/lib/rubocop/ast/node/indexasgn_node.rb +50 -0
  22. data/lib/rubocop/ast/node/int_node.rb +1 -0
  23. data/lib/rubocop/ast/node/lambda_node.rb +65 -0
  24. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +2 -8
  25. data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +99 -3
  26. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +56 -0
  27. data/lib/rubocop/ast/node/next_node.rb +12 -0
  28. data/lib/rubocop/ast/node/pair_node.rb +2 -2
  29. data/lib/rubocop/ast/node/regexp_node.rb +56 -0
  30. data/lib/rubocop/ast/node/resbody_node.rb +21 -0
  31. data/lib/rubocop/ast/node/rescue_node.rb +49 -0
  32. data/lib/rubocop/ast/node/return_node.rb +1 -13
  33. data/lib/rubocop/ast/node/send_node.rb +9 -2
  34. data/lib/rubocop/ast/node/super_node.rb +2 -0
  35. data/lib/rubocop/ast/node/when_node.rb +3 -9
  36. data/lib/rubocop/ast/node/yield_node.rb +2 -0
  37. data/lib/rubocop/ast/node_pattern.rb +952 -0
  38. data/lib/rubocop/ast/processed_source.rb +285 -0
  39. data/lib/rubocop/ast/token.rb +116 -0
  40. data/lib/rubocop/ast/traversal.rb +6 -4
  41. data/lib/rubocop/ast/version.rb +1 -1
  42. metadata +19 -13
  43. data/lib/rubocop/ast/node/retry_node.rb +0 -17
  44. data/lib/rubocop/error.rb +0 -34
  45. data/lib/rubocop/node_pattern.rb +0 -881
  46. data/lib/rubocop/processed_source.rb +0 -211
  47. data/lib/rubocop/token.rb +0 -114
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
4
+
5
+ # rubocop:disable Metrics/ClassLength
6
+ module RuboCop
7
+ module AST
8
+ # ProcessedSource contains objects which are generated by Parser
9
+ # and other information such as disabled lines for cops.
10
+ # It also provides a convenient way to access source lines.
11
+ class ProcessedSource
12
+ STRING_SOURCE_NAME = '(string)'
13
+
14
+ attr_reader :path, :buffer, :ast, :comments, :tokens, :diagnostics,
15
+ :parser_error, :raw_source, :ruby_version
16
+
17
+ def self.from_file(path, ruby_version)
18
+ file = File.read(path, mode: 'rb')
19
+ new(file, ruby_version, path)
20
+ end
21
+
22
+ def initialize(source, ruby_version, path = nil)
23
+ # Defaults source encoding to UTF-8, regardless of the encoding it has
24
+ # been read with, which could be non-utf8 depending on the default
25
+ # external encoding.
26
+ source.force_encoding(Encoding::UTF_8) unless source.encoding == Encoding::UTF_8
27
+
28
+ @raw_source = source
29
+ @path = path
30
+ @diagnostics = []
31
+ @ruby_version = ruby_version
32
+ @parser_error = nil
33
+
34
+ parse(source, ruby_version)
35
+ end
36
+
37
+ def ast_with_comments
38
+ return if !ast || !comments
39
+
40
+ @ast_with_comments ||= Parser::Source::Comment.associate(ast, comments)
41
+ end
42
+
43
+ # Returns the source lines, line break characters removed, excluding a
44
+ # possible __END__ and everything that comes after.
45
+ def lines
46
+ @lines ||= begin
47
+ all_lines = @buffer.source_lines
48
+ last_token_line = tokens.any? ? tokens.last.line : all_lines.size
49
+ result = []
50
+ all_lines.each_with_index do |line, ix|
51
+ break if ix >= last_token_line && line == '__END__'
52
+
53
+ result << line
54
+ end
55
+ result
56
+ end
57
+ end
58
+
59
+ def [](*args)
60
+ lines[*args]
61
+ end
62
+
63
+ def valid_syntax?
64
+ return false if @parser_error
65
+
66
+ @diagnostics.none? { |d| %i[error fatal].include?(d.level) }
67
+ end
68
+
69
+ # Raw source checksum for tracking infinite loops.
70
+ def checksum
71
+ Digest::SHA1.hexdigest(@raw_source)
72
+ end
73
+
74
+ # @deprecated Use `comments.each`
75
+ def each_comment(&block)
76
+ comments.each(&block)
77
+ end
78
+
79
+ # @deprecated Use `comment_at_line`, `each_comment_in_lines`, or `comments.find`
80
+ def find_comment(&block)
81
+ comments.find(&block)
82
+ end
83
+
84
+ # @deprecated Use `tokens.each`
85
+ def each_token(&block)
86
+ tokens.each(&block)
87
+ end
88
+
89
+ # @deprecated Use `tokens.find`
90
+ def find_token(&block)
91
+ tokens.find(&block)
92
+ end
93
+
94
+ def file_path
95
+ buffer.name
96
+ end
97
+
98
+ def blank?
99
+ ast.nil?
100
+ end
101
+
102
+ # @return [Comment, nil] the comment at that line, if any.
103
+ def comment_at_line(line)
104
+ comment_index[line]
105
+ end
106
+
107
+ # @return [Boolean] if the given line number has a comment.
108
+ def line_with_comment?(line)
109
+ comment_index.include?(line)
110
+ end
111
+
112
+ # Enumerates on the comments contained with the given `line_range`
113
+ def each_comment_in_lines(line_range)
114
+ return to_enum(:each_comment_in_lines, line_range) unless block_given?
115
+
116
+ line_range.each do |line|
117
+ if (comment = comment_index[line])
118
+ yield comment
119
+ end
120
+ end
121
+ end
122
+
123
+ # @return [Boolean] if any of the lines in the given `source_range` has a comment.
124
+ # Consider using `each_comment_in_lines` instead
125
+ def contains_comment?(source_range)
126
+ each_comment_in_lines(source_range.line..source_range.last_line).any?
127
+ end
128
+ # @deprecated use contains_comment?
129
+ alias commented? contains_comment?
130
+
131
+ # @deprecated Use `each_comment_in_lines`
132
+ # Should have been called `comments_before_or_at_line`. Doubtful it has of any valid use.
133
+ def comments_before_line(line)
134
+ each_comment_in_lines(0..line).to_a
135
+ end
136
+
137
+ def start_with?(string)
138
+ return false if self[0].nil?
139
+
140
+ self[0].start_with?(string)
141
+ end
142
+
143
+ def preceding_line(token)
144
+ lines[token.line - 2]
145
+ end
146
+
147
+ def current_line(token)
148
+ lines[token.line - 1]
149
+ end
150
+
151
+ def following_line(token)
152
+ lines[token.line]
153
+ end
154
+
155
+ def line_indentation(line_number)
156
+ lines[line_number - 1]
157
+ .match(/^(\s*)/)[1]
158
+ .to_s
159
+ .length
160
+ end
161
+
162
+ def tokens_within(range_or_node)
163
+ begin_index = first_token_index(range_or_node)
164
+ end_index = last_token_index(range_or_node)
165
+ sorted_tokens[begin_index..end_index]
166
+ end
167
+
168
+ def first_token_of(range_or_node)
169
+ sorted_tokens[first_token_index(range_or_node)]
170
+ end
171
+
172
+ def last_token_of(range_or_node)
173
+ sorted_tokens[last_token_index(range_or_node)]
174
+ end
175
+
176
+ private
177
+
178
+ def comment_index
179
+ @comment_index ||= {}.tap do |hash|
180
+ comments.each { |c| hash[c.location.line] = c }
181
+ end
182
+ end
183
+
184
+ def parse(source, ruby_version)
185
+ buffer_name = @path || STRING_SOURCE_NAME
186
+ @buffer = Parser::Source::Buffer.new(buffer_name, 1)
187
+
188
+ begin
189
+ @buffer.source = source
190
+ rescue EncodingError => e
191
+ @parser_error = e
192
+ @ast = nil
193
+ @comments = []
194
+ @tokens = []
195
+ return
196
+ end
197
+
198
+ @ast, @comments, @tokens = tokenize(create_parser(ruby_version))
199
+ end
200
+
201
+ def tokenize(parser)
202
+ begin
203
+ ast, comments, tokens = parser.tokenize(@buffer)
204
+ ast ||= nil # force `false` to `nil`, see https://github.com/whitequark/parser/pull/722
205
+ rescue Parser::SyntaxError
206
+ # All errors are in diagnostics. No need to handle exception.
207
+ comments = []
208
+ tokens = []
209
+ end
210
+
211
+ ast&.complete!
212
+ tokens.map! { |t| Token.from_parser_token(t) }
213
+
214
+ [ast, comments, tokens]
215
+ end
216
+
217
+ # rubocop:disable Metrics/MethodLength
218
+ def parser_class(ruby_version)
219
+ case ruby_version
220
+ when 2.4
221
+ require 'parser/ruby24'
222
+ Parser::Ruby24
223
+ when 2.5
224
+ require 'parser/ruby25'
225
+ Parser::Ruby25
226
+ when 2.6
227
+ require 'parser/ruby26'
228
+ Parser::Ruby26
229
+ when 2.7
230
+ require 'parser/ruby27'
231
+ Parser::Ruby27
232
+ when 2.8
233
+ require 'parser/ruby28'
234
+ Parser::Ruby28
235
+ else
236
+ raise ArgumentError,
237
+ "RuboCop found unknown Ruby version: #{ruby_version.inspect}"
238
+ end
239
+ end
240
+ # rubocop:enable Metrics/MethodLength
241
+
242
+ def create_parser(ruby_version)
243
+ builder = RuboCop::AST::Builder.new
244
+
245
+ parser_class(ruby_version).new(builder).tap do |parser|
246
+ # On JRuby there's a risk that we hang in tokenize() if we
247
+ # don't set the all errors as fatal flag. The problem is caused by a bug
248
+ # in Racc that is discussed in issue #93 of the whitequark/parser
249
+ # project on GitHub.
250
+ parser.diagnostics.all_errors_are_fatal = (RUBY_ENGINE != 'ruby')
251
+ parser.diagnostics.ignore_warnings = false
252
+ parser.diagnostics.consumer = lambda do |diagnostic|
253
+ @diagnostics << diagnostic
254
+ end
255
+ end
256
+ end
257
+
258
+ def first_token_index(range_or_node)
259
+ begin_pos = source_range(range_or_node).begin_pos
260
+ sorted_tokens.bsearch_index { |token| token.begin_pos >= begin_pos }
261
+ end
262
+
263
+ def last_token_index(range_or_node)
264
+ end_pos = source_range(range_or_node).end_pos
265
+ sorted_tokens.bsearch_index { |token| token.end_pos >= end_pos }
266
+ end
267
+
268
+ # The tokens list is always sorted by token position, except for cases when heredoc
269
+ # is passed as a method argument. In this case tokens are interleaved by
270
+ # heredoc contents' tokens.
271
+ def sorted_tokens
272
+ @sorted_tokens ||= tokens.sort_by(&:begin_pos)
273
+ end
274
+
275
+ def source_range(range_or_node)
276
+ if range_or_node.respond_to?(:source_range)
277
+ range_or_node.source_range
278
+ else
279
+ range_or_node
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
285
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A basic wrapper around Parser's tokens.
6
+ class Token
7
+ attr_reader :pos, :type, :text
8
+
9
+ def self.from_parser_token(parser_token)
10
+ type, details = parser_token
11
+ text, range = details
12
+ new(range, type, text)
13
+ end
14
+
15
+ def initialize(pos, type, text)
16
+ @pos = pos
17
+ @type = type
18
+ # Parser token "text" may be an Integer
19
+ @text = text.to_s
20
+ end
21
+
22
+ def line
23
+ @pos.line
24
+ end
25
+
26
+ def column
27
+ @pos.column
28
+ end
29
+
30
+ def begin_pos
31
+ @pos.begin_pos
32
+ end
33
+
34
+ def end_pos
35
+ @pos.end_pos
36
+ end
37
+
38
+ def to_s
39
+ "[[#{line}, #{column}], #{type}, #{text.inspect}]"
40
+ end
41
+
42
+ # Checks if there is whitespace after token
43
+ def space_after?
44
+ pos.source_buffer.source.match(/\G\s/, end_pos)
45
+ end
46
+
47
+ # Checks if there is whitespace before token
48
+ def space_before?
49
+ position = begin_pos.zero? ? begin_pos : begin_pos - 1
50
+ pos.source_buffer.source.match(/\G\s/, position)
51
+ end
52
+
53
+ ## Type Predicates
54
+
55
+ def comment?
56
+ type == :tCOMMENT
57
+ end
58
+
59
+ def semicolon?
60
+ type == :tSEMI
61
+ end
62
+
63
+ def left_array_bracket?
64
+ type == :tLBRACK
65
+ end
66
+
67
+ def left_ref_bracket?
68
+ type == :tLBRACK2
69
+ end
70
+
71
+ def left_bracket?
72
+ %i[tLBRACK tLBRACK2].include?(type)
73
+ end
74
+
75
+ def right_bracket?
76
+ type == :tRBRACK
77
+ end
78
+
79
+ def left_brace?
80
+ type == :tLBRACE
81
+ end
82
+
83
+ def left_curly_brace?
84
+ type == :tLCURLY
85
+ end
86
+
87
+ def right_curly_brace?
88
+ type == :tRCURLY
89
+ end
90
+
91
+ def left_parens?
92
+ %i[tLPAREN tLPAREN2].include?(type)
93
+ end
94
+
95
+ def right_parens?
96
+ type == :tRPAREN
97
+ end
98
+
99
+ def comma?
100
+ type == :tCOMMA
101
+ end
102
+
103
+ def rescue_modifier?
104
+ type == :kRESCUE_MOD
105
+ end
106
+
107
+ def end?
108
+ type == :kEND
109
+ end
110
+
111
+ def equal_sign?
112
+ %i[tEQL tOP_ASGN].include?(type)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -19,21 +19,23 @@ module RuboCop
19
19
  rational str sym regopt self lvar
20
20
  ivar cvar gvar nth_ref back_ref cbase
21
21
  arg restarg blockarg shadowarg
22
- kwrestarg zsuper lambda redo retry
22
+ kwrestarg zsuper redo retry
23
23
  forward_args forwarded_args
24
- match_var match_nil_pattern empty_else].freeze
24
+ match_var match_nil_pattern empty_else
25
+ forward_arg lambda procarg0 __ENCODING__].freeze
25
26
  ONE_CHILD_NODE = %i[splat kwsplat block_pass not break next
26
27
  preexe postexe match_current_line defined?
27
28
  arg_expr pin match_rest if_guard unless_guard
28
29
  match_with_trailing_comma].freeze
29
30
  MANY_CHILD_NODES = %i[dstr dsym xstr regexp array hash pair
30
- mlhs masgn or_asgn and_asgn
31
+ mlhs masgn or_asgn and_asgn rasgn mrasgn
31
32
  undef alias args super yield or and
32
33
  while_post until_post iflipflop eflipflop
33
34
  match_with_lvasgn begin kwbegin return
34
35
  in_match match_alt
35
36
  match_as array_pattern array_pattern_with_tail
36
- hash_pattern const_pattern].freeze
37
+ hash_pattern const_pattern find_pattern
38
+ index indexasgn].freeze
37
39
  SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
38
40
  kwoptarg].freeze
39
41
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module AST
5
5
  module Version
6
- STRING = '0.0.2'
6
+ STRING = '0.4.0'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-ast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  - Jonas Arvidsson
9
9
  - Yuji Nakayama
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-05-12 00:00:00.000000000 Z
13
+ date: 2020-09-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parser
@@ -18,14 +18,14 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 2.7.0.1
21
+ version: 2.7.1.4
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 2.7.0.1
28
+ version: 2.7.1.4
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: bundler
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -59,6 +59,8 @@ files:
59
59
  - lib/rubocop-ast.rb
60
60
  - lib/rubocop/ast.rb
61
61
  - lib/rubocop/ast/builder.rb
62
+ - lib/rubocop/ast/ext/range.rb
63
+ - lib/rubocop/ast/ext/set.rb
62
64
  - lib/rubocop/ast/node.rb
63
65
  - lib/rubocop/ast/node/alias_node.rb
64
66
  - lib/rubocop/ast/node/and_node.rb
@@ -69,6 +71,7 @@ files:
69
71
  - lib/rubocop/ast/node/case_match_node.rb
70
72
  - lib/rubocop/ast/node/case_node.rb
71
73
  - lib/rubocop/ast/node/class_node.rb
74
+ - lib/rubocop/ast/node/const_node.rb
72
75
  - lib/rubocop/ast/node/def_node.rb
73
76
  - lib/rubocop/ast/node/defined_node.rb
74
77
  - lib/rubocop/ast/node/ensure_node.rb
@@ -77,8 +80,11 @@ files:
77
80
  - lib/rubocop/ast/node/forward_args_node.rb
78
81
  - lib/rubocop/ast/node/hash_node.rb
79
82
  - lib/rubocop/ast/node/if_node.rb
83
+ - lib/rubocop/ast/node/index_node.rb
84
+ - lib/rubocop/ast/node/indexasgn_node.rb
80
85
  - lib/rubocop/ast/node/int_node.rb
81
86
  - lib/rubocop/ast/node/keyword_splat_node.rb
87
+ - lib/rubocop/ast/node/lambda_node.rb
82
88
  - lib/rubocop/ast/node/mixin/basic_literal_node.rb
83
89
  - lib/rubocop/ast/node/mixin/binary_operator_node.rb
84
90
  - lib/rubocop/ast/node/mixin/collection_node.rb
@@ -91,12 +97,13 @@ files:
91
97
  - lib/rubocop/ast/node/mixin/parameterized_node.rb
92
98
  - lib/rubocop/ast/node/mixin/predicate_operator_node.rb
93
99
  - lib/rubocop/ast/node/module_node.rb
100
+ - lib/rubocop/ast/node/next_node.rb
94
101
  - lib/rubocop/ast/node/or_node.rb
95
102
  - lib/rubocop/ast/node/pair_node.rb
96
103
  - lib/rubocop/ast/node/range_node.rb
97
104
  - lib/rubocop/ast/node/regexp_node.rb
98
105
  - lib/rubocop/ast/node/resbody_node.rb
99
- - lib/rubocop/ast/node/retry_node.rb
106
+ - lib/rubocop/ast/node/rescue_node.rb
100
107
  - lib/rubocop/ast/node/return_node.rb
101
108
  - lib/rubocop/ast/node/self_class_node.rb
102
109
  - lib/rubocop/ast/node/send_node.rb
@@ -107,13 +114,12 @@ files:
107
114
  - lib/rubocop/ast/node/when_node.rb
108
115
  - lib/rubocop/ast/node/while_node.rb
109
116
  - lib/rubocop/ast/node/yield_node.rb
117
+ - lib/rubocop/ast/node_pattern.rb
118
+ - lib/rubocop/ast/processed_source.rb
110
119
  - lib/rubocop/ast/sexp.rb
120
+ - lib/rubocop/ast/token.rb
111
121
  - lib/rubocop/ast/traversal.rb
112
122
  - lib/rubocop/ast/version.rb
113
- - lib/rubocop/error.rb
114
- - lib/rubocop/node_pattern.rb
115
- - lib/rubocop/processed_source.rb
116
- - lib/rubocop/token.rb
117
123
  homepage: https://github.com/rubocop-hq/rubocop-ast
118
124
  licenses:
119
125
  - MIT
@@ -121,9 +127,9 @@ metadata:
121
127
  homepage_uri: https://www.rubocop.org/
122
128
  changelog_uri: https://github.com/rubocop-hq/rubocop-ast/blob/master/CHANGELOG.md
123
129
  source_code_uri: https://github.com/rubocop-hq/rubocop-ast/
124
- documentation_uri: https://docs.rubocop.org/
130
+ documentation_uri: https://docs.rubocop.org/rubocop-ast/
125
131
  bug_tracker_uri: https://github.com/rubocop-hq/rubocop-ast/issues
126
- post_install_message:
132
+ post_install_message:
127
133
  rdoc_options: []
128
134
  require_paths:
129
135
  - lib
@@ -139,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
145
  version: '0'
140
146
  requirements: []
141
147
  rubygems_version: 3.1.2
142
- signing_key:
148
+ signing_key:
143
149
  specification_version: 4
144
150
  summary: RuboCop tools to deal with Ruby code AST.
145
151
  test_files: []