brakeman 5.0.4 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +49 -1
  3. data/README.md +1 -1
  4. data/bundle/load.rb +5 -4
  5. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/CHANGELOG.md +8 -0
  6. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/FAQ.md +0 -0
  7. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/Gemfile +0 -0
  8. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/MIT-LICENSE +0 -0
  9. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/README.md +19 -13
  10. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/REFERENCE.md +10 -3
  11. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/TODO +0 -0
  12. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/haml.gemspec +0 -0
  13. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/attribute_builder.rb +55 -0
  14. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/attribute_compiler.rb +4 -2
  15. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/attribute_parser.rb +0 -0
  16. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/buffer.rb +0 -56
  17. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/compiler.rb +0 -0
  18. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/engine.rb +0 -0
  19. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/error.rb +0 -0
  20. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/escapable.rb +0 -0
  21. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/exec.rb +0 -0
  22. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/filters.rb +0 -0
  23. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/generator.rb +0 -0
  24. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers/action_view_extensions.rb +0 -0
  25. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers/action_view_mods.rb +0 -0
  26. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers/action_view_xss_mods.rb +0 -0
  27. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers/safe_erubi_template.rb +0 -0
  28. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers/safe_erubis_template.rb +0 -0
  29. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers/xss_mods.rb +0 -0
  30. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/helpers.rb +0 -0
  31. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/options.rb +0 -0
  32. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/parser.rb +0 -0
  33. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/plugin.rb +18 -1
  34. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/railtie.rb +5 -0
  35. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/sass_rails_filter.rb +0 -0
  36. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/template/options.rb +0 -0
  37. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/template.rb +0 -0
  38. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/temple_engine.rb +2 -1
  39. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/temple_line_counter.rb +0 -0
  40. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/util.rb +0 -0
  41. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml/version.rb +1 -1
  42. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/lib/haml.rb +0 -0
  43. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/yard/default/fulldoc/html/css/common.sass +0 -0
  44. data/bundle/ruby/2.7.0/gems/{haml-5.2.1 → haml-5.2.2}/yard/default/layout/html/footer.erb +0 -0
  45. data/bundle/ruby/2.7.0/gems/parallel-1.21.0/MIT-LICENSE.txt +20 -0
  46. data/bundle/ruby/2.7.0/gems/parallel-1.21.0/lib/parallel/processor_count.rb +45 -0
  47. data/bundle/ruby/2.7.0/gems/parallel-1.21.0/lib/parallel/version.rb +4 -0
  48. data/bundle/ruby/2.7.0/gems/parallel-1.21.0/lib/parallel.rb +532 -0
  49. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/History.rdoc +88 -0
  50. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/Manifest.txt +3 -0
  51. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/README.rdoc +1 -0
  52. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/compare/normalize.rb +6 -1
  53. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/debugging.md +0 -0
  54. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/gauntlet.md +106 -0
  55. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/rp_extensions.rb +15 -36
  56. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/rp_stringscanner.rb +33 -0
  57. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby20_parser.rb +7128 -0
  58. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby20_parser.y +335 -252
  59. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby21_parser.rb +7182 -0
  60. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby21_parser.y +330 -249
  61. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby22_parser.rb +7228 -0
  62. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby22_parser.y +334 -251
  63. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby23_parser.rb +7237 -0
  64. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0/lib/ruby26_parser.y → ruby_parser-3.18.1/lib/ruby23_parser.y} +336 -276
  65. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby24_parser.rb +7268 -0
  66. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby24_parser.y +334 -251
  67. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby25_parser.rb +7268 -0
  68. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0/lib/ruby30_parser.y → ruby_parser-3.18.1/lib/ruby25_parser.y} +335 -304
  69. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby26_parser.rb +7287 -0
  70. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0/lib/ruby27_parser.y → ruby_parser-3.18.1/lib/ruby26_parser.y} +334 -288
  71. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby27_parser.rb +8517 -0
  72. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0/lib/ruby_parser.yy → ruby_parser-3.18.1/lib/ruby27_parser.y} +906 -380
  73. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby30_parser.rb +8751 -0
  74. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby30_parser.y +3472 -0
  75. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby3_parser.yy +3476 -0
  76. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby_lexer.rb +261 -609
  77. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby_lexer.rex +27 -20
  78. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby_lexer.rex.rb +59 -23
  79. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby_lexer_strings.rb +638 -0
  80. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby_parser.rb +0 -0
  81. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/lib/ruby_parser.yy +3487 -0
  82. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/lib/ruby_parser_extras.rb +296 -115
  83. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.16.0 → ruby_parser-3.18.1}/tools/munge.rb +34 -6
  84. data/bundle/ruby/2.7.0/gems/ruby_parser-3.18.1/tools/ripper.rb +44 -0
  85. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/History.rdoc +15 -0
  86. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/Manifest.txt +0 -0
  87. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/README.rdoc +0 -0
  88. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/composite_sexp_processor.rb +0 -0
  89. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/pt_testcase.rb +7 -2
  90. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/sexp.rb +19 -9
  91. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/sexp_matcher.rb +0 -0
  92. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/sexp_processor.rb +1 -1
  93. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/strict_sexp.rb +25 -3
  94. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.3 → sexp_processor-4.16.0}/lib/unique.rb +0 -0
  95. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/CHANGELOG.md +4 -0
  96. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/MIT-LICENSE.txt +0 -0
  97. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/README.md +1 -1
  98. data/bundle/ruby/2.7.0/gems/unicode-display_width-1.8.0/data/display_width.marshal.gz +0 -0
  99. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/lib/unicode/display_width/constants.rb +2 -2
  100. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/lib/unicode/display_width/index.rb +0 -0
  101. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/lib/unicode/display_width/no_string_ext.rb +0 -0
  102. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/lib/unicode/display_width/string_ext.rb +0 -0
  103. data/bundle/ruby/2.7.0/gems/{unicode-display_width-1.7.0 → unicode-display_width-1.8.0}/lib/unicode/display_width.rb +0 -0
  104. data/lib/brakeman/app_tree.rb +1 -1
  105. data/lib/brakeman/checks/base_check.rb +10 -0
  106. data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
  107. data/lib/brakeman/checks/check_eol_rails.rb +23 -0
  108. data/lib/brakeman/checks/check_eol_ruby.rb +26 -0
  109. data/lib/brakeman/checks/check_evaluation.rb +1 -1
  110. data/lib/brakeman/checks/check_execute.rb +10 -0
  111. data/lib/brakeman/checks/check_json_parsing.rb +1 -1
  112. data/lib/brakeman/checks/check_render.rb +15 -1
  113. data/lib/brakeman/checks/check_sql.rb +58 -7
  114. data/lib/brakeman/checks/check_symbol_dos.rb +1 -1
  115. data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
  116. data/lib/brakeman/checks/eol_check.rb +47 -0
  117. data/lib/brakeman/file_parser.rb +45 -15
  118. data/lib/brakeman/options.rb +15 -2
  119. data/lib/brakeman/processors/alias_processor.rb +91 -9
  120. data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
  121. data/lib/brakeman/processors/gem_processor.rb +3 -0
  122. data/lib/brakeman/processors/haml_template_processor.rb +9 -0
  123. data/lib/brakeman/processors/lib/call_conversion_helper.rb +12 -6
  124. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -0
  125. data/lib/brakeman/processors/library_processor.rb +9 -0
  126. data/lib/brakeman/processors/model_processor.rb +32 -0
  127. data/lib/brakeman/report/ignore/config.rb +1 -1
  128. data/lib/brakeman/report/ignore/interactive.rb +1 -1
  129. data/lib/brakeman/report/report_csv.rb +1 -1
  130. data/lib/brakeman/report/report_github.rb +31 -0
  131. data/lib/brakeman/report/report_sarif.rb +22 -3
  132. data/lib/brakeman/report/report_text.rb +1 -1
  133. data/lib/brakeman/report.rb +4 -1
  134. data/lib/brakeman/rescanner.rb +1 -1
  135. data/lib/brakeman/scanner.rb +19 -14
  136. data/lib/brakeman/tracker/collection.rb +57 -7
  137. data/lib/brakeman/tracker/config.rb +8 -1
  138. data/lib/brakeman/tracker/method_info.rb +70 -0
  139. data/lib/brakeman/tracker.rb +33 -4
  140. data/lib/brakeman/util.rb +34 -18
  141. data/lib/brakeman/version.rb +1 -1
  142. data/lib/brakeman/warning_codes.rb +2 -0
  143. data/lib/brakeman.rb +8 -2
  144. data/lib/ruby_parser/bm_sexp.rb +24 -0
  145. metadata +107 -95
  146. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/rp_stringscanner.rb +0 -64
  147. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby20_parser.rb +0 -7075
  148. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby21_parser.rb +0 -7148
  149. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby22_parser.rb +0 -7185
  150. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby23_parser.rb +0 -7199
  151. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby23_parser.y +0 -2643
  152. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby24_parser.rb +0 -7219
  153. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby25_parser.rb +0 -7218
  154. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby25_parser.y +0 -2651
  155. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby26_parser.rb +0 -7240
  156. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby27_parser.rb +0 -7358
  157. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby30_parser.rb +0 -7358
  158. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/tools/ripper.rb +0 -39
  159. data/bundle/ruby/2.7.0/gems/unicode-display_width-1.7.0/data/display_width.marshal.gz +0 -0
@@ -0,0 +1,532 @@
1
+ # frozen_string_literal: true
2
+ require 'rbconfig'
3
+ require 'parallel/version'
4
+ require 'parallel/processor_count'
5
+
6
+ module Parallel
7
+ extend ProcessorCount
8
+
9
+ Stop = Object.new.freeze
10
+
11
+ class DeadWorker < StandardError
12
+ end
13
+
14
+ class Break < StandardError
15
+ attr_reader :value
16
+
17
+ def initialize(value = nil)
18
+ super()
19
+ @value = value
20
+ end
21
+ end
22
+
23
+ class Kill < Break
24
+ end
25
+
26
+ class UndumpableException < StandardError
27
+ attr_reader :backtrace
28
+
29
+ def initialize(original)
30
+ super "#{original.class}: #{original.message}"
31
+ @backtrace = original.backtrace
32
+ end
33
+ end
34
+
35
+ class ExceptionWrapper
36
+ attr_reader :exception
37
+
38
+ def initialize(exception)
39
+ # Remove the bindings stack added by the better_errors gem,
40
+ # because it cannot be marshalled
41
+ if exception.instance_variable_defined? :@__better_errors_bindings_stack
42
+ exception.send :remove_instance_variable, :@__better_errors_bindings_stack
43
+ end
44
+
45
+ @exception =
46
+ begin
47
+ Marshal.dump(exception) && exception
48
+ rescue StandardError
49
+ UndumpableException.new(exception)
50
+ end
51
+ end
52
+ end
53
+
54
+ class Worker
55
+ attr_reader :pid, :read, :write
56
+ attr_accessor :thread
57
+
58
+ def initialize(read, write, pid)
59
+ @read = read
60
+ @write = write
61
+ @pid = pid
62
+ end
63
+
64
+ def stop
65
+ close_pipes
66
+ wait # if it goes zombie, rather wait here to be able to debug
67
+ end
68
+
69
+ # might be passed to started_processes and simultaneously closed by another thread
70
+ # when running in isolation mode, so we have to check if it is closed before closing
71
+ def close_pipes
72
+ read.close unless read.closed?
73
+ write.close unless write.closed?
74
+ end
75
+
76
+ def work(data)
77
+ begin
78
+ Marshal.dump(data, write)
79
+ rescue Errno::EPIPE
80
+ raise DeadWorker
81
+ end
82
+
83
+ result = begin
84
+ Marshal.load(read)
85
+ rescue EOFError
86
+ raise DeadWorker
87
+ end
88
+ raise result.exception if result.is_a?(ExceptionWrapper)
89
+ result
90
+ end
91
+
92
+ private
93
+
94
+ def wait
95
+ Process.wait(pid)
96
+ rescue Interrupt
97
+ # process died
98
+ end
99
+ end
100
+
101
+ class JobFactory
102
+ def initialize(source, mutex)
103
+ @lambda = (source.respond_to?(:call) && source) || queue_wrapper(source)
104
+ @source = source.to_a unless @lambda # turn Range and other Enumerable-s into an Array
105
+ @mutex = mutex
106
+ @index = -1
107
+ @stopped = false
108
+ end
109
+
110
+ def next
111
+ if producer?
112
+ # - index and item stay in sync
113
+ # - do not call lambda after it has returned Stop
114
+ item, index = @mutex.synchronize do
115
+ return if @stopped
116
+ item = @lambda.call
117
+ @stopped = (item == Stop)
118
+ return if @stopped
119
+ [item, @index += 1]
120
+ end
121
+ else
122
+ index = @mutex.synchronize { @index += 1 }
123
+ return if index >= size
124
+ item = @source[index]
125
+ end
126
+ [item, index]
127
+ end
128
+
129
+ def size
130
+ if producer?
131
+ Float::INFINITY
132
+ else
133
+ @source.size
134
+ end
135
+ end
136
+
137
+ # generate item that is sent to workers
138
+ # just index is faster + less likely to blow up with unserializable errors
139
+ def pack(item, index)
140
+ producer? ? [item, index] : index
141
+ end
142
+
143
+ # unpack item that is sent to workers
144
+ def unpack(data)
145
+ producer? ? data : [@source[data], data]
146
+ end
147
+
148
+ private
149
+
150
+ def producer?
151
+ @lambda
152
+ end
153
+
154
+ def queue_wrapper(array)
155
+ array.respond_to?(:num_waiting) && array.respond_to?(:pop) && -> { array.pop(false) }
156
+ end
157
+ end
158
+
159
+ class UserInterruptHandler
160
+ INTERRUPT_SIGNAL = :SIGINT
161
+
162
+ class << self
163
+ # kill all these pids or threads if user presses Ctrl+c
164
+ def kill_on_ctrl_c(pids, options)
165
+ @to_be_killed ||= []
166
+ old_interrupt = nil
167
+ signal = options.fetch(:interrupt_signal, INTERRUPT_SIGNAL)
168
+
169
+ if @to_be_killed.empty?
170
+ old_interrupt = trap_interrupt(signal) do
171
+ warn 'Parallel execution interrupted, exiting ...'
172
+ @to_be_killed.flatten.each { |pid| kill(pid) }
173
+ end
174
+ end
175
+
176
+ @to_be_killed << pids
177
+
178
+ yield
179
+ ensure
180
+ @to_be_killed.pop # do not kill pids that could be used for new processes
181
+ restore_interrupt(old_interrupt, signal) if @to_be_killed.empty?
182
+ end
183
+
184
+ def kill(thing)
185
+ Process.kill(:KILL, thing)
186
+ rescue Errno::ESRCH
187
+ # some linux systems already automatically killed the children at this point
188
+ # so we just ignore them not being there
189
+ end
190
+
191
+ private
192
+
193
+ def trap_interrupt(signal)
194
+ old = Signal.trap signal, 'IGNORE'
195
+
196
+ Signal.trap signal do
197
+ yield
198
+ if !old || old == "DEFAULT"
199
+ raise Interrupt
200
+ else
201
+ old.call
202
+ end
203
+ end
204
+
205
+ old
206
+ end
207
+
208
+ def restore_interrupt(old, signal)
209
+ Signal.trap signal, old
210
+ end
211
+ end
212
+ end
213
+
214
+ class << self
215
+ def in_threads(options = { count: 2 })
216
+ threads = []
217
+ count, = extract_count_from_options(options)
218
+
219
+ Thread.handle_interrupt(Exception => :never) do
220
+ Thread.handle_interrupt(Exception => :immediate) do
221
+ count.times do |i|
222
+ threads << Thread.new { yield(i) }
223
+ end
224
+ threads.map(&:value)
225
+ end
226
+ ensure
227
+ threads.each(&:kill)
228
+ end
229
+ end
230
+
231
+ def in_processes(options = {}, &block)
232
+ count, options = extract_count_from_options(options)
233
+ count ||= processor_count
234
+ map(0...count, options.merge(in_processes: count), &block)
235
+ end
236
+
237
+ def each(array, options = {}, &block)
238
+ map(array, options.merge(preserve_results: false), &block)
239
+ end
240
+
241
+ def any?(*args, &block)
242
+ raise "You must provide a block when calling #any?" if block.nil?
243
+ !each(*args) { |*a| raise Kill if block.call(*a) }
244
+ end
245
+
246
+ def all?(*args, &block)
247
+ raise "You must provide a block when calling #all?" if block.nil?
248
+ !!each(*args) { |*a| raise Kill unless block.call(*a) }
249
+ end
250
+
251
+ def each_with_index(array, options = {}, &block)
252
+ each(array, options.merge(with_index: true), &block)
253
+ end
254
+
255
+ def map(source, options = {}, &block)
256
+ options = options.dup
257
+ options[:mutex] = Mutex.new
258
+
259
+ if options[:in_processes] && options[:in_threads]
260
+ raise ArgumentError, "Please specify only one of `in_processes` or `in_threads`."
261
+ elsif RUBY_PLATFORM =~ (/java/) && !(options[:in_processes])
262
+ method = :in_threads
263
+ size = options[method] || processor_count
264
+ elsif options[:in_threads]
265
+ method = :in_threads
266
+ size = options[method]
267
+ else
268
+ method = :in_processes
269
+ if Process.respond_to?(:fork)
270
+ size = options[method] || processor_count
271
+ else
272
+ warn "Process.fork is not supported by this Ruby"
273
+ size = 0
274
+ end
275
+ end
276
+
277
+ job_factory = JobFactory.new(source, options[:mutex])
278
+ size = [job_factory.size, size].min
279
+
280
+ options[:return_results] = (options[:preserve_results] != false || !!options[:finish])
281
+ add_progress_bar!(job_factory, options)
282
+
283
+ result =
284
+ if size == 0
285
+ work_direct(job_factory, options, &block)
286
+ elsif method == :in_threads
287
+ work_in_threads(job_factory, options.merge(count: size), &block)
288
+ else
289
+ work_in_processes(job_factory, options.merge(count: size), &block)
290
+ end
291
+
292
+ return result.value if result.is_a?(Break)
293
+ raise result if result.is_a?(Exception)
294
+ options[:return_results] ? result : source
295
+ end
296
+
297
+ def map_with_index(array, options = {}, &block)
298
+ map(array, options.merge(with_index: true), &block)
299
+ end
300
+
301
+ def flat_map(*args, &block)
302
+ map(*args, &block).flatten(1)
303
+ end
304
+
305
+ def worker_number
306
+ Thread.current[:parallel_worker_number]
307
+ end
308
+
309
+ # TODO: this does not work when doing threads in forks, so should remove and yield the number instead if needed
310
+ def worker_number=(worker_num)
311
+ Thread.current[:parallel_worker_number] = worker_num
312
+ end
313
+
314
+ private
315
+
316
+ def add_progress_bar!(job_factory, options)
317
+ if progress_options = options[:progress]
318
+ raise "Progressbar can only be used with array like items" if job_factory.size == Float::INFINITY
319
+ require 'ruby-progressbar'
320
+
321
+ if progress_options == true
322
+ progress_options = { title: "Progress" }
323
+ elsif progress_options.respond_to? :to_str
324
+ progress_options = { title: progress_options.to_str }
325
+ end
326
+
327
+ progress_options = {
328
+ total: job_factory.size,
329
+ format: '%t |%E | %B | %a'
330
+ }.merge(progress_options)
331
+
332
+ progress = ProgressBar.create(progress_options)
333
+ old_finish = options[:finish]
334
+ options[:finish] = lambda do |item, i, result|
335
+ old_finish.call(item, i, result) if old_finish
336
+ progress.increment
337
+ end
338
+ end
339
+ end
340
+
341
+ def work_direct(job_factory, options, &block)
342
+ self.worker_number = 0
343
+ results = []
344
+ exception = nil
345
+ begin
346
+ while set = job_factory.next
347
+ item, index = set
348
+ results << with_instrumentation(item, index, options) do
349
+ call_with_index(item, index, options, &block)
350
+ end
351
+ end
352
+ rescue StandardError
353
+ exception = $!
354
+ end
355
+ exception || results
356
+ ensure
357
+ self.worker_number = nil
358
+ end
359
+
360
+ def work_in_threads(job_factory, options, &block)
361
+ raise "interrupt_signal is no longer supported for threads" if options[:interrupt_signal]
362
+ results = []
363
+ results_mutex = Mutex.new # arrays are not thread-safe on jRuby
364
+ exception = nil
365
+
366
+ in_threads(options) do |worker_num|
367
+ self.worker_number = worker_num
368
+ # as long as there are more jobs, work on one of them
369
+ while !exception && set = job_factory.next
370
+ begin
371
+ item, index = set
372
+ result = with_instrumentation item, index, options do
373
+ call_with_index(item, index, options, &block)
374
+ end
375
+ results_mutex.synchronize { results[index] = result }
376
+ rescue StandardError
377
+ exception = $!
378
+ end
379
+ end
380
+ end
381
+
382
+ exception || results
383
+ end
384
+
385
+ def work_in_processes(job_factory, options, &blk)
386
+ workers = create_workers(job_factory, options, &blk)
387
+ results = []
388
+ results_mutex = Mutex.new # arrays are not thread-safe
389
+ exception = nil
390
+
391
+ UserInterruptHandler.kill_on_ctrl_c(workers.map(&:pid), options) do
392
+ in_threads(options) do |i|
393
+ worker = workers[i]
394
+ worker.thread = Thread.current
395
+ worked = false
396
+
397
+ begin
398
+ loop do
399
+ break if exception
400
+ item, index = job_factory.next
401
+ break unless index
402
+
403
+ if options[:isolation]
404
+ worker = replace_worker(job_factory, workers, i, options, blk) if worked
405
+ worked = true
406
+ worker.thread = Thread.current
407
+ end
408
+
409
+ begin
410
+ result = with_instrumentation item, index, options do
411
+ worker.work(job_factory.pack(item, index))
412
+ end
413
+ results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
414
+ rescue StandardError => e
415
+ exception = e
416
+ if exception.is_a?(Kill)
417
+ (workers - [worker]).each do |w|
418
+ w.thread&.kill
419
+ UserInterruptHandler.kill(w.pid)
420
+ end
421
+ end
422
+ end
423
+ end
424
+ ensure
425
+ worker.stop
426
+ end
427
+ end
428
+ end
429
+ exception || results
430
+ end
431
+
432
+ def replace_worker(job_factory, workers, index, options, blk)
433
+ options[:mutex].synchronize do
434
+ # old worker is no longer used ... stop it
435
+ worker = workers[index]
436
+ worker.stop
437
+
438
+ # create a new replacement worker
439
+ running = workers - [worker]
440
+ workers[index] = worker(job_factory, options.merge(started_workers: running, worker_number: index), &blk)
441
+ end
442
+ end
443
+
444
+ def create_workers(job_factory, options, &block)
445
+ workers = []
446
+ Array.new(options[:count]).each_with_index do |_, i|
447
+ workers << worker(job_factory, options.merge(started_workers: workers, worker_number: i), &block)
448
+ end
449
+ workers
450
+ end
451
+
452
+ def worker(job_factory, options, &block)
453
+ child_read, parent_write = IO.pipe
454
+ parent_read, child_write = IO.pipe
455
+
456
+ pid = Process.fork do
457
+ self.worker_number = options[:worker_number]
458
+
459
+ begin
460
+ options.delete(:started_workers).each(&:close_pipes)
461
+
462
+ parent_write.close
463
+ parent_read.close
464
+
465
+ process_incoming_jobs(child_read, child_write, job_factory, options, &block)
466
+ ensure
467
+ child_read.close
468
+ child_write.close
469
+ end
470
+ end
471
+
472
+ child_read.close
473
+ child_write.close
474
+
475
+ Worker.new(parent_read, parent_write, pid)
476
+ end
477
+
478
+ def process_incoming_jobs(read, write, job_factory, options, &block)
479
+ until read.eof?
480
+ data = Marshal.load(read)
481
+ item, index = job_factory.unpack(data)
482
+
483
+ result =
484
+ begin
485
+ call_with_index(item, index, options, &block)
486
+ # https://github.com/rspec/rspec-support/blob/673133cdd13b17077b3d88ece8d7380821f8d7dc/lib/rspec/support.rb#L132-L140
487
+ rescue NoMemoryError, SignalException, Interrupt, SystemExit # rubocop:disable Lint/ShadowedException
488
+ raise $!
489
+ rescue Exception # # rubocop:disable Lint/RescueException
490
+ ExceptionWrapper.new($!)
491
+ end
492
+
493
+ begin
494
+ Marshal.dump(result, write)
495
+ rescue Errno::EPIPE
496
+ return # parent thread already dead
497
+ end
498
+ end
499
+ end
500
+
501
+ # options is either a Integer or a Hash with :count
502
+ def extract_count_from_options(options)
503
+ if options.is_a?(Hash)
504
+ count = options[:count]
505
+ else
506
+ count = options
507
+ options = {}
508
+ end
509
+ [count, options]
510
+ end
511
+
512
+ def call_with_index(item, index, options, &block)
513
+ args = [item]
514
+ args << index if options[:with_index]
515
+ results = block.call(*args)
516
+ if options[:return_results]
517
+ results
518
+ else
519
+ nil # avoid GC overhead of passing large results around
520
+ end
521
+ end
522
+
523
+ def with_instrumentation(item, index, options)
524
+ on_start = options[:start]
525
+ on_finish = options[:finish]
526
+ options[:mutex].synchronize { on_start.call(item, index) } if on_start
527
+ result = yield
528
+ options[:mutex].synchronize { on_finish.call(item, index, result) } if on_finish
529
+ result unless options[:preserve_results] == false
530
+ end
531
+ end
532
+ end
@@ -1,3 +1,91 @@
1
+ === 3.18.1 / 2021-11-10
2
+
3
+ * 1 minor enhancement:
4
+
5
+ * All parser tests are now explicitly testing line numbers at every level.
6
+
7
+ * 3 bug fixes:
8
+
9
+ * Fixed endless method with noargs. (mitsuru)
10
+ * Fixed line numbers on some yield forms.
11
+ * Handle and clearly report if unifdef is missing.
12
+
13
+ === 3.18.0 / 2021-10-27
14
+
15
+ Holy crap... 58 commits! 2.7 and 3.0 are feature complete. Strings
16
+ & heredocs have been rewritten.
17
+
18
+ * 9 major enhancements:
19
+
20
+ * !!! Rewrote lexer (and friends) for strings, heredocs, and %*[] constructs.
21
+ * Massive overhaul on line numbers.
22
+ * Freeze input! Finally!!! No more modifying the input string for heredocs.
23
+ * Overhauled RPStringScanner. Removed OLD compatibility methods!
24
+ * Removed Sexp methods: value, to_sym, add, add_all, node_type, values.
25
+ * value moved to sexp_processor.
26
+ * Removed String#grep monkey-patch.
27
+ * Removed String#lineno monkey-patch.
28
+ * Removed string_to_pos, charpos, etc hacks for ancient ruby versions.
29
+ * Removed unread_many... NO! NO EDITING THE INPUT STRING!
30
+
31
+ * 31 minor enhancements:
32
+
33
+ * 2.7/3.0: many more pattern edge cases
34
+ * 2.7: Added `mlhs = rhs rescue expr`
35
+ * 2.7: refactored destructured args (`|(k,v)|`) and unfactored(?!) case_body/args.
36
+ * 3.0: excessed_comma
37
+ * 3.0: finished most everything: endless methods, patterns, etc.
38
+ * 3.0: refactored / added new pattern changes
39
+ * Added RubyLexer#in_heredoc? (ie, is there old_ss ?)
40
+ * Added RubyLexer#old_ss and old_lineno and removed much of SSStack(ish).
41
+ * Added Symbol#end_with? when necessary
42
+ * Added TALLY and DEBUG options for ss.getch and ss.scan
43
+ * Added ignore_body_comments to make parser productions more clear.
44
+ * Added support for no_kwarg (eg `def f(**nil)`).
45
+ * Added support for no_kwarg in blocks (eg `f { |**nil| }`).
46
+ * Augmented generated parser files to have frozen_string_literal comments and fixed tests.
47
+ * Broke out 3.0 parser into its own to ease development.
48
+ * Bumped dependencies on sexp_processor and oedipus_lex.
49
+ * Clean generated 3.x files.
50
+ * Extracted all string scanner methods to their own module.
51
+ * Fixed some precedence decls.
52
+ * Implemented most of pattern matching for 2.7+.
53
+ * Improve lex_state= to report location in verbose debug mode.
54
+ * Made it easier to debug with a particular version of ruby via rake.
55
+ * Make sure ripper uses the same version of ruby we specified.
56
+ * Moved all string/heredoc/etc code to ruby_lexer_strings.rb
57
+ * Remove warning from newer bisons.
58
+ * Sprinkled in some frozen_string_literal, but mostly helped by oedipus bump.
59
+ * Switch to comparing against ruby binary since ripper is buggy.
60
+ * bugs task should try both bug*.rb and bad*.rb.
61
+ * endless methods
62
+ * f_any_kwrest refactoring.
63
+ * refactored defn/defs
64
+
65
+ * 15 bug fixes:
66
+
67
+ * Cleaned a bunch of old hacks. Initializing RubyLexer w/ Parser is cleaner now.
68
+ * Corrected some lex_state errors in process_token_keyword.
69
+ * Fixed ancient ruby2 change (use #lines) in ruby_parse_extract_error.
70
+ * Fixed bug where else without rescue only raises on 2.6+
71
+ * Fixed caller for getch and scan when DEBUG=1
72
+ * Fixed comments in the middle of message cascades.
73
+ * Fixed differences w/ symbol productions against ruby 2.7.
74
+ * Fixed dsym to use string_contents production.
75
+ * Fixed error in bdot2/3 in some edge cases. Fixed p_alt line.
76
+ * Fixed heredoc dedenting in the presence of empty lines. (mvz)
77
+ * Fixed some leading whitespace / comment processing
78
+ * Fixed up how class/module/defn/defs comments were collected.
79
+ * Overhauled ripper.rb to deal with buggy ripper w/ yydebug.
80
+ * Removed dsym from literal.
81
+ * Removed tUBANG lexeme but kept it distinct as a method name (eg: `def !@`).
82
+
83
+ === 3.17.0 / 2021-08-03
84
+
85
+ * 1 minor enhancement:
86
+
87
+ * Added support for arg forwarding (eg `def f(...); m(...); end`) (presidentbeef)
88
+
1
89
  === 3.16.0 / 2021-05-15
2
90
 
3
91
  * 1 major enhancement:
@@ -7,6 +7,7 @@ bin/ruby_parse
7
7
  bin/ruby_parse_extract_error
8
8
  compare/normalize.rb
9
9
  debugging.md
10
+ gauntlet.md
10
11
  lib/.document
11
12
  lib/rp_extensions.rb
12
13
  lib/rp_stringscanner.rb
@@ -28,9 +29,11 @@ lib/ruby27_parser.rb
28
29
  lib/ruby27_parser.y
29
30
  lib/ruby30_parser.rb
30
31
  lib/ruby30_parser.y
32
+ lib/ruby3_parser.yy
31
33
  lib/ruby_lexer.rb
32
34
  lib/ruby_lexer.rex
33
35
  lib/ruby_lexer.rex.rb
36
+ lib/ruby_lexer_strings.rb
34
37
  lib/ruby_parser.rb
35
38
  lib/ruby_parser.yy
36
39
  lib/ruby_parser_extras.rb
@@ -32,6 +32,7 @@ Tested against 801,039 files from the latest of all rubygems (as of 2013-05):
32
32
  * 1.8 parser is at 99.9739% accuracy, 3.651 sigma
33
33
  * 1.9 parser is at 99.9940% accuracy, 4.013 sigma
34
34
  * 2.0 parser is at 99.9939% accuracy, 4.008 sigma
35
+ * 2.6 parser is at 99.9972% accuracy, 4.191 sigma
35
36
 
36
37
  == FEATURES/PROBLEMS:
37
38
 
@@ -84,6 +84,7 @@ def munge s
84
84
 
85
85
  "' '", "tSPACE", # needs to be later to avoid bad hits
86
86
 
87
+ "%empty", "none", # newer bison
87
88
  "/* empty */", "none",
88
89
  /^\s*$/, "none",
89
90
 
@@ -140,6 +141,7 @@ def munge s
140
141
  '"do for block"', "kDO_BLOCK",
141
142
  '"do for condition"', "kDO_COND",
142
143
  '"do for lambda"', "kDO_LAMBDA",
144
+ "tLABEL", "kLABEL",
143
145
 
144
146
  # UGH
145
147
  "k_LINE__", "k__LINE__",
@@ -155,7 +157,10 @@ def munge s
155
157
  /\"(\w+) \(?modifier\)?\"/, proc { |x| "k#{$1.upcase}_MOD" },
156
158
  /\"(\w+)\"/, proc { |x| "k#{$1.upcase}" },
157
159
 
158
- /@(\d+)(\s+|$)/, "",
160
+ /\$?@(\d+)(\s+|$)/, "", # newer bison
161
+
162
+ # TODO: remove for 3.0 work:
163
+ "lex_ctxt ", "" # 3.0 production that's mostly noise right now
159
164
  ]
160
165
 
161
166
  renames.each_slice(2) do |(a, b)|