rubydex 0.1.0.beta12-aarch64-linux

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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +23 -0
  3. data/README.md +125 -0
  4. data/THIRD_PARTY_LICENSES.html +4562 -0
  5. data/exe/rdx +47 -0
  6. data/ext/rubydex/declaration.c +453 -0
  7. data/ext/rubydex/declaration.h +23 -0
  8. data/ext/rubydex/definition.c +284 -0
  9. data/ext/rubydex/definition.h +28 -0
  10. data/ext/rubydex/diagnostic.c +6 -0
  11. data/ext/rubydex/diagnostic.h +11 -0
  12. data/ext/rubydex/document.c +97 -0
  13. data/ext/rubydex/document.h +10 -0
  14. data/ext/rubydex/extconf.rb +138 -0
  15. data/ext/rubydex/graph.c +681 -0
  16. data/ext/rubydex/graph.h +10 -0
  17. data/ext/rubydex/handle.h +44 -0
  18. data/ext/rubydex/location.c +22 -0
  19. data/ext/rubydex/location.h +15 -0
  20. data/ext/rubydex/reference.c +123 -0
  21. data/ext/rubydex/reference.h +15 -0
  22. data/ext/rubydex/rubydex.c +22 -0
  23. data/ext/rubydex/utils.c +108 -0
  24. data/ext/rubydex/utils.h +34 -0
  25. data/lib/rubydex/3.2/rubydex.so +0 -0
  26. data/lib/rubydex/3.3/rubydex.so +0 -0
  27. data/lib/rubydex/3.4/rubydex.so +0 -0
  28. data/lib/rubydex/4.0/rubydex.so +0 -0
  29. data/lib/rubydex/comment.rb +17 -0
  30. data/lib/rubydex/diagnostic.rb +21 -0
  31. data/lib/rubydex/failures.rb +15 -0
  32. data/lib/rubydex/graph.rb +98 -0
  33. data/lib/rubydex/keyword.rb +17 -0
  34. data/lib/rubydex/keyword_parameter.rb +13 -0
  35. data/lib/rubydex/librubydex_sys.so +0 -0
  36. data/lib/rubydex/location.rb +90 -0
  37. data/lib/rubydex/mixin.rb +22 -0
  38. data/lib/rubydex/version.rb +5 -0
  39. data/lib/rubydex.rb +23 -0
  40. data/rbi/rubydex.rbi +422 -0
  41. data/rust/Cargo.lock +1851 -0
  42. data/rust/Cargo.toml +29 -0
  43. data/rust/about.hbs +78 -0
  44. data/rust/about.toml +10 -0
  45. data/rust/rubydex/Cargo.toml +42 -0
  46. data/rust/rubydex/src/compile_assertions.rs +13 -0
  47. data/rust/rubydex/src/diagnostic.rs +110 -0
  48. data/rust/rubydex/src/errors.rs +28 -0
  49. data/rust/rubydex/src/indexing/local_graph.rs +224 -0
  50. data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -0
  51. data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -0
  52. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
  53. data/rust/rubydex/src/indexing.rs +210 -0
  54. data/rust/rubydex/src/integrity.rs +279 -0
  55. data/rust/rubydex/src/job_queue.rs +205 -0
  56. data/rust/rubydex/src/lib.rs +17 -0
  57. data/rust/rubydex/src/listing.rs +371 -0
  58. data/rust/rubydex/src/main.rs +160 -0
  59. data/rust/rubydex/src/model/built_in.rs +83 -0
  60. data/rust/rubydex/src/model/comment.rs +24 -0
  61. data/rust/rubydex/src/model/declaration.rs +671 -0
  62. data/rust/rubydex/src/model/definitions.rs +1682 -0
  63. data/rust/rubydex/src/model/document.rs +222 -0
  64. data/rust/rubydex/src/model/encoding.rs +22 -0
  65. data/rust/rubydex/src/model/graph.rs +3754 -0
  66. data/rust/rubydex/src/model/id.rs +110 -0
  67. data/rust/rubydex/src/model/identity_maps.rs +58 -0
  68. data/rust/rubydex/src/model/ids.rs +60 -0
  69. data/rust/rubydex/src/model/keywords.rs +256 -0
  70. data/rust/rubydex/src/model/name.rs +298 -0
  71. data/rust/rubydex/src/model/references.rs +111 -0
  72. data/rust/rubydex/src/model/string_ref.rs +50 -0
  73. data/rust/rubydex/src/model/visibility.rs +41 -0
  74. data/rust/rubydex/src/model.rs +15 -0
  75. data/rust/rubydex/src/offset.rs +147 -0
  76. data/rust/rubydex/src/position.rs +6 -0
  77. data/rust/rubydex/src/query.rs +1841 -0
  78. data/rust/rubydex/src/resolution.rs +6517 -0
  79. data/rust/rubydex/src/stats/memory.rs +71 -0
  80. data/rust/rubydex/src/stats/orphan_report.rs +264 -0
  81. data/rust/rubydex/src/stats/timer.rs +127 -0
  82. data/rust/rubydex/src/stats.rs +11 -0
  83. data/rust/rubydex/src/test_utils/context.rs +226 -0
  84. data/rust/rubydex/src/test_utils/graph_test.rs +730 -0
  85. data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -0
  86. data/rust/rubydex/src/test_utils.rs +52 -0
  87. data/rust/rubydex/src/visualization/dot.rs +192 -0
  88. data/rust/rubydex/src/visualization.rs +6 -0
  89. data/rust/rubydex/tests/cli.rs +185 -0
  90. data/rust/rubydex-mcp/Cargo.toml +28 -0
  91. data/rust/rubydex-mcp/src/main.rs +48 -0
  92. data/rust/rubydex-mcp/src/server.rs +1145 -0
  93. data/rust/rubydex-mcp/src/tools.rs +49 -0
  94. data/rust/rubydex-mcp/tests/mcp.rs +302 -0
  95. data/rust/rubydex-sys/Cargo.toml +20 -0
  96. data/rust/rubydex-sys/build.rs +14 -0
  97. data/rust/rubydex-sys/cbindgen.toml +12 -0
  98. data/rust/rubydex-sys/src/declaration_api.rs +485 -0
  99. data/rust/rubydex-sys/src/definition_api.rs +443 -0
  100. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
  101. data/rust/rubydex-sys/src/document_api.rs +85 -0
  102. data/rust/rubydex-sys/src/graph_api.rs +948 -0
  103. data/rust/rubydex-sys/src/lib.rs +79 -0
  104. data/rust/rubydex-sys/src/location_api.rs +79 -0
  105. data/rust/rubydex-sys/src/name_api.rs +135 -0
  106. data/rust/rubydex-sys/src/reference_api.rs +267 -0
  107. data/rust/rubydex-sys/src/utils.rs +70 -0
  108. data/rust/rustfmt.toml +2 -0
  109. metadata +159 -0
