Skip to content

Add MethodDefinition#signatures to Ruby API#697

Open
soutaro wants to merge 27 commits intomainfrom
soutaro-definition-signatures
Open

Add MethodDefinition#signatures to Ruby API#697
soutaro wants to merge 27 commits intomainfrom
soutaro-definition-signatures

Conversation

@soutaro
Copy link
Copy Markdown
Contributor

@soutaro soutaro commented Mar 26, 2026

Summary

  • Expose method signature information through MethodDefinition#signatures, returning an array of Rubydex::Signature objects
  • Each Signature holds an array of typed Parameter subclasses (PositionalParameter, KeywordParameter, etc.) with name and location attributes
  • Add Rust C API (ParameterKind, SignatureEntry, SignatureArray) and C extension glue for conversion

Extracted from #686.

Test plan

  • Tests cover all 8 parameter kinds (positional, optional, rest, keyword, optional keyword, rest keyword, block, forward)
  • Tests cover RBS-defined signatures, overloads, untyped parameters, and no-parameter methods

🤖 Generated with Claude Code

@soutaro soutaro requested a review from a team as a code owner March 26, 2026 05:34
@soutaro soutaro self-assigned this Mar 26, 2026
soutaro added a commit that referenced this pull request Mar 27, 2026
- Rename ParameterKind variants to full names (e.g. Req -> RequiredPositional)
- Merge two let-else bindings into one in rdx_definition_signatures
- Make collect_method_signatures private
- Change Signature#initialize from keyword arguments to positional arguments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@soutaro soutaro force-pushed the soutaro-definition-signatures branch from 79877ae to 39084db Compare March 31, 2026 08:58
}

VALUE rdxi_signatures_to_ruby(SignatureArray *arr, VALUE graph_obj, VALUE default_method_def) {
if (arr == NULL || arr->len == 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we raise in rdxr_method_definition_signatures and rdxr_method_alias_definition_signatures if the pointer is NULL?

This should never really happen, so I'm trying to avoid having it fail silently.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the design is weird. Will take a look at this tonight.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the implementation. rdx_method_definition_signatures and rdx_method_alias_definition_signatures always return an array. rdxi_signatures_to_ruby can safely skips checking if arr is NULL.

let mut boxed = sig_entries.into_boxed_slice();
let len = boxed.len();
let items_ptr = boxed.as_mut_ptr();
std::mem::forget(boxed);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to forget here if we're ultimately passing this to Box::into_raw?

Copy link
Copy Markdown
Contributor Author

@soutaro soutaro Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't use forget, the contents of the Box will be deallocated by the destructor.
We could use Box::into_raw instead, but I don't think it's worth the rewrite since the mem::forget pattern is already used elsewhere in the codebase.

let items_ptr = Box::into_raw(boxed) as *mut SignatureEntry;

ParameterEntry param_entry = sig_entry.parameters[j];

VALUE param_class = parameter_class_for_kind(param_entry.kind);
VALUE name_sym = ID2SYM(rb_intern(param_entry.name));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does rb_intern make a copy of the string we pass? I'm asking because we free the signature array object further below. If Ruby interning just keeps a pointer, it could lead to a seg fault.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe rb_intern copies the string, but I couldn't find any documentation that explicitly says so. Changed to rb_str_intern with rb_utf8_str_new_cstr, which creates dynamic symbols (subject to GC) from a copied C string.

///
/// Panics if the definition or its document does not exist.
#[must_use]
pub fn source_at(&self, definition_id: &DefinitionId) -> &str {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move this to Offset and pass the document as we do with https://github.com/Shopify/rubydex/blob/main/rust/rubydex/src/offset.rs#L90-L103

@soutaro soutaro force-pushed the soutaro-definition-signatures branch from b33e8c6 to e0b3e8a Compare April 8, 2026 04:48
}

/// Returns a newly allocated array of signatures for the given method definition id.
/// Returns NULL if the definition is not a method definition.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it panics in this case?

end
end

def test_method_definition_signatures_with_various_parameter_kinds
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we include test cases for singleton methods?

@soutaro soutaro force-pushed the soutaro-definition-signatures branch from eef4e19 to aeeebb3 Compare April 13, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants