summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--parse.y15
-rw-r--r--test/ripper/test_parser_events.rb22
2 files changed, 33 insertions, 4 deletions
diff --git a/parse.y b/parse.y
index 9cba9caf56..b670f2f927 100644
--- a/parse.y
+++ b/parse.y
@@ -543,6 +543,8 @@ struct parser_params {
rb_ast_t *ast;
int node_id;
+ st_table *warn_duplicate_keys_table;
+
int max_numparam;
ID it_id;
@@ -14701,7 +14703,7 @@ static void
warn_duplicate_keys(struct parser_params *p, NODE *hash)
{
/* See https://bugs.ruby-lang.org/issues/20331 for discussion about what is warned. */
- st_table *literal_keys = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
+ p->warn_duplicate_keys_table = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
while (hash && RNODE_LIST(hash)->nd_next) {
NODE *head = RNODE_LIST(hash)->nd_head;
NODE *value = RNODE_LIST(hash)->nd_next;
@@ -14717,16 +14719,17 @@ warn_duplicate_keys(struct parser_params *p, NODE *hash)
if (nd_type_st_key_enable_p(head)) {
key = (st_data_t)head;
- if (st_delete(literal_keys, &key, &data)) {
+ if (st_delete(p->warn_duplicate_keys_table, &key, &data)) {
rb_warn2L(nd_line((NODE *)data),
"key %+"PRIsWARN" is duplicated and overwritten on line %d",
nd_value(p, head), WARN_I(nd_line(head)));
}
- st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
+ st_insert(p->warn_duplicate_keys_table, (st_data_t)key, (st_data_t)hash);
}
hash = next;
}
- st_free_table(literal_keys);
+ st_free_table(p->warn_duplicate_keys_table);
+ p->warn_duplicate_keys_table = NULL;
}
static NODE *
@@ -15612,6 +15615,10 @@ rb_ruby_parser_free(void *ptr)
rb_ast_free(p->ast);
}
+ if (p->warn_duplicate_keys_table) {
+ st_free_table(p->warn_duplicate_keys_table);
+ }
+
#ifndef RIPPER
if (p->tokens) {
rb_parser_ary_free(p, p->tokens);
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index 35264868b9..348e87696a 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -1765,4 +1765,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
end
end;
end
+
+ def test_return_out_of_warn_no_memory_leak
+ assert_no_memory_leak(%w(-rripper), "#{<<~'begin;'}", "#{<<~'end;'}", rss: true)
+ class MyRipper < Ripper
+ def initialize(src, &blk)
+ super(src)
+ @blk = blk
+ end
+
+ def warn(msg, *args) = @blk.call(msg)
+ end
+
+ def call_parse = MyRipper.new("{ a: 1, a: 2 }") { |msg| return msg }.parse
+
+ # Check that call_parse does warn
+ raise "call_parse should warn" unless call_parse
+ begin;
+ 500_000.times do
+ call_parse
+ end
+ end;
+ end
end if ripper_test