diff options
-rw-r--r-- | parse.y | 15 | ||||
-rw-r--r-- | test/ripper/test_parser_events.rb | 22 |
2 files changed, 33 insertions, 4 deletions
@@ -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 |