Support Symbol#to_proc block inference#452
Conversation
Co-authored-by: Codex <codex@openai.com>
|
I tried this branch against Redmine 6.1.1 with this script, and the analysis hangs (master finishes in under 20s). Could you take a look? |
| resolve(genv, changes) do |me, ty, mid, orig_ty| | ||
| if !me | ||
| if @node.is_a?(AST::YieldNode) && mid == :call && orig_ty.is_a?(Type::Symbol) | ||
| box = add_symbol_proc_call_box(changes, genv, orig_ty.sym, @a_args.positionals) |
There was a problem hiding this comment.
yield with keyword arguments is valid Ruby, e.g.:
def foo
yield 1, key: "v"
end
foo { |x, key:| puts "x=#{x}, key=#{key}" } # => x=1, key=vSo I think @a_args.keywords should also be passed through to add_symbol_proc_call_box here.
There was a problem hiding this comment.
Good catch — fixed in 7b8e9b0. @a_args.keywords is now forwarded, with a kwarg-yield scenario added.
Allocating a fresh ActualArguments / MethodCallBox on every parent re-run produced fresh `box.ret` vertex identities, which forced `typecheck`'s edge to be re-added/removed each cycle. The resulting on_type_added / on_type_removed re-scheduled the parent endlessly, hanging analysis on large codebases (e.g. Redmine 6.1.1 never terminates). Memoize the sub-box on the parent box itself, keyed by (recv, sym, *positionals), and tear it down in the parent's destroy. This keeps `box.ret` stable across re-runs so the change-set edges converge, matching the pattern already used by `@generics`.
`yield 1, key: "v"` is valid Ruby and, when received as `&:sym`, desugars to `1.sym(key: "v")`. The previous implementation dropped the keywords, leaving inference incomplete for symbol-proc blocks that take keyword arguments. Add a `caller_keywords` parameter to `add_symbol_proc_call_box` and pass `@a_args.keywords` from the yield path. Cover the behavior with a yield-with-kwargs scenario.
|
@sinsoku Reproduced the Redmine 6.1.1 hang locally. |
Summary
Fixes #414
Support
Symbol#to_procblocks such asmap(&:to_i).This treats a symbol passed as a block as a method call where the first yielded block argument is the receiver and the remaining yielded arguments are method arguments.
Changes
["1", "2"].map(&:to_i)asArray[Integer]yield