rubydex 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,209 @@
1
+ //! C API for method signature accessors
2
+
3
+ use crate::graph_api::{GraphPointer, with_graph};
4
+ use crate::location_api::{Location, create_location_for_uri_and_offset};
5
+ use libc::c_char;
6
+ use rubydex::model::declaration::Declaration;
7
+ use rubydex::model::definitions::{Definition, MethodDefinition, Parameter};
8
+ use rubydex::model::graph::Graph;
9
+ use rubydex::model::ids::DefinitionId;
10
+ use std::ffi::CString;
11
+ use std::ptr;
12
+
13
+ /// C-compatible enum representing the kind of a parameter.
14
+ #[repr(C)]
15
+ #[derive(Copy, Clone, Debug, PartialEq, Eq)]
16
+ pub enum ParameterKind {
17
+ RequiredPositional = 0,
18
+ OptionalPositional = 1,
19
+ RestPositional = 2,
20
+ Post = 3,
21
+ RequiredKeyword = 4,
22
+ OptionalKeyword = 5,
23
+ RestKeyword = 6,
24
+ Forward = 7,
25
+ Block = 8,
26
+ }
27
+
28
+ fn map_parameter_kind(param: &Parameter) -> ParameterKind {
29
+ match param {
30
+ Parameter::RequiredPositional(_) => ParameterKind::RequiredPositional,
31
+ Parameter::Post(_) => ParameterKind::Post,
32
+ Parameter::OptionalPositional(_) => ParameterKind::OptionalPositional,
33
+ Parameter::RestPositional(_) => ParameterKind::RestPositional,
34
+ Parameter::RequiredKeyword(_) => ParameterKind::RequiredKeyword,
35
+ Parameter::OptionalKeyword(_) => ParameterKind::OptionalKeyword,
36
+ Parameter::RestKeyword(_) => ParameterKind::RestKeyword,
37
+ Parameter::Block(_) => ParameterKind::Block,
38
+ Parameter::Forward(_) => ParameterKind::Forward,
39
+ }
40
+ }
41
+
42
+ /// C-compatible struct representing a single parameter with its name, kind, and location.
43
+ #[repr(C)]
44
+ pub struct ParameterEntry {
45
+ pub name: *const c_char,
46
+ pub location: *mut Location,
47
+ pub kind: ParameterKind,
48
+ }
49
+
50
+ /// C-compatible struct representing a single method signature (a list of parameters).
51
+ #[repr(C)]
52
+ pub struct SignatureEntry {
53
+ pub parameters: *mut ParameterEntry,
54
+ pub parameters_len: usize,
55
+ }
56
+
57
+ /// C-compatible array of signatures.
58
+ #[repr(C)]
59
+ pub struct SignatureArray {
60
+ pub items: *mut SignatureEntry,
61
+ pub len: usize,
62
+ }
63
+
64
+ /// Returns a newly allocated array of signatures for the given method definition id.
65
+ /// Caller must free the returned pointer with `rdx_definition_signatures_free`.
66
+ ///
67
+ /// # Safety
68
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
69
+ /// - `definition_id` must be a valid definition id.
70
+ ///
71
+ /// # Panics
72
+ /// Panics if `definition_id` does not exist or is not a `MethodDefinition`.
73
+ #[unsafe(no_mangle)]
74
+ pub unsafe extern "C" fn rdx_definition_signatures(pointer: GraphPointer, definition_id: u64) -> *mut SignatureArray {
75
+ with_graph(pointer, |graph| {
76
+ let def_id = DefinitionId::new(definition_id);
77
+ let Definition::Method(method_def) = graph.definitions().get(&def_id).expect("definition should exist") else {
78
+ panic!("expected a method definition");
79
+ };
80
+
81
+ let sig_entries = collect_method_signatures(graph, method_def);
82
+
83
+ let len = sig_entries.len();
84
+ let items_ptr = Box::into_raw(sig_entries.into_boxed_slice()).cast::<SignatureEntry>();
85
+
86
+ Box::into_raw(Box::new(SignatureArray { items: items_ptr, len }))
87
+ })
88
+ }
89
+
90
+ /// Helper: build signature entries from a `MethodDefinition`.
91
+ fn collect_method_signatures(graph: &Graph, method_def: &MethodDefinition) -> Vec<SignatureEntry> {
92
+ let uri_id = *method_def.uri_id();
93
+ let document = graph.documents().get(&uri_id).expect("document should exist");
94
+
95
+ method_def
96
+ .signatures()
97
+ .as_slice()
98
+ .iter()
99
+ .map(|sig| {
100
+ let param_entries: Vec<ParameterEntry> = sig
101
+ .iter()
102
+ .map(|param| {
103
+ let param_struct = param.inner();
104
+ let name = graph
105
+ .strings()
106
+ .get(param_struct.str())
107
+ .expect("parameter name string should exist");
108
+ let name_str = CString::new(name.as_str()).unwrap().into_raw().cast_const();
109
+
110
+ ParameterEntry {
111
+ name: name_str,
112
+ kind: map_parameter_kind(param),
113
+ location: create_location_for_uri_and_offset(graph, document, param_struct.offset()),
114
+ }
115
+ })
116
+ .collect();
117
+
118
+ let parameters_len = param_entries.len();
119
+ let parameters_ptr = Box::into_raw(param_entries.into_boxed_slice()).cast::<ParameterEntry>();
120
+
121
+ SignatureEntry {
122
+ parameters: parameters_ptr,
123
+ parameters_len,
124
+ }
125
+ })
126
+ .collect()
127
+ }
128
+
129
+ /// Returns signatures for a `MethodAliasDefinition` by following the alias chain.
130
+ /// Always returns a valid `SignatureArray` pointer (possibly with `len == 0`).
131
+ /// Errors during alias resolution (unresolved receivers, circular chains, etc.)
132
+ /// are silently ignored.
133
+ ///
134
+ /// # Safety
135
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
136
+ /// - `definition_id` must be a valid definition id.
137
+ ///
138
+ /// # Panics
139
+ /// Panics if `definition_id` does not exist or is not a `MethodAliasDefinition`.
140
+ #[unsafe(no_mangle)]
141
+ pub unsafe extern "C" fn rdx_method_alias_definition_signatures(
142
+ pointer: GraphPointer,
143
+ definition_id: u64,
144
+ ) -> *mut SignatureArray {
145
+ with_graph(pointer, |graph| {
146
+ let def_id = DefinitionId::new(definition_id);
147
+ let resolved = rubydex::query::follow_method_alias(graph, def_id);
148
+
149
+ let mut sig_entries: Vec<SignatureEntry> = Vec::new();
150
+
151
+ if let Ok(declaration_id) = resolved {
152
+ if let Some(Declaration::Method(method_def)) = graph.declarations().get(&declaration_id) {
153
+ for definition_id in method_def.definitions() {
154
+ if let Some(Definition::Method(method_definition)) = graph.definitions().get(definition_id) {
155
+ sig_entries.extend(collect_method_signatures(graph, method_definition));
156
+ }
157
+ }
158
+ } else {
159
+ panic!("expected a method declaration");
160
+ }
161
+ }
162
+
163
+ let mut boxed = sig_entries.into_boxed_slice();
164
+ let len = boxed.len();
165
+ let items_ptr = boxed.as_mut_ptr();
166
+ std::mem::forget(boxed);
167
+
168
+ Box::into_raw(Box::new(SignatureArray { items: items_ptr, len }))
169
+ })
170
+ }
171
+
172
+ /// Frees a `SignatureArray` previously returned by `rdx_definition_signatures`.
173
+ ///
174
+ /// # Safety
175
+ /// - `ptr` must be a valid pointer previously returned by `rdx_definition_signatures`.
176
+ /// - `ptr` must not be used after being freed.
177
+ #[unsafe(no_mangle)]
178
+ pub unsafe extern "C" fn rdx_definition_signatures_free(ptr: *mut SignatureArray) {
179
+ if ptr.is_null() {
180
+ return;
181
+ }
182
+
183
+ let arr = unsafe { Box::from_raw(ptr) };
184
+
185
+ if arr.items.is_null() || arr.len == 0 {
186
+ return;
187
+ }
188
+
189
+ let slice_ptr = ptr::slice_from_raw_parts_mut(arr.items, arr.len);
190
+ let sig_slice: Box<[SignatureEntry]> = unsafe { Box::from_raw(slice_ptr) };
191
+
192
+ for sig_entry in &*sig_slice {
193
+ if sig_entry.parameters.is_null() || sig_entry.parameters_len == 0 {
194
+ continue;
195
+ }
196
+
197
+ let param_slice_ptr = ptr::slice_from_raw_parts_mut(sig_entry.parameters, sig_entry.parameters_len);
198
+ let param_slice: Box<[ParameterEntry]> = unsafe { Box::from_raw(param_slice_ptr) };
199
+
200
+ for param_entry in &*param_slice {
201
+ if !param_entry.name.is_null() {
202
+ drop(unsafe { CString::from_raw(param_entry.name.cast_mut()) });
203
+ }
204
+ if !param_entry.location.is_null() {
205
+ unsafe { crate::location_api::rdx_location_free(param_entry.location) };
206
+ }
207
+ }
208
+ }
209
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubydex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-01 00:00:00.000000000 Z
11
+ date: 2026-05-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A high-performance static analysis suite for Ruby, built in Rust with
14
14
  Ruby APIs
@@ -41,6 +41,8 @@ files:
41
41
  - ext/rubydex/reference.c
42
42
  - ext/rubydex/reference.h
43
43
  - ext/rubydex/rubydex.c
44
+ - ext/rubydex/signature.c
45
+ - ext/rubydex/signature.h
44
46
  - ext/rubydex/utils.c
45
47
  - ext/rubydex/utils.h
46
48
  - lib/rubydex.rb
@@ -53,6 +55,7 @@ files:
53
55
  - lib/rubydex/keyword_parameter.rb
54
56
  - lib/rubydex/location.rb
55
57
  - lib/rubydex/mixin.rb
58
+ - lib/rubydex/signature.rb
56
59
  - lib/rubydex/version.rb
57
60
  - rbi/rubydex.rbi
58
61
  - rust/Cargo.lock
@@ -77,6 +80,7 @@ files:
77
80
  - rust/rubydex-sys/src/location_api.rs
78
81
  - rust/rubydex-sys/src/name_api.rs
79
82
  - rust/rubydex-sys/src/reference_api.rs
83
+ - rust/rubydex-sys/src/signature_api.rs
80
84
  - rust/rubydex-sys/src/utils.rs
81
85
  - rust/rubydex/Cargo.toml
82
86
  - rust/rubydex/src/compile_assertions.rs