@@ -0,0 +1,79 @@
1
+ /// Helper macro to generate all required functions for an iterator. We use iterators to go over any collection of data
2
+ /// that exists on the Rust side (e.g.: definitions, documents, declarations, references). The goal is to avoid eager
3
+ /// allocation of large collections when possible.
4
+ ///
5
+ /// Note: structs must be defined manually so that the cbindgen can see them. The actual methods are not extern "C" and
6
+ /// so they can be expanded from the macro.
7
+ ///
8
+ /// # Example
9
+ ///
10
+ /// ```ignore
11
+ /// pub struct FoosIter {
12
+ /// entries: Box<[Foo]>,
13
+ /// index: usize,
14
+ /// }
15
+ ///
16
+ /// iterator!(FoosIter, entries: Foo);
17
+ /// ```
18
+ macro_rules! iterator {
19
+ ($name:ident, $field:ident : $entry:ty) => {
20
+ impl $name {
21
+ #[must_use]
22
+ pub fn new($field: Box<[$entry]>) -> *mut $name {
23
+ Box::into_raw(Box::new($name { $field, index: 0 }))
24
+ }
25
+
26
+ /// # Safety
27
+ /// `iter` must be a valid pointer returned by `new`, or null.
28
+ pub unsafe fn len(iter: *const Self) -> usize {
29
+ if iter.is_null() {
30
+ return 0;
31
+ }
32
+ unsafe { (&*iter).$field.len() }
33
+ }
34
+
35
+ /// # Safety
36
+ /// - `iter` must be a valid pointer returned by `new`, or null.
37
+ /// - `out` must be a valid, writable pointer, or null.
38
+ pub unsafe fn next(iter: *mut Self, out: *mut $entry) -> bool {
39
+ if iter.is_null() || out.is_null() {
40
+ return false;
41
+ }
42
+
43
+ let it = unsafe { &mut *iter };
44
+ if it.index >= it.$field.len() {
45
+ return false;
46
+ }
47
+
48
+ let entry = it.$field[it.index];
49
+ it.index += 1;
50
+ unsafe {
51
+ *out = entry;
52
+ }
53
+
54
+ true
55
+ }
56
+
57
+ /// # Safety
58
+ /// `iter` must be a pointer returned by `new` (or null). Must not be used after.
59
+ pub unsafe fn free(iter: *mut Self) {
60
+ if iter.is_null() {
61
+ return;
62
+ }
63
+ unsafe {
64
+ let _ = Box::from_raw(iter);
65
+ }
66
+ }
67
+ }
68
+ };
69
+ }
70
+
71
+ pub mod declaration_api;
72
+ pub mod definition_api;
73
+ pub mod diagnostic_api;
74
+ pub mod document_api;
75
+ pub mod graph_api;
76
+ pub mod location_api;
77
+ pub mod name_api;
78
+ pub mod reference_api;
79
+ pub mod utils;
@@ -0,0 +1,79 @@
1
+ //! Location-related C API and structs
2
+
3
+ use libc::c_char;
4
+ use rubydex::model::document::Document;
5
+ use rubydex::model::graph::Graph;
6
+ use rubydex::offset::Offset;
7
+ use std::ffi::CString;
8
+
9
+ /// C-compatible struct representing a definition location with offsets and line/column positions.
10
+ #[repr(C)]
11
+ #[derive(Debug, Clone)]
12
+ pub struct Location {
13
+ pub uri: *const c_char,
14
+ pub start_line: u32,
15
+ pub end_line: u32,
16
+ pub start_column: u32,
17
+ pub end_column: u32,
18
+ }
19
+
20
+ /// Helper to create a location for a given URI and byte-offset range.
21
+ /// Allocates and returns a pointer to `Location`. Caller must free with `rdx_location_free`.
22
+ ///
23
+ /// # Panics
24
+ ///
25
+ /// - If the URI cannot be converted to a file path.
26
+ /// - If the file cannot be read.
27
+ /// - If the offset cannot be converted to a position.
28
+ #[must_use]
29
+ pub(crate) fn create_location_for_uri_and_offset(graph: &Graph, document: &Document, offset: &Offset) -> *mut Location {
30
+ let line_index = document.line_index();
31
+ let start_pos = line_index.line_col(offset.start().into());
32
+ let end_pos = line_index.line_col(offset.end().into());
33
+
34
+ let loc = if let Some(wide_encoding) = graph.encoding().to_wide() {
35
+ let wide_start_pos = line_index.to_wide(wide_encoding, start_pos).unwrap();
36
+ let wide_end_pos = line_index.to_wide(wide_encoding, end_pos).unwrap();
37
+
38
+ Location {
39
+ uri: CString::new(document.uri()).unwrap().into_raw().cast_const(),
40
+ start_line: wide_start_pos.line,
41
+ end_line: wide_end_pos.line,
42
+ start_column: wide_start_pos.col,
43
+ end_column: wide_end_pos.col,
44
+ }
45
+ } else {
46
+ Location {
47
+ uri: CString::new(document.uri()).unwrap().into_raw().cast_const(),
48
+ start_line: start_pos.line,
49
+ end_line: end_pos.line,
50
+ start_column: start_pos.col,
51
+ end_column: end_pos.col,
52
+ }
53
+ };
54
+
55
+ Box::into_raw(Box::new(loc))
56
+ }
57
+
58
+ /// Frees a `Location` struct and its owned inner strings.
59
+ ///
60
+ /// # Safety
61
+ ///
62
+ /// - `ptr` must be a valid pointer previously returned by `create_location_for_uri_and_offset`.
63
+ /// - `ptr` must not be used after being freed.
64
+ #[unsafe(no_mangle)]
65
+ pub unsafe extern "C" fn rdx_location_free(ptr: *mut Location) {
66
+ if ptr.is_null() {
67
+ return;
68
+ }
69
+ unsafe {
70
+ // Take ownership of the box so we can free inner allocations first
71
+ let boxed = Box::from_raw(ptr);
72
+
73
+ if !boxed.uri.is_null() {
74
+ let _ = CString::from_raw(boxed.uri.cast_mut());
75
+ }
76
+
77
+ // Box drops here, freeing the struct memory
78
+ }
79
+ }
@@ -0,0 +1,135 @@
1
+ use rubydex::model::{
2
+ graph::Graph,
3
+ ids::NameId,
4
+ name::{Name, ParentScope},
5
+ };
6
+
7
+ /// Takes a constant name and a nesting stack (e.g.: `["Foo", "Bar::Baz", "Qux"]`) and transforms it into a `NameId`,
8
+ /// registering each required part in the graph. Returns the `NameId` and a list of name ids that need to be untracked
9
+ /// afterwards
10
+ ///
11
+ /// # Panics
12
+ ///
13
+ /// Should not panic because `const_name` will always be turned into a name
14
+ pub fn nesting_stack_to_name_id(graph: &mut Graph, const_name: &str, nesting: Vec<String>) -> (NameId, Vec<NameId>) {
15
+ let mut current_nesting = None;
16
+ let mut current_name = ParentScope::None;
17
+ let mut names_to_untrack = Vec::new();
18
+
19
+ for entry in nesting {
20
+ for part in entry.split("::").map(String::from) {
21
+ if part.is_empty() {
22
+ current_name = ParentScope::TopLevel;
23
+ continue;
24
+ }
25
+
26
+ let str_id = graph.intern_string(part);
27
+ let name_id = graph.add_name(Name::new(str_id, current_name, current_nesting));
28
+ names_to_untrack.push(name_id);
29
+ current_name = ParentScope::Some(name_id);
30
+ }
31
+
32
+ current_nesting = current_name.map_or(None, |id| Some(*id));
33
+ current_name = ParentScope::None;
34
+ }
35
+
36
+ for part in const_name.split("::").map(String::from) {
37
+ if part.is_empty() {
38
+ current_name = ParentScope::TopLevel;
39
+ continue;
40
+ }
41
+
42
+ let str_id = graph.intern_string(part);
43
+ let name_id = graph.add_name(Name::new(str_id, current_name, current_nesting));
44
+ names_to_untrack.push(name_id);
45
+ current_name = ParentScope::Some(name_id);
46
+ }
47
+
48
+ (
49
+ current_name.expect("The NameId cannot be None since it contains at least `const_name`"),
50
+ names_to_untrack,
51
+ )
52
+ }
53
+
54
+ #[cfg(test)]
55
+ mod tests {
56
+ use rubydex::model::ids::StringId;
57
+
58
+ use super::*;
59
+
60
+ #[test]
61
+ fn nesting_is_converted_to_name_id() {
62
+ let mut graph = Graph::new();
63
+
64
+ let (name_id, _) = nesting_stack_to_name_id(
65
+ &mut graph,
66
+ "Some::CONST",
67
+ vec!["Foo".into(), "Bar::Zip".into(), "Qux".into()],
68
+ );
69
+
70
+ let const_name = graph.names().get(&name_id).unwrap();
71
+ assert_eq!(StringId::from("CONST"), *const_name.str());
72
+
73
+ let some_name = graph
74
+ .names()
75
+ .get(&const_name.parent_scope().expect("Parent scope should exist"))
76
+ .unwrap();
77
+ assert_eq!(StringId::from("Some"), *some_name.str());
78
+ assert_eq!(const_name.nesting(), some_name.nesting());
79
+
80
+ let qux_name = graph.names().get(&some_name.nesting().unwrap()).unwrap();
81
+ assert_eq!(StringId::from("Qux"), *qux_name.str());
82
+ assert!(qux_name.parent_scope().is_none());
83
+
84
+ let zip_name = graph.names().get(&qux_name.nesting().unwrap()).unwrap();
85
+ assert_eq!(StringId::from("Zip"), *zip_name.str());
86
+
87
+ let bar_name = graph
88
+ .names()
89
+ .get(&zip_name.parent_scope().expect("Parent scope should exist"))
90
+ .unwrap();
91
+ assert_eq!(StringId::from("Bar"), *bar_name.str());
92
+ assert_eq!(zip_name.nesting(), bar_name.nesting());
93
+
94
+ let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
95
+ assert_eq!(StringId::from("Foo"), *foo_name.str());
96
+ assert!(foo_name.parent_scope().is_none());
97
+ assert!(foo_name.nesting().is_none());
98
+ }
99
+
100
+ #[test]
101
+ fn top_level_reference_is_converted_to_name_id() {
102
+ let mut graph = Graph::new();
103
+
104
+ let (name_id, _) = nesting_stack_to_name_id(&mut graph, "::CONST", vec!["Foo".into()]);
105
+
106
+ let const_name = graph.names().get(&name_id).unwrap();
107
+ assert_eq!(StringId::from("CONST"), *const_name.str());
108
+ assert!(const_name.parent_scope().is_top_level());
109
+
110
+ let foo_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
111
+ assert_eq!(StringId::from("Foo"), *foo_name.str());
112
+ assert!(foo_name.nesting().is_none());
113
+ assert!(foo_name.parent_scope().is_none());
114
+ }
115
+
116
+ #[test]
117
+ fn top_level_nesting_is_converted_to_name_id() {
118
+ let mut graph = Graph::new();
119
+
120
+ let (name_id, _) = nesting_stack_to_name_id(&mut graph, "CONST", vec!["Foo".into(), "::Bar".into()]);
121
+
122
+ let const_name = graph.names().get(&name_id).unwrap();
123
+ assert_eq!(StringId::from("CONST"), *const_name.str());
124
+ assert!(const_name.parent_scope().is_none());
125
+
126
+ let bar_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
127
+ assert_eq!(StringId::from("Bar"), *bar_name.str());
128
+ assert!(bar_name.parent_scope().is_top_level());
129
+
130
+ let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
131
+ assert_eq!(StringId::from("Foo"), *foo_name.str());
132
+ assert!(foo_name.parent_scope().is_none());
133
+ assert!(foo_name.nesting().is_none());
134
+ }
135
+ }
@@ -0,0 +1,267 @@
1
+ //! C API for exposing references through ID handles (like definitions)
2
+
3
+ use std::ffi::CString;
4
+ use std::ptr;
5
+
6
+ use crate::declaration_api::CDeclaration;
7
+ use crate::graph_api::{GraphPointer, with_graph};
8
+ use crate::location_api::{Location, create_location_for_uri_and_offset};
9
+ use libc::c_char;
10
+ use rubydex::model::graph::Graph;
11
+ use rubydex::model::ids::{ConstantReferenceId, MethodReferenceId};
12
+ use rubydex::model::name::NameRef;
13
+
14
+ #[repr(C)]
15
+ #[derive(Debug, Clone, Copy)]
16
+ pub struct CConstantReference {
17
+ pub id: u64,
18
+ pub declaration_id: u64,
19
+ }
20
+
21
+ impl CConstantReference {
22
+ /// Build a `CConstantReference` from a graph and reference ID. Sets `declaration_id` to 0 when the reference is
23
+ /// unresolved.
24
+ ///
25
+ /// # Panics
26
+ ///
27
+ /// This function will panic if there's inconsistent data in the graph
28
+ #[must_use]
29
+ pub fn from_id(graph: &Graph, ref_id: ConstantReferenceId) -> Self {
30
+ let reference = graph
31
+ .constant_references()
32
+ .get(&ref_id)
33
+ .expect("Constant reference not found");
34
+
35
+ let name_ref = graph.names().get(reference.name_id()).expect("Name ID should exist");
36
+
37
+ let declaration_id = match name_ref {
38
+ NameRef::Resolved(resolved) => **resolved.declaration_id(),
39
+ NameRef::Unresolved(_) => 0,
40
+ };
41
+
42
+ Self {
43
+ id: *ref_id,
44
+ declaration_id,
45
+ }
46
+ }
47
+ }
48
+
49
+ #[derive(Debug)]
50
+ pub struct ConstantReferencesIter {
51
+ entries: Box<[CConstantReference]>,
52
+ index: usize,
53
+ }
54
+
55
+ iterator!(ConstantReferencesIter, entries: CConstantReference);
56
+
57
+ /// # Safety
58
+ /// `iter` must be a valid pointer previously returned by `ConstantReferencesIter::new`.
59
+ #[unsafe(no_mangle)]
60
+ pub unsafe extern "C" fn rdx_constant_references_iter_len(iter: *const ConstantReferencesIter) -> usize {
61
+ unsafe { ConstantReferencesIter::len(iter) }
62
+ }
63
+
64
+ /// # Safety
65
+ /// - `iter` must be a valid pointer previously returned by `ConstantReferencesIter::new`.
66
+ /// - `out` must be a valid, writable pointer.
67
+ #[unsafe(no_mangle)]
68
+ pub unsafe extern "C" fn rdx_constant_references_iter_next(
69
+ iter: *mut ConstantReferencesIter,
70
+ out: *mut CConstantReference,
71
+ ) -> bool {
72
+ unsafe { ConstantReferencesIter::next(iter, out) }
73
+ }
74
+
75
+ /// # Safety
76
+ /// - `iter` must be a pointer previously returned by `ConstantReferencesIter::new`.
77
+ /// - `iter` must not be used after being freed.
78
+ #[unsafe(no_mangle)]
79
+ pub unsafe extern "C" fn rdx_constant_references_iter_free(iter: *mut ConstantReferencesIter) {
80
+ unsafe { ConstantReferencesIter::free(iter) }
81
+ }
82
+
83
+ #[repr(C)]
84
+ #[derive(Debug, Clone, Copy)]
85
+ pub struct CMethodReference {
86
+ pub id: u64,
87
+ }
88
+
89
+ #[derive(Debug)]
90
+ pub struct MethodReferencesIter {
91
+ entries: Box<[CMethodReference]>,
92
+ index: usize,
93
+ }
94
+
95
+ iterator!(MethodReferencesIter, entries: CMethodReference);
96
+
97
+ /// # Safety
98
+ /// `iter` must be a valid pointer previously returned by `MethodReferencesIter::new`.
99
+ #[unsafe(no_mangle)]
100
+ pub unsafe extern "C" fn rdx_method_references_iter_len(iter: *const MethodReferencesIter) -> usize {
101
+ unsafe { MethodReferencesIter::len(iter) }
102
+ }
103
+
104
+ /// # Safety
105
+ /// - `iter` must be a valid pointer previously returned by `MethodReferencesIter::new`.
106
+ /// - `out` must be a valid, writable pointer.
107
+ #[unsafe(no_mangle)]
108
+ pub unsafe extern "C" fn rdx_method_references_iter_next(
109
+ iter: *mut MethodReferencesIter,
110
+ out: *mut CMethodReference,
111
+ ) -> bool {
112
+ unsafe { MethodReferencesIter::next(iter, out) }
113
+ }
114
+
115
+ /// # Safety
116
+ /// - `iter` must be a pointer previously returned by `MethodReferencesIter::new`.
117
+ /// - `iter` must not be used after being freed.
118
+ #[unsafe(no_mangle)]
119
+ pub unsafe extern "C" fn rdx_method_references_iter_free(iter: *mut MethodReferencesIter) {
120
+ unsafe { MethodReferencesIter::free(iter) }
121
+ }
122
+
123
+ /// Returns the UTF-8 name string for a constant reference id.
124
+ /// Caller must free with `free_c_string`.
125
+ ///
126
+ /// # Safety
127
+ ///
128
+ /// Assumes pointer is valid.
129
+ ///
130
+ /// # Panics
131
+ ///
132
+ /// This function will panic if the reference cannot be found.
133
+ #[unsafe(no_mangle)]
134
+ pub unsafe extern "C" fn rdx_constant_reference_name(pointer: GraphPointer, reference_id: u64) -> *const c_char {
135
+ with_graph(pointer, |graph| {
136
+ let ref_id = ConstantReferenceId::new(reference_id);
137
+ let reference = graph.constant_references().get(&ref_id).expect("Reference not found");
138
+ let name = graph.names().get(reference.name_id()).expect("Name ID should exist");
139
+
140
+ let name_string = graph
141
+ .strings()
142
+ .get(name.str())
143
+ .expect("String ID should exist")
144
+ .to_string();
145
+ CString::new(name_string).unwrap().into_raw().cast_const()
146
+ })
147
+ }
148
+
149
+ /// Returns the UTF-8 name string for a method reference id.
150
+ /// Caller must free with `free_c_string`.
151
+ ///
152
+ /// # Safety
153
+ ///
154
+ /// Assumes pointer is valid.
155
+ ///
156
+ /// # Panics
157
+ ///
158
+ /// This function will panic if the reference cannot be found.
159
+ #[unsafe(no_mangle)]
160
+ pub unsafe extern "C" fn rdx_method_reference_name(pointer: GraphPointer, reference_id: u64) -> *const c_char {
161
+ with_graph(pointer, |graph| {
162
+ let ref_id = MethodReferenceId::new(reference_id);
163
+ let reference = graph.method_references().get(&ref_id).expect("Reference not found");
164
+ let name = graph
165
+ .strings()
166
+ .get(reference.str())
167
+ .expect("Name ID should exist")
168
+ .to_string();
169
+ CString::new(name).unwrap().into_raw().cast_const()
170
+ })
171
+ }
172
+
173
+ /// Returns a newly allocated `Location` for the given constant reference id.
174
+ /// Caller must free the returned pointer with `rdx_location_free`.
175
+ ///
176
+ /// # Safety
177
+ ///
178
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
179
+ /// - `reference_id` must be a valid reference id.
180
+ ///
181
+ /// # Panics
182
+ ///
183
+ /// This function will panic if a reference or document cannot be found.
184
+ #[unsafe(no_mangle)]
185
+ pub unsafe extern "C" fn rdx_constant_reference_location(pointer: GraphPointer, reference_id: u64) -> *mut Location {
186
+ with_graph(pointer, |graph| {
187
+ let ref_id = ConstantReferenceId::new(reference_id);
188
+ let reference = graph.constant_references().get(&ref_id).expect("Reference not found");
189
+ let document = graph
190
+ .documents()
191
+ .get(&reference.uri_id())
192
+ .expect("Document should exist");
193
+
194
+ create_location_for_uri_and_offset(graph, document, reference.offset())
195
+ })
196
+ }
197
+
198
+ /// Returns the declaration that the given resolved constant reference points to. Returns NULL if the reference is
199
+ /// unresolved. Caller must free with `free_c_declaration`.
200
+ ///
201
+ /// # Safety
202
+ ///
203
+ /// Assumes pointer is valid.
204
+ ///
205
+ /// # Panics
206
+ ///
207
+ /// This function will panic if the reference cannot be found.
208
+ #[unsafe(no_mangle)]
209
+ pub unsafe extern "C" fn rdx_resolved_constant_reference_declaration(
210
+ pointer: GraphPointer,
211
+ reference_id: u64,
212
+ ) -> *const CDeclaration {
213
+ with_graph(pointer, |graph| {
214
+ let ref_id = ConstantReferenceId::new(reference_id);
215
+ let reference = graph.constant_references().get(&ref_id).expect("Reference not found");
216
+ let name_ref = graph.names().get(reference.name_id()).expect("Name ID should exist");
217
+
218
+ match name_ref {
219
+ NameRef::Resolved(resolved) => {
220
+ let decl_id = *resolved.declaration_id();
221
+ let decl = graph.declarations().get(&decl_id).expect("Declaration not found");
222
+ Box::into_raw(Box::new(CDeclaration::from_declaration(decl_id, decl))).cast_const()
223
+ }
224
+ NameRef::Unresolved(_) => ptr::null(),
225
+ }
226
+ })
227
+ }
228
+
229
+ /// Returns a newly allocated `Location` for the given method reference id.
230
+ /// Caller must free the returned pointer with `rdx_location_free`.
231
+ ///
232
+ /// # Safety
233
+ ///
234
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
235
+ /// - `reference_id` must be a valid reference id.
236
+ ///
237
+ /// # Panics
238
+ ///
239
+ /// This function will panic if a reference or document cannot be found.
240
+ #[unsafe(no_mangle)]
241
+ pub unsafe extern "C" fn rdx_method_reference_location(pointer: GraphPointer, reference_id: u64) -> *mut Location {
242
+ with_graph(pointer, |graph| {
243
+ let ref_id = MethodReferenceId::new(reference_id);
244
+ let reference = graph.method_references().get(&ref_id).expect("Reference not found");
245
+ let document = graph
246
+ .documents()
247
+ .get(&reference.uri_id())
248
+ .expect("Document should exist");
249
+
250
+ create_location_for_uri_and_offset(graph, document, reference.offset())
251
+ })
252
+ }
253
+
254
+ /// Frees a `CConstantReference` previously returned by an FFI function.
255
+ ///
256
+ /// # Safety
257
+ /// - `ptr` must be a valid pointer previously returned by an FFI function that allocates a `CConstantReference`, or
258
+ /// NULL.
259
+ /// - `ptr` must not be used after being freed.
260
+ #[unsafe(no_mangle)]
261
+ pub unsafe extern "C" fn free_c_constant_reference(ptr: *const CConstantReference) {
262
+ if !ptr.is_null() {
263
+ unsafe {
264
+ let _ = Box::from_raw(ptr.cast_mut());
265
+ }
266
+ }
267
+ }
@@ -0,0 +1,70 @@
1
+ use libc::{c_char, size_t};
2
+ use std::ffi::{CStr, CString};
3
+ use std::slice;
4
+ use std::str::Utf8Error;
5
+
6
+ /// Converts a C array of strings into a Vec<String>
7
+ ///
8
+ /// # Safety
9
+ ///
10
+ /// This function is unsafe because it attempts to instantiate a Vec<String> from a raw char** pointer
11
+ ///
12
+ /// # Errors
13
+ ///
14
+ /// This function errors if any of the strings inside the array contain invalid UTF-8 data
15
+ pub unsafe fn convert_double_pointer_to_vec(data: *const *const c_char, len: size_t) -> Result<Vec<String>, Utf8Error> {
16
+ unsafe {
17
+ slice::from_raw_parts(data, len)
18
+ .iter()
19
+ .map(|arg| CStr::from_ptr(*arg).to_str().map(ToString::to_string))
20
+ .collect()
21
+ }
22
+ }
23
+
24
+ /// # Safety
25
+ ///
26
+ /// This function is unsafe because it dereferences the char pointer, which needs to be valid for the duration of the
27
+ /// function
28
+ ///
29
+ /// # Errors
30
+ ///
31
+ /// This function errors if any of the strings inside the array contain invalid UTF-8 data
32
+ pub unsafe fn convert_char_ptr_to_string(data: *const c_char) -> Result<String, Utf8Error> {
33
+ unsafe { CStr::from_ptr(data).to_str().map(ToString::to_string) }
34
+ }
35
+
36
+ /// Frees a `CString` allocated on the Rust side
37
+ #[unsafe(no_mangle)]
38
+ pub extern "C" fn free_c_string(ptr: *const c_char) {
39
+ unsafe {
40
+ let _ = CString::from_raw(ptr.cast_mut());
41
+ }
42
+ }
43
+
44
+ /// Frees a boxed u64 allocated on the Rust side
45
+ #[unsafe(no_mangle)]
46
+ pub extern "C" fn free_u64(ptr: *const u64) {
47
+ unsafe {
48
+ let _ = Box::from_raw(ptr.cast_mut());
49
+ }
50
+ }
51
+
52
+ /// Frees an array of C strings allocated by Rust.
53
+ ///
54
+ /// # Safety
55
+ /// - `ptr` must be a pointer to a boxed slice of C strings previously allocated by this crate.
56
+ /// - `count` must be the length of the array.
57
+ /// - `ptr` must not be used after being freed.
58
+ #[unsafe(no_mangle)]
59
+ pub unsafe extern "C" fn free_c_string_array(ptr: *const *const c_char, count: usize) {
60
+ if ptr.is_null() {
61
+ return;
62
+ }
63
+
64
+ let slice = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(ptr.cast_mut(), count)) };
65
+ let _: Vec<_> = slice
66
+ .iter()
67
+ .filter(|p| !p.is_null())
68
+ .map(|arg| unsafe { CString::from_raw((*arg).cast_mut()) })
69
+ .collect();
70
+ }
data/rust/rustfmt.toml ADDED
@@ -0,0 +1,2 @@
1
+ max_width = 120
2
+ edition = "2024"