dead_end 3.0.3 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88cc140d421e1df16009993e7d7438054dad7a6b6fec740496e92c06c0bc2f6d
4
- data.tar.gz: ba736b1459f6c18aed62b566fa8e48ca6a4dd8379bc2dc2d99594c0daf136fb6
3
+ metadata.gz: f35aa207ae1ff3904d39f5c0ddca5b8a0f68ae157f5c0a999cd3f2f1530e461b
4
+ data.tar.gz: b08307eb152fc12b84493409bfd8a9b8f23905180ad5ed9168a58d152913bc6e
5
5
  SHA512:
6
- metadata.gz: 3cf585c9f073344d6259478c239fd45b77a5fb7fd1c37c18d3d2a7cf91323b00750cccb18d666935ea20ce2c209137216304429a26aac0bf26fec7aa5440ff14
7
- data.tar.gz: 8a818ac4001ce7ec01ae36c4c223f47c7686e111226718cf22e8e2ca9f00a0bb3f3ef6409ad703cb698a67dc62035225973f37ea5f527207c26e705acd4c8f33
6
+ metadata.gz: 02b4ec64732eea9dbb55594c130d2fade4838a855bf3366408a5846683c3d2866e21844c7d3a2797adc865e177e7edf3bffaad0b87f555627f75fcac3d2aa219
7
+ data.tar.gz: '029add7304c46c97a8a04b564f5798a2e5d43afe24472ae51268226e2dd5e71c56b9c48bf889232d4c3f2759b6edb7ea4c4f332688d6995ddaac0f7ec8ae5803'
data/.circleci/config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  version: 2.1
2
2
  orbs:
3
- ruby: circleci/ruby@1.1.2
3
+ ruby: circleci/ruby@1.2.0
4
4
  references:
5
5
  unit: &unit
6
6
  run:
@@ -45,6 +45,28 @@ jobs:
45
45
  - ruby/install-deps
46
46
  - <<: *unit
47
47
 
48
+ "ruby-3-1":
49
+ docker:
50
+ - image: 'cimg/base:stable'
51
+ steps:
52
+ - checkout
53
+ - ruby/install:
54
+ version: '3.1.2'
55
+ - run: ruby -v
56
+ - ruby/install-deps
57
+ - <<: *unit
58
+
59
+ "ruby-3-2":
60
+ docker:
61
+ - image: 'cimg/base:stable'
62
+ steps:
63
+ - checkout
64
+ - ruby/install:
65
+ version: '3.2.0-preview1'
66
+ - run: ruby -v
67
+ - ruby/install-deps
68
+ - <<: *unit
69
+
48
70
  "lint":
49
71
  docker:
50
72
  - image: circleci/ruby:3.0
@@ -61,4 +83,6 @@ workflows:
61
83
  - "ruby-2-6"
62
84
  - "ruby-2-7"
63
85
  - "ruby-3-0"
86
+ - "ruby-3-1"
87
+ - "ruby-3-2"
64
88
  - "lint"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## HEAD (unreleased)
2
2
 
3
+ ## 3.1.2
4
+
5
+ - Fixed internal class AroundBlockScan, minor changes in outputs (https://github.com/zombocom/dead_end/pull/131)
6
+
7
+ ## 3.1.1
8
+
9
+ - Fix case where Ripper lexing identified incorrect code as a keyword (https://github.com/zombocom/dead_end/pull/122)
10
+
11
+ ## 3.1.0
12
+
13
+ - Add support for Ruby 3.1 by updating `require_relative` logic (https://github.com/zombocom/dead_end/pull/120)
14
+ - Requiring `dead_end/auto` is now deprecated please require `dead_end` instead (https://github.com/zombocom/dead_end/pull/119)
15
+ - Requiring `dead_end/api` now loads code without monkeypatching core extensions (https://github.com/zombocom/dead_end/pull/119)
16
+ - The interface `DeadEnd.handle_error` is declared public and stable (https://github.com/zombocom/dead_end/pull/119)
17
+
3
18
  ## 3.0.3
4
19
 
5
20
  - Expand explanations coming from additional Ripper errors (https://github.com/zombocom/dead_end/pull/117)
data/Gemfile CHANGED
@@ -10,3 +10,5 @@ gem "rspec", "~> 3.0"
10
10
  gem "stackprof"
11
11
  gem "standard"
12
12
  gem "ruby-prof"
13
+
14
+ gem "benchmark-ips"
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dead_end (3.0.3)
4
+ dead_end (3.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.2)
10
+ benchmark-ips (2.9.2)
10
11
  diff-lcs (1.4.4)
11
12
  parallel (1.21.0)
12
13
  parser (3.0.2.0)
@@ -54,6 +55,7 @@ PLATFORMS
54
55
  ruby
55
56
 
56
57
  DEPENDENCIES
58
+ benchmark-ips
57
59
  dead_end!
58
60
  rake (~> 12.0)
59
61
  rspec (~> 3.0)
@@ -62,4 +64,4 @@ DEPENDENCIES
62
64
  standard
63
65
 
64
66
  BUNDLED WITH
65
- 2.2.30
67
+ 2.3.14
data/README.md CHANGED
@@ -166,6 +166,16 @@ Here's an example:
166
166
 
167
167
  ![](assets/syntax_search.gif)
168
168
 
169
+ ## Use internals
170
+
171
+ To use the `dead_end` gem without monkeypatching you can `require 'dead_end/api'`. This will allow you to load `dead_end` and use its internals without mutating `require`.
172
+
173
+ Stable internal interface(s):
174
+
175
+ - `DeadEnd.handle_error(e)`
176
+
177
+ Any other entrypoints are subject to change without warning. If you want to use an internal interface from `dead_end` not on this list, open an issue to explain your use case.
178
+
169
179
  ## Development
170
180
 
171
181
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/dead_end.gemspec CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/dead_end/version"
3
+ begin
4
+ require_relative "lib/dead_end/version"
5
+ rescue LoadError # Fallback to load version file in ruby core repository
6
+ require_relative "version"
7
+ end
4
8
 
5
9
  Gem::Specification.new do |spec|
6
10
  spec.name = "dead_end"
@@ -0,0 +1,198 @@
1
+ require_relative "version"
2
+
3
+ require "tmpdir"
4
+ require "stringio"
5
+ require "pathname"
6
+ require "ripper"
7
+ require "timeout"
8
+
9
+ module DeadEnd
10
+ # Used to indicate a default value that cannot
11
+ # be confused with another input.
12
+ DEFAULT_VALUE = Object.new.freeze
13
+
14
+ class Error < StandardError; end
15
+ TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i
16
+
17
+ # DeadEnd.handle_error [Public]
18
+ #
19
+ # Takes a `SyntaxError`` exception, uses the
20
+ # error message to locate the file. Then the file
21
+ # will be analyzed to find the location of the syntax
22
+ # error and emit that location to stderr.
23
+ #
24
+ # Example:
25
+ #
26
+ # begin
27
+ # require 'bad_file'
28
+ # rescue => e
29
+ # DeadEnd.handle_error(e)
30
+ # end
31
+ #
32
+ # By default it will re-raise the exception unless
33
+ # `re_raise: false`. The message output location
34
+ # can be configured using the `io: $stderr` input.
35
+ #
36
+ # If a valid filename cannot be determined, the original
37
+ # exception will be re-raised (even with
38
+ # `re_raise: false`).
39
+ def self.handle_error(e, re_raise: true, io: $stderr)
40
+ unless e.is_a?(SyntaxError)
41
+ io.puts("DeadEnd: Must pass a SyntaxError, got: #{e.class}")
42
+ raise e
43
+ end
44
+
45
+ file = PathnameFromMessage.new(e.message, io: io).call.name
46
+ raise e unless file
47
+
48
+ io.sync = true
49
+
50
+ call(
51
+ io: io,
52
+ source: file.read,
53
+ filename: file
54
+ )
55
+
56
+ raise e if re_raise
57
+ end
58
+
59
+ # DeadEnd.call [Private]
60
+ #
61
+ # Main private interface
62
+ def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr)
63
+ search = nil
64
+ filename = nil if filename == DEFAULT_VALUE
65
+ Timeout.timeout(timeout) do
66
+ record_dir ||= ENV["DEBUG"] ? "tmp" : nil
67
+ search = CodeSearch.new(source, record_dir: record_dir).call
68
+ end
69
+
70
+ blocks = search.invalid_blocks
71
+ DisplayInvalidBlocks.new(
72
+ io: io,
73
+ blocks: blocks,
74
+ filename: filename,
75
+ terminal: terminal,
76
+ code_lines: search.code_lines
77
+ ).call
78
+ rescue Timeout::Error => e
79
+ io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
80
+ io.puts e.backtrace.first(3).join($/)
81
+ end
82
+
83
+ # DeadEnd.record_dir [Private]
84
+ #
85
+ # Used to generate a unique directory to record
86
+ # search steps for debugging
87
+ def self.record_dir(dir)
88
+ time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
89
+ dir = Pathname(dir)
90
+ symlink = dir.join("last").tap { |path| path.delete if path.exist? }
91
+ dir.join(time).tap { |path|
92
+ path.mkpath
93
+ FileUtils.symlink(path.basename, symlink)
94
+ }
95
+ end
96
+
97
+ # DeadEnd.valid_without? [Private]
98
+ #
99
+ # This will tell you if the `code_lines` would be valid
100
+ # if you removed the `without_lines`. In short it's a
101
+ # way to detect if we've found the lines with syntax errors
102
+ # in our document yet.
103
+ #
104
+ # code_lines = [
105
+ # CodeLine.new(line: "def foo\n", index: 0)
106
+ # CodeLine.new(line: " def bar\n", index: 1)
107
+ # CodeLine.new(line: "end\n", index: 2)
108
+ # ]
109
+ #
110
+ # DeadEnd.valid_without?(
111
+ # without_lines: code_lines[1],
112
+ # code_lines: code_lines
113
+ # ) # => true
114
+ #
115
+ # DeadEnd.valid?(code_lines) # => false
116
+ def self.valid_without?(without_lines:, code_lines:)
117
+ lines = code_lines - Array(without_lines).flatten
118
+
119
+ if lines.empty?
120
+ true
121
+ else
122
+ valid?(lines)
123
+ end
124
+ end
125
+
126
+ # DeadEnd.invalid? [Private]
127
+ #
128
+ # Opposite of `DeadEnd.valid?`
129
+ def self.invalid?(source)
130
+ source = source.join if source.is_a?(Array)
131
+ source = source.to_s
132
+
133
+ Ripper.new(source).tap(&:parse).error?
134
+ end
135
+
136
+ # DeadEnd.valid? [Private]
137
+ #
138
+ # Returns truthy if a given input source is valid syntax
139
+ #
140
+ # DeadEnd.valid?(<<~EOM) # => true
141
+ # def foo
142
+ # end
143
+ # EOM
144
+ #
145
+ # DeadEnd.valid?(<<~EOM) # => false
146
+ # def foo
147
+ # def bar # Syntax error here
148
+ # end
149
+ # EOM
150
+ #
151
+ # You can also pass in an array of lines and they'll be
152
+ # joined before evaluating
153
+ #
154
+ # DeadEnd.valid?(
155
+ # [
156
+ # "def foo\n",
157
+ # "end\n"
158
+ # ]
159
+ # ) # => true
160
+ #
161
+ # DeadEnd.valid?(
162
+ # [
163
+ # "def foo\n",
164
+ # " def bar\n", # Syntax error here
165
+ # "end\n"
166
+ # ]
167
+ # ) # => false
168
+ #
169
+ # As an FYI the CodeLine class instances respond to `to_s`
170
+ # so passing a CodeLine in as an object or as an array
171
+ # will convert it to it's code representation.
172
+ def self.valid?(source)
173
+ !invalid?(source)
174
+ end
175
+ end
176
+
177
+ # Integration
178
+ require_relative "cli"
179
+
180
+ # Core logic
181
+ require_relative "code_search"
182
+ require_relative "code_frontier"
183
+ require_relative "explain_syntax"
184
+ require_relative "clean_document"
185
+
186
+ # Helpers
187
+ require_relative "lex_all"
188
+ require_relative "code_line"
189
+ require_relative "code_block"
190
+ require_relative "block_expand"
191
+ require_relative "ripper_errors"
192
+ require_relative "priority_queue"
193
+ require_relative "unvisited_lines"
194
+ require_relative "around_block_scan"
195
+ require_relative "priority_engulf_queue"
196
+ require_relative "pathname_from_message"
197
+ require_relative "display_invalid_blocks"
198
+ require_relative "parse_blocks_from_indent_line"
@@ -37,10 +37,20 @@ module DeadEnd
37
37
  @after_array = []
38
38
  @before_array = []
39
39
  @stop_after_kw = false
40
+
41
+ @skip_hidden = false
42
+ @skip_empty = false
40
43
  end
41
44
 
42
45
  def skip(name)
43
- @skip_array << name
46
+ case name
47
+ when :hidden?
48
+ @skip_hidden = true
49
+ when :empty?
50
+ @skip_empty = true
51
+ else
52
+ raise "Unsupported skip #{name}"
53
+ end
44
54
  self
45
55
  end
46
56
 
@@ -49,14 +59,15 @@ module DeadEnd
49
59
  self
50
60
  end
51
61
 
52
- def scan_while(&block)
62
+ def scan_while
53
63
  stop_next = false
54
64
 
55
65
  kw_count = 0
56
66
  end_count = 0
57
- @before_index = before_lines.reverse_each.take_while do |line|
67
+ index = before_lines.reverse_each.take_while do |line|
58
68
  next false if stop_next
59
- next true if @skip_array.detect { |meth| line.send(meth) }
69
+ next true if @skip_hidden && line.hidden?
70
+ next true if @skip_empty && line.empty?
60
71
 
61
72
  kw_count += 1 if line.is_kw?
62
73
  end_count += 1 if line.is_end?
@@ -64,15 +75,20 @@ module DeadEnd
64
75
  stop_next = true
65
76
  end
66
77
 
67
- block.call(line)
78
+ yield line
68
79
  end.last&.index
69
80
 
81
+ if index && index < before_index
82
+ @before_index = index
83
+ end
84
+
70
85
  stop_next = false
71
86
  kw_count = 0
72
87
  end_count = 0
73
- @after_index = after_lines.take_while do |line|
88
+ index = after_lines.take_while do |line|
74
89
  next false if stop_next
75
- next true if @skip_array.detect { |meth| line.send(meth) }
90
+ next true if @skip_hidden && line.hidden?
91
+ next true if @skip_empty && line.empty?
76
92
 
77
93
  kw_count += 1 if line.is_kw?
78
94
  end_count += 1 if line.is_end?
@@ -80,8 +96,12 @@ module DeadEnd
80
96
  stop_next = true
81
97
  end
82
98
 
83
- block.call(line)
99
+ yield line
84
100
  end.last&.index
101
+
102
+ if index && index > after_index
103
+ @after_index = index
104
+ end
85
105
  self
86
106
  end
87
107
 
@@ -178,7 +198,11 @@ module DeadEnd
178
198
  end
179
199
 
180
200
  def code_block
181
- CodeBlock.new(lines: @code_lines[before_index..after_index])
201
+ CodeBlock.new(lines: lines)
202
+ end
203
+
204
+ def lines
205
+ @code_lines[before_index..after_index]
182
206
  end
183
207
 
184
208
  def before_index
@@ -190,7 +214,7 @@ module DeadEnd
190
214
  end
191
215
 
192
216
  private def before_lines
193
- @code_lines[0...@orig_before_index] || []
217
+ @code_lines[0...before_index] || []
194
218
  end
195
219
 
196
220
  private def after_lines
data/lib/dead_end/auto.rb CHANGED
@@ -1,35 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../dead_end"
4
+ require_relative "core_ext"
4
5
 
5
- # Monkey patch kernel to ensure that all `require` calls call the same
6
- # method
7
- module Kernel
8
- module_function
9
-
10
- alias_method :dead_end_original_require, :require
11
- alias_method :dead_end_original_require_relative, :require_relative
12
- alias_method :dead_end_original_load, :load
13
-
14
- def load(file, wrap = false)
15
- dead_end_original_load(file)
16
- rescue SyntaxError => e
17
- DeadEnd.handle_error(e)
18
- end
19
-
20
- def require(file)
21
- dead_end_original_require(file)
22
- rescue SyntaxError => e
23
- DeadEnd.handle_error(e)
24
- end
25
-
26
- def require_relative(file)
27
- if Pathname.new(file).absolute?
28
- dead_end_original_require file
29
- else
30
- dead_end_original_require File.expand_path("../#{file}", Kernel.caller_locations(1, 1)[0].absolute_path)
31
- end
32
- rescue SyntaxError => e
33
- DeadEnd.handle_error(e)
34
- end
35
- end
6
+ warn "Calling `require 'dead_end/auto'` is deprecated, please `require 'dead_end'` instead."
@@ -36,7 +36,7 @@ module DeadEnd
36
36
  end
37
37
 
38
38
  def call(block)
39
- if (next_block = expand_neighbors(block, grab_empty: true))
39
+ if (next_block = expand_neighbors(block))
40
40
  return next_block
41
41
  end
42
42
 
@@ -51,25 +51,24 @@ module DeadEnd
51
51
  .code_block
52
52
  end
53
53
 
54
- def expand_neighbors(block, grab_empty: true)
55
- scan = AroundBlockScan.new(code_lines: @code_lines, block: block)
54
+ def expand_neighbors(block)
55
+ expanded_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
56
56
  .skip(:hidden?)
57
57
  .stop_after_kw
58
58
  .scan_neighbors
59
+ .scan_while { |line| line.empty? } # Slurp up empties
60
+ .lines
59
61
 
60
- # Slurp up empties
61
- if grab_empty
62
- scan = AroundBlockScan.new(code_lines: @code_lines, block: scan.code_block)
63
- .scan_while { |line| line.empty? || line.hidden? }
64
- end
65
-
66
- new_block = scan.code_block
67
-
68
- if block.lines == new_block.lines
62
+ if block.lines == expanded_lines
69
63
  nil
70
64
  else
71
- new_block
65
+ CodeBlock.new(lines: expanded_lines)
72
66
  end
73
67
  end
68
+
69
+ # Managable rspec errors
70
+ def inspect
71
+ "#<DeadEnd::CodeBlock:0x0000123843lol >"
72
+ end
74
73
  end
75
74
  end
@@ -18,11 +18,22 @@ module DeadEnd
18
18
  #
19
19
  class CodeBlock
20
20
  UNSET = Object.new.freeze
21
- attr_reader :lines
21
+ attr_reader :lines, :starts_at, :ends_at
22
22
 
23
23
  def initialize(lines: [])
24
24
  @lines = Array(lines)
25
25
  @valid = UNSET
26
+ @deleted = false
27
+ @starts_at = @lines.first.number
28
+ @ends_at = @lines.last.number
29
+ end
30
+
31
+ def delete
32
+ @deleted = true
33
+ end
34
+
35
+ def deleted?
36
+ @deleted
26
37
  end
27
38
 
28
39
  def visible_lines
@@ -41,14 +52,6 @@ module DeadEnd
41
52
  @lines.all?(&:hidden?)
42
53
  end
43
54
 
44
- def starts_at
45
- @starts_at ||= @lines.first&.line_number
46
- end
47
-
48
- def ends_at
49
- @ends_at ||= @lines.last&.line_number
50
- end
51
-
52
55
  # This is used for frontier ordering, we are searching from
53
56
  # the largest indentation to the smallest. This allows us to
54
57
  # populate an array with multiple code blocks then call `sort!`
@@ -50,18 +50,16 @@ module DeadEnd
50
50
  # CodeFrontier#detect_invalid_blocks
51
51
  #
52
52
  class CodeFrontier
53
- def initialize(code_lines:)
53
+ def initialize(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines))
54
54
  @code_lines = code_lines
55
- @frontier = InsertionSort.new
56
- @unvisited_lines = @code_lines.sort_by(&:indent_index)
57
- @visited_lines = {}
55
+ @unvisited = unvisited
56
+ @queue = PriorityEngulfQueue.new
58
57
 
59
- @has_run = false
60
58
  @check_next = true
61
59
  end
62
60
 
63
61
  def count
64
- @frontier.to_a.length
62
+ @queue.length
65
63
  end
66
64
 
67
65
  # Performance optimization
@@ -88,7 +86,7 @@ module DeadEnd
88
86
  # removed. By default it checks all blocks in present in
89
87
  # the frontier array, but can be used for arbitrary arrays
90
88
  # of codeblocks as well
91
- def holds_all_syntax_errors?(block_array = @frontier, can_cache: true)
89
+ def holds_all_syntax_errors?(block_array = @queue, can_cache: true)
92
90
  return false if can_cache && can_skip_check?
93
91
 
94
92
  without_lines = block_array.to_a.flat_map do |block|
@@ -103,23 +101,23 @@ module DeadEnd
103
101
 
104
102
  # Returns a code block with the largest indentation possible
105
103
  def pop
106
- @frontier.to_a.pop
104
+ @queue.pop
107
105
  end
108
106
 
109
107
  def next_indent_line
110
- @unvisited_lines.last
108
+ @unvisited.peek
111
109
  end
112
110
 
113
111
  def expand?
114
- return false if @frontier.to_a.empty?
115
- return true if @unvisited_lines.to_a.empty?
112
+ return false if @queue.empty?
113
+ return true if @unvisited.empty?
116
114
 
117
- frontier_indent = @frontier.to_a.last.current_indent
115
+ frontier_indent = @queue.peek.current_indent
118
116
  unvisited_indent = next_indent_line.indent
119
117
 
120
118
  if ENV["DEBUG"]
121
119
  puts "```"
122
- puts @frontier.to_a.last.to_s
120
+ puts @queue.peek.to_s
123
121
  puts "```"
124
122
  puts " @frontier indent: #{frontier_indent}"
125
123
  puts " @unvisited indent: #{unvisited_indent}"
@@ -129,33 +127,30 @@ module DeadEnd
129
127
  frontier_indent >= unvisited_indent
130
128
  end
131
129
 
130
+ # Keeps track of what lines have been added to blocks and which are not yet
131
+ # visited.
132
132
  def register_indent_block(block)
133
- block.lines.each do |line|
134
- next if @visited_lines[line]
135
- @visited_lines[line] = true
136
-
137
- index = @unvisited_lines.bsearch_index { |l| line.indent_index <=> l.indent_index }
138
- @unvisited_lines.delete_at(index)
139
- end
133
+ @unvisited.visit_block(block)
140
134
  self
141
135
  end
142
136
 
137
+ # When one element fully encapsulates another we remove the smaller
138
+ # block from the frontier. This prevents double expansions and all-around
139
+ # weird behavior. However this guarantee is quite expensive to maintain
140
+ def register_engulf_block(block)
141
+ end
142
+
143
143
  # Add a block to the frontier
144
144
  #
145
145
  # This method ensures the frontier always remains sorted (in indentation order)
146
146
  # and that each code block's lines are removed from the indentation hash so we
147
147
  # don't re-evaluate the same line multiple times.
148
148
  def <<(block)
149
- register_indent_block(block)
149
+ @unvisited.visit_block(block)
150
150
 
151
- # Make sure we don't double expand, if a code block fully engulfs another code block, keep the bigger one
152
- @frontier.to_a.reject! { |b|
153
- b.starts_at >= block.starts_at && b.ends_at <= block.ends_at
154
- }
151
+ @queue.push(block)
155
152
 
156
153
  @check_next = true if block.invalid?
157
- @frontier << block
158
- # @frontier.sort!
159
154
 
160
155
  self
161
156
  end
@@ -175,7 +170,7 @@ module DeadEnd
175
170
  # Given that we know our syntax error exists somewhere in our frontier, we want to find
176
171
  # the smallest possible set of blocks that contain all the syntax errors
177
172
  def detect_invalid_blocks
178
- self.class.combination(@frontier.to_a.select(&:invalid?)).detect do |block_array|
173
+ self.class.combination(@queue.to_a.select(&:invalid?)).detect do |block_array|
179
174
  holds_all_syntax_errors?(block_array, can_cache: false)
180
175
  end || []
181
176
  end