rubydex 0.1.0.beta1-x86_64-linux → 0.1.0.beta2-x86_64-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.
- checksums.yaml +4 -4
- data/ext/rubydex/declaration.c +146 -0
- data/ext/rubydex/declaration.h +10 -0
- data/ext/rubydex/definition.c +234 -0
- data/ext/rubydex/definition.h +28 -0
- data/ext/rubydex/diagnostic.c +6 -0
- data/ext/rubydex/diagnostic.h +11 -0
- data/ext/rubydex/document.c +98 -0
- data/ext/rubydex/document.h +10 -0
- data/ext/rubydex/extconf.rb +36 -15
- data/ext/rubydex/graph.c +405 -0
- data/ext/rubydex/graph.h +10 -0
- data/ext/rubydex/handle.h +44 -0
- data/ext/rubydex/location.c +22 -0
- data/ext/rubydex/location.h +15 -0
- data/ext/rubydex/reference.c +104 -0
- data/ext/rubydex/reference.h +16 -0
- data/ext/rubydex/rubydex.c +22 -0
- data/ext/rubydex/utils.c +27 -0
- data/ext/rubydex/utils.h +13 -0
- data/lib/rubydex/3.2/rubydex.so +0 -0
- data/lib/rubydex/3.3/rubydex.so +0 -0
- data/lib/rubydex/3.4/rubydex.so +0 -0
- data/lib/rubydex/4.0/rubydex.so +0 -0
- data/lib/rubydex/librubydex_sys.so +0 -0
- data/lib/rubydex/version.rb +1 -1
- data/rust/Cargo.lock +1275 -0
- data/rust/Cargo.toml +23 -0
- data/rust/about.hbs +78 -0
- data/rust/about.toml +9 -0
- data/rust/rubydex/Cargo.toml +41 -0
- data/rust/rubydex/src/diagnostic.rs +108 -0
- data/rust/rubydex/src/errors.rs +28 -0
- data/rust/rubydex/src/indexing/local_graph.rs +172 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +5397 -0
- data/rust/rubydex/src/indexing.rs +128 -0
- data/rust/rubydex/src/job_queue.rs +186 -0
- data/rust/rubydex/src/lib.rs +15 -0
- data/rust/rubydex/src/listing.rs +249 -0
- data/rust/rubydex/src/main.rs +116 -0
- data/rust/rubydex/src/model/comment.rs +24 -0
- data/rust/rubydex/src/model/declaration.rs +541 -0
- data/rust/rubydex/src/model/definitions.rs +1475 -0
- data/rust/rubydex/src/model/document.rs +111 -0
- data/rust/rubydex/src/model/encoding.rs +22 -0
- data/rust/rubydex/src/model/graph.rs +1387 -0
- data/rust/rubydex/src/model/id.rs +90 -0
- data/rust/rubydex/src/model/identity_maps.rs +54 -0
- data/rust/rubydex/src/model/ids.rs +32 -0
- data/rust/rubydex/src/model/name.rs +188 -0
- data/rust/rubydex/src/model/references.rs +129 -0
- data/rust/rubydex/src/model/string_ref.rs +44 -0
- data/rust/rubydex/src/model/visibility.rs +41 -0
- data/rust/rubydex/src/model.rs +13 -0
- data/rust/rubydex/src/offset.rs +70 -0
- data/rust/rubydex/src/position.rs +6 -0
- data/rust/rubydex/src/query.rs +103 -0
- data/rust/rubydex/src/resolution.rs +4421 -0
- data/rust/rubydex/src/stats/memory.rs +71 -0
- data/rust/rubydex/src/stats/timer.rs +126 -0
- data/rust/rubydex/src/stats.rs +9 -0
- data/rust/rubydex/src/test_utils/context.rs +226 -0
- data/rust/rubydex/src/test_utils/graph_test.rs +229 -0
- data/rust/rubydex/src/test_utils/local_graph_test.rs +166 -0
- data/rust/rubydex/src/test_utils.rs +52 -0
- data/rust/rubydex/src/visualization/dot.rs +176 -0
- data/rust/rubydex/src/visualization.rs +6 -0
- data/rust/rubydex/tests/cli.rs +167 -0
- data/rust/rubydex-sys/Cargo.toml +20 -0
- data/rust/rubydex-sys/build.rs +14 -0
- data/rust/rubydex-sys/cbindgen.toml +12 -0
- data/rust/rubydex-sys/src/declaration_api.rs +114 -0
- data/rust/rubydex-sys/src/definition_api.rs +350 -0
- data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
- data/rust/rubydex-sys/src/document_api.rs +54 -0
- data/rust/rubydex-sys/src/graph_api.rs +493 -0
- data/rust/rubydex-sys/src/lib.rs +9 -0
- data/rust/rubydex-sys/src/location_api.rs +79 -0
- data/rust/rubydex-sys/src/name_api.rs +81 -0
- data/rust/rubydex-sys/src/reference_api.rs +191 -0
- data/rust/rubydex-sys/src/utils.rs +50 -0
- data/rust/rustfmt.toml +2 -0
- metadata +77 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//! This file provides the C API for the Graph object
|
|
2
|
+
|
|
3
|
+
use libc::c_char;
|
|
4
|
+
use rubydex::model::declaration::Declaration;
|
|
5
|
+
use std::ffi::CString;
|
|
6
|
+
use std::ptr;
|
|
7
|
+
|
|
8
|
+
use crate::definition_api::{DefinitionKind, DefinitionsIter, rdx_definitions_iter_new_from_ids};
|
|
9
|
+
use crate::graph_api::{GraphPointer, with_graph};
|
|
10
|
+
use crate::utils;
|
|
11
|
+
use rubydex::model::ids::{DeclarationId, StringId};
|
|
12
|
+
|
|
13
|
+
/// Returns the UTF-8 name string for a declaration id.
|
|
14
|
+
/// Caller must free with `free_c_string`.
|
|
15
|
+
///
|
|
16
|
+
/// # Safety
|
|
17
|
+
///
|
|
18
|
+
/// Assumes pointer is valid.
|
|
19
|
+
///
|
|
20
|
+
/// # Panics
|
|
21
|
+
///
|
|
22
|
+
/// This function will panic if the name pointer is invalid.
|
|
23
|
+
#[unsafe(no_mangle)]
|
|
24
|
+
pub unsafe extern "C" fn rdx_declaration_name(pointer: GraphPointer, name_id: i64) -> *const c_char {
|
|
25
|
+
with_graph(pointer, |graph| {
|
|
26
|
+
let name_id = DeclarationId::new(name_id);
|
|
27
|
+
if let Some(decl) = graph.declarations().get(&name_id) {
|
|
28
|
+
CString::new(decl.name()).unwrap().into_raw().cast_const()
|
|
29
|
+
} else {
|
|
30
|
+
ptr::null()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Returns the declaration ID for a member from a declaration.
|
|
36
|
+
/// Returns NULL if the member is not found.
|
|
37
|
+
///
|
|
38
|
+
/// # Safety
|
|
39
|
+
/// - `member` must be a valid, null-terminated UTF-8 string
|
|
40
|
+
#[unsafe(no_mangle)]
|
|
41
|
+
pub unsafe extern "C" fn rdx_declaration_member(
|
|
42
|
+
pointer: GraphPointer,
|
|
43
|
+
name_id: i64,
|
|
44
|
+
member: *const c_char,
|
|
45
|
+
) -> *const i64 {
|
|
46
|
+
let Ok(member_str) = (unsafe { utils::convert_char_ptr_to_string(member) }) else {
|
|
47
|
+
return ptr::null();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
with_graph(pointer, |graph| {
|
|
51
|
+
let name_id = DeclarationId::new(name_id);
|
|
52
|
+
if let Some(Declaration::Namespace(decl)) = graph.declarations().get(&name_id) {
|
|
53
|
+
let member_id = StringId::from(member_str.as_str());
|
|
54
|
+
|
|
55
|
+
if let Some(member_decl_id) = decl.member(&member_id) {
|
|
56
|
+
return Box::into_raw(Box::new(**member_decl_id)).cast_const();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
ptr::null()
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Returns the UTF-8 unqualified name string for a declaration id.
|
|
65
|
+
/// Caller must free with `free_c_string`.
|
|
66
|
+
///
|
|
67
|
+
/// # Safety
|
|
68
|
+
///
|
|
69
|
+
/// Assumes pointer is valid.
|
|
70
|
+
///
|
|
71
|
+
/// # Panics
|
|
72
|
+
///
|
|
73
|
+
/// This function will panic if the name pointer is invalid.
|
|
74
|
+
#[unsafe(no_mangle)]
|
|
75
|
+
pub unsafe extern "C" fn rdx_declaration_unqualified_name(pointer: GraphPointer, name_id: i64) -> *const c_char {
|
|
76
|
+
with_graph(pointer, |graph| {
|
|
77
|
+
let name_id = DeclarationId::new(name_id);
|
|
78
|
+
if let Some(decl) = graph.declarations().get(&name_id) {
|
|
79
|
+
CString::new(decl.unqualified_name()).unwrap().into_raw().cast_const()
|
|
80
|
+
} else {
|
|
81
|
+
ptr::null()
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// An iterator over definition IDs and kinds for a given declaration
|
|
87
|
+
///
|
|
88
|
+
/// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
|
|
89
|
+
// Use shared DefinitionsIter directly in signatures
|
|
90
|
+
/// Creates a new iterator over definition IDs for a given declaration by snapshotting the current set of IDs.
|
|
91
|
+
///
|
|
92
|
+
/// # Safety
|
|
93
|
+
///
|
|
94
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
95
|
+
/// - The returned pointer must be freed with `rdx_declaration_definitions_iter_free`.
|
|
96
|
+
///
|
|
97
|
+
/// # Panics
|
|
98
|
+
///
|
|
99
|
+
/// This function will panic if acquiring a read lock fails
|
|
100
|
+
#[unsafe(no_mangle)]
|
|
101
|
+
pub unsafe extern "C" fn rdx_declaration_definitions_iter_new(
|
|
102
|
+
pointer: GraphPointer,
|
|
103
|
+
decl_id: i64,
|
|
104
|
+
) -> *mut DefinitionsIter {
|
|
105
|
+
// Snapshot the IDs and kinds at iterator creation to avoid borrowing across FFI calls
|
|
106
|
+
with_graph(pointer, |graph| {
|
|
107
|
+
let decl_id = DeclarationId::new(decl_id);
|
|
108
|
+
if let Some(decl) = graph.declarations().get(&decl_id) {
|
|
109
|
+
rdx_definitions_iter_new_from_ids(graph, decl.definitions())
|
|
110
|
+
} else {
|
|
111
|
+
DefinitionsIter::new(Vec::<(i64, DefinitionKind)>::new().into_boxed_slice())
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
//! This file provides the C API for Definition 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::definitions::Definition;
|
|
7
|
+
use rubydex::model::ids::DefinitionId;
|
|
8
|
+
use std::ffi::CString;
|
|
9
|
+
use std::ptr;
|
|
10
|
+
|
|
11
|
+
/// C-compatible enum representing the kind of a definition.
|
|
12
|
+
#[repr(C)]
|
|
13
|
+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
14
|
+
pub enum DefinitionKind {
|
|
15
|
+
Class = 0,
|
|
16
|
+
SingletonClass = 1,
|
|
17
|
+
Module = 2,
|
|
18
|
+
Constant = 3,
|
|
19
|
+
ConstantAlias = 4,
|
|
20
|
+
Method = 5,
|
|
21
|
+
AttrAccessor = 6,
|
|
22
|
+
AttrReader = 7,
|
|
23
|
+
AttrWriter = 8,
|
|
24
|
+
GlobalVariable = 9,
|
|
25
|
+
InstanceVariable = 10,
|
|
26
|
+
ClassVariable = 11,
|
|
27
|
+
MethodAlias = 12,
|
|
28
|
+
GlobalVariableAlias = 13,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub(crate) fn map_definition_to_kind(defn: &Definition) -> DefinitionKind {
|
|
32
|
+
match defn {
|
|
33
|
+
Definition::Class(_) => DefinitionKind::Class,
|
|
34
|
+
Definition::SingletonClass(_) => DefinitionKind::SingletonClass,
|
|
35
|
+
Definition::Module(_) => DefinitionKind::Module,
|
|
36
|
+
Definition::Constant(_) => DefinitionKind::Constant,
|
|
37
|
+
Definition::ConstantAlias(_) => DefinitionKind::ConstantAlias,
|
|
38
|
+
Definition::Method(_) => DefinitionKind::Method,
|
|
39
|
+
Definition::AttrAccessor(_) => DefinitionKind::AttrAccessor,
|
|
40
|
+
Definition::AttrReader(_) => DefinitionKind::AttrReader,
|
|
41
|
+
Definition::AttrWriter(_) => DefinitionKind::AttrWriter,
|
|
42
|
+
Definition::GlobalVariable(_) => DefinitionKind::GlobalVariable,
|
|
43
|
+
Definition::InstanceVariable(_) => DefinitionKind::InstanceVariable,
|
|
44
|
+
Definition::ClassVariable(_) => DefinitionKind::ClassVariable,
|
|
45
|
+
Definition::MethodAlias(_) => DefinitionKind::MethodAlias,
|
|
46
|
+
Definition::GlobalVariableAlias(_) => DefinitionKind::GlobalVariableAlias,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Returns the enum kind for a definition id (e.g. Class, Module).
|
|
51
|
+
///
|
|
52
|
+
/// # Safety
|
|
53
|
+
///
|
|
54
|
+
/// Assumes pointer is valid.
|
|
55
|
+
///
|
|
56
|
+
/// # Panics
|
|
57
|
+
///
|
|
58
|
+
/// This function will panic if the definition cannot be found.
|
|
59
|
+
#[unsafe(no_mangle)]
|
|
60
|
+
pub unsafe extern "C" fn rdx_definition_kind(pointer: GraphPointer, definition_id: i64) -> DefinitionKind {
|
|
61
|
+
with_graph(pointer, |graph| {
|
|
62
|
+
let definition_id = DefinitionId::new(definition_id);
|
|
63
|
+
if let Some(defn) = graph.definitions().get(&definition_id) {
|
|
64
|
+
map_definition_to_kind(defn)
|
|
65
|
+
} else {
|
|
66
|
+
panic!("Definition not found: {definition_id:?}");
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Returns the UTF-8 unqualified name string for a definition id.
|
|
72
|
+
/// Caller must free with `free_c_string`.
|
|
73
|
+
///
|
|
74
|
+
/// # Safety
|
|
75
|
+
///
|
|
76
|
+
/// Assumes pointer is valid.
|
|
77
|
+
///
|
|
78
|
+
/// # Panics
|
|
79
|
+
///
|
|
80
|
+
/// This function will panic if the definition cannot be found.
|
|
81
|
+
#[unsafe(no_mangle)]
|
|
82
|
+
pub unsafe extern "C" fn rdx_definition_name(pointer: GraphPointer, definition_id: i64) -> *const c_char {
|
|
83
|
+
with_graph(pointer, |graph| {
|
|
84
|
+
let def_id = DefinitionId::new(definition_id);
|
|
85
|
+
if let Some(defn) = graph.definitions().get(&def_id) {
|
|
86
|
+
let string_id = graph.definition_string_id(defn);
|
|
87
|
+
|
|
88
|
+
if let Some(name) = graph.strings().get(&string_id) {
|
|
89
|
+
CString::new(name.as_str()).unwrap().into_raw().cast_const()
|
|
90
|
+
} else {
|
|
91
|
+
ptr::null()
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
ptr::null()
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Shared iterator over definition (id, kind) pairs
|
|
100
|
+
#[derive(Debug)]
|
|
101
|
+
pub struct DefinitionsIter {
|
|
102
|
+
pub entries: Box<[(i64, DefinitionKind)]>,
|
|
103
|
+
pub index: usize,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
impl DefinitionsIter {
|
|
107
|
+
#[must_use]
|
|
108
|
+
pub fn new(entries: Box<[(i64, DefinitionKind)]>) -> *mut DefinitionsIter {
|
|
109
|
+
Box::into_raw(Box::new(DefinitionsIter { entries, index: 0 }))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Returns the total number of entries in the iterator snapshot.
|
|
114
|
+
///
|
|
115
|
+
/// # Safety
|
|
116
|
+
/// - `iter` must be a valid pointer previously returned by `DefinitionsIter::new`.
|
|
117
|
+
#[unsafe(no_mangle)]
|
|
118
|
+
pub unsafe extern "C" fn rdx_definitions_iter_len(iter: *const DefinitionsIter) -> usize {
|
|
119
|
+
if iter.is_null() {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
unsafe { (&*iter).entries.len() }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// Advances the iterator and writes the next (ID, Kind) into `out_id` and `out_kind`.
|
|
126
|
+
/// Returns `true` if a pair was written, or `false` if the iterator is exhausted or inputs are invalid.
|
|
127
|
+
///
|
|
128
|
+
/// # Safety
|
|
129
|
+
/// - `iter` must be a valid pointer previously returned by `DefinitionsIter::new`.
|
|
130
|
+
/// - `out_id` and `out_kind` must be valid, writable pointers.
|
|
131
|
+
#[unsafe(no_mangle)]
|
|
132
|
+
pub unsafe extern "C" fn rdx_definitions_iter_next(
|
|
133
|
+
iter: *mut DefinitionsIter,
|
|
134
|
+
out_id: *mut i64,
|
|
135
|
+
out_kind: *mut DefinitionKind,
|
|
136
|
+
) -> bool {
|
|
137
|
+
if iter.is_null() || out_id.is_null() || out_kind.is_null() {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let it = unsafe { &mut *iter };
|
|
142
|
+
if it.index >= it.entries.len() {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let (id, kind) = it.entries[it.index];
|
|
147
|
+
it.index += 1;
|
|
148
|
+
unsafe {
|
|
149
|
+
*out_id = id;
|
|
150
|
+
*out_kind = kind;
|
|
151
|
+
}
|
|
152
|
+
true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Frees an iterator created by `DefinitionsIter::new`.
|
|
156
|
+
///
|
|
157
|
+
/// # Safety
|
|
158
|
+
/// - `iter` must be a pointer previously returned by `DefinitionsIter::new`.
|
|
159
|
+
/// - `iter` must not be used after being freed.
|
|
160
|
+
#[unsafe(no_mangle)]
|
|
161
|
+
pub unsafe extern "C" fn rdx_definitions_iter_free(iter: *mut DefinitionsIter) {
|
|
162
|
+
if iter.is_null() {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
unsafe {
|
|
166
|
+
let _ = Box::from_raw(iter);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// C-compatible struct representing a single comment with its string and location
|
|
171
|
+
#[repr(C)]
|
|
172
|
+
pub struct CommentEntry {
|
|
173
|
+
pub string: *const c_char,
|
|
174
|
+
pub location: *mut Location,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// C-compatible array of comments
|
|
178
|
+
#[repr(C)]
|
|
179
|
+
pub struct CommentArray {
|
|
180
|
+
pub items: *mut CommentEntry,
|
|
181
|
+
pub len: usize,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// Returns a newly allocated array of comments (string and location) for the given definition id.
|
|
185
|
+
/// Caller must free the returned pointer with `rdx_definition_comments_free` and each inner string with `free_c_string` if needed.
|
|
186
|
+
///
|
|
187
|
+
/// # Safety
|
|
188
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
189
|
+
/// - `definition_id` must be a valid definition id.
|
|
190
|
+
///
|
|
191
|
+
/// # Panics
|
|
192
|
+
/// This function will panic if a definition or document cannot be found.
|
|
193
|
+
#[unsafe(no_mangle)]
|
|
194
|
+
pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definition_id: i64) -> *mut CommentArray {
|
|
195
|
+
with_graph(pointer, |graph| {
|
|
196
|
+
let def_id = DefinitionId::new(definition_id);
|
|
197
|
+
let Some(defn) = graph.definitions().get(&def_id) else {
|
|
198
|
+
panic!("Definition not found: {definition_id:?}");
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
let uri_id = *defn.uri_id();
|
|
202
|
+
let document = graph.documents().get(&uri_id).expect("document should exist");
|
|
203
|
+
|
|
204
|
+
let mut entries = defn
|
|
205
|
+
.comments()
|
|
206
|
+
.iter()
|
|
207
|
+
.map(|c| CommentEntry {
|
|
208
|
+
string: CString::new(c.string().as_str()).unwrap().into_raw().cast_const(),
|
|
209
|
+
location: create_location_for_uri_and_offset(graph, document, c.offset()),
|
|
210
|
+
})
|
|
211
|
+
.collect::<Vec<CommentEntry>>()
|
|
212
|
+
.into_boxed_slice();
|
|
213
|
+
|
|
214
|
+
let len = entries.len();
|
|
215
|
+
let items_ptr = entries.as_mut_ptr();
|
|
216
|
+
std::mem::forget(entries);
|
|
217
|
+
|
|
218
|
+
Box::into_raw(Box::new(CommentArray { items: items_ptr, len }))
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/// Frees a `CommentArray` previously returned by `rdx_definition_comments`.
|
|
223
|
+
///
|
|
224
|
+
/// # Safety
|
|
225
|
+
/// - `ptr` must be a valid pointer previously returned by `rdx_definition_comments`.
|
|
226
|
+
/// - `ptr` must not be used after being freed.
|
|
227
|
+
#[unsafe(no_mangle)]
|
|
228
|
+
pub unsafe extern "C" fn rdx_definition_comments_free(ptr: *mut CommentArray) {
|
|
229
|
+
if ptr.is_null() {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Take ownership of the CommentArray
|
|
234
|
+
let arr = unsafe { Box::from_raw(ptr) };
|
|
235
|
+
|
|
236
|
+
if !arr.items.is_null() && arr.len > 0 {
|
|
237
|
+
// Reconstruct the boxed slice so we can drop it after freeing inner allocations
|
|
238
|
+
let slice_ptr = ptr::slice_from_raw_parts_mut(arr.items, arr.len);
|
|
239
|
+
let mut boxed_slice: Box<[CommentEntry]> = unsafe { Box::from_raw(slice_ptr) };
|
|
240
|
+
|
|
241
|
+
for item in &mut boxed_slice {
|
|
242
|
+
if !item.string.is_null() {
|
|
243
|
+
// Free the CString allocated for the comment string
|
|
244
|
+
let _ = unsafe { CString::from_raw(item.string.cast_mut()) };
|
|
245
|
+
}
|
|
246
|
+
if !item.location.is_null() {
|
|
247
|
+
unsafe { crate::location_api::rdx_location_free(item.location) };
|
|
248
|
+
item.location = ptr::null_mut();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// boxed_slice is dropped here, freeing the items buffer
|
|
253
|
+
}
|
|
254
|
+
// arr is dropped here
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/// Returns a newly allocated `Location` for the given definition id.
|
|
258
|
+
/// Caller must free the returned pointer with `rdx_location_free`.
|
|
259
|
+
///
|
|
260
|
+
/// # Safety
|
|
261
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
262
|
+
/// - `definition_id` must be a valid definition id.
|
|
263
|
+
///
|
|
264
|
+
/// # Panics
|
|
265
|
+
///
|
|
266
|
+
/// This function will panic if a definition or document cannot be found.
|
|
267
|
+
#[unsafe(no_mangle)]
|
|
268
|
+
pub unsafe extern "C" fn rdx_definition_location(pointer: GraphPointer, definition_id: i64) -> *mut Location {
|
|
269
|
+
with_graph(pointer, |graph| {
|
|
270
|
+
let def_id = DefinitionId::new(definition_id);
|
|
271
|
+
let Some(defn) = graph.definitions().get(&def_id) else {
|
|
272
|
+
panic!("Definition not found: {definition_id:?}");
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
let document = graph.documents().get(defn.uri_id()).expect("document should exist");
|
|
276
|
+
create_location_for_uri_and_offset(graph, document, defn.offset())
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// Creates a new iterator over definition IDs for a given declaration by snapshotting the current set of IDs.
|
|
281
|
+
///
|
|
282
|
+
/// # Panics
|
|
283
|
+
///
|
|
284
|
+
/// This function will panic if a definition cannot be found.
|
|
285
|
+
pub(crate) fn rdx_definitions_iter_new_from_ids<'a, I>(
|
|
286
|
+
graph: &rubydex::model::graph::Graph,
|
|
287
|
+
ids: I,
|
|
288
|
+
) -> *mut DefinitionsIter
|
|
289
|
+
where
|
|
290
|
+
I: IntoIterator<Item = &'a DefinitionId>,
|
|
291
|
+
{
|
|
292
|
+
let entries = ids
|
|
293
|
+
.into_iter()
|
|
294
|
+
.map(|def_id| {
|
|
295
|
+
let id = **def_id;
|
|
296
|
+
let kind = graph
|
|
297
|
+
.definitions()
|
|
298
|
+
.get(&DefinitionId::new(id))
|
|
299
|
+
.map_or_else(|| panic!("Definition not found: {id:?}"), map_definition_to_kind);
|
|
300
|
+
(id, kind)
|
|
301
|
+
})
|
|
302
|
+
.collect::<Vec<(i64, DefinitionKind)>>()
|
|
303
|
+
.into_boxed_slice();
|
|
304
|
+
|
|
305
|
+
DefinitionsIter::new(entries)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/// Returns true if the definition is deprecated.
|
|
309
|
+
///
|
|
310
|
+
/// # Safety
|
|
311
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
312
|
+
/// - `definition_id` must be a valid definition id.
|
|
313
|
+
///
|
|
314
|
+
/// # Panics
|
|
315
|
+
/// This function will panic if a definition cannot be found.
|
|
316
|
+
#[unsafe(no_mangle)]
|
|
317
|
+
pub unsafe extern "C" fn rdx_definition_is_deprecated(pointer: GraphPointer, definition_id: i64) -> bool {
|
|
318
|
+
with_graph(pointer, |graph| {
|
|
319
|
+
let def_id = DefinitionId::new(definition_id);
|
|
320
|
+
let defn = graph.definitions().get(&def_id).expect("definition not found");
|
|
321
|
+
defn.is_deprecated()
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/// Returns a newly allocated `Location` for the name portion of a definition id.
|
|
326
|
+
/// For class, module, and singleton class definitions, this returns the location of just
|
|
327
|
+
/// the name (e.g., "Bar" in `class Foo::Bar`).
|
|
328
|
+
/// For other definition types, returns NULL.
|
|
329
|
+
/// Caller must free the returned pointer with `rdx_location_free`.
|
|
330
|
+
///
|
|
331
|
+
/// # Safety
|
|
332
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
333
|
+
/// - `definition_id` must be a valid definition id.
|
|
334
|
+
///
|
|
335
|
+
/// # Panics
|
|
336
|
+
/// Panics if the definition's document does not exist in the graph.
|
|
337
|
+
#[unsafe(no_mangle)]
|
|
338
|
+
pub unsafe extern "C" fn rdx_definition_name_location(pointer: GraphPointer, definition_id: i64) -> *mut Location {
|
|
339
|
+
with_graph(pointer, |graph| {
|
|
340
|
+
let def_id = DefinitionId::new(definition_id);
|
|
341
|
+
let Some(defn) = graph.definitions().get(&def_id) else {
|
|
342
|
+
return ptr::null_mut();
|
|
343
|
+
};
|
|
344
|
+
let Some(name_offset) = defn.name_offset() else {
|
|
345
|
+
return ptr::null_mut();
|
|
346
|
+
};
|
|
347
|
+
let document = graph.documents().get(defn.uri_id()).expect("document should exist");
|
|
348
|
+
create_location_for_uri_and_offset(graph, document, name_offset)
|
|
349
|
+
})
|
|
350
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//! Diagnostics-related FFI helpers
|
|
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 std::{ffi::CString, mem, ptr};
|
|
7
|
+
|
|
8
|
+
/// C-compatible struct representing a diagnostic entry.
|
|
9
|
+
#[repr(C)]
|
|
10
|
+
pub struct DiagnosticEntry {
|
|
11
|
+
pub rule: *const c_char,
|
|
12
|
+
pub message: *const c_char,
|
|
13
|
+
pub location: *mut Location,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// C-compatible array wrapper for diagnostics.
|
|
17
|
+
#[repr(C)]
|
|
18
|
+
pub struct DiagnosticArray {
|
|
19
|
+
pub items: *mut DiagnosticEntry,
|
|
20
|
+
pub len: usize,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl DiagnosticArray {
|
|
24
|
+
fn from_vec(mut entries: Vec<DiagnosticEntry>) -> *mut DiagnosticArray {
|
|
25
|
+
let len = entries.len();
|
|
26
|
+
let ptr = entries.as_mut_ptr();
|
|
27
|
+
mem::forget(entries);
|
|
28
|
+
Box::into_raw(Box::new(DiagnosticArray { items: ptr, len }))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Returns all diagnostics currently recorded in the global graph.
|
|
33
|
+
///
|
|
34
|
+
/// # Safety
|
|
35
|
+
///
|
|
36
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
37
|
+
/// - The pointed graph must remain alive for the duration of the call.
|
|
38
|
+
///
|
|
39
|
+
/// # Panics
|
|
40
|
+
///
|
|
41
|
+
/// - If a diagnostic references a URI whose file cannot be read to build a location.
|
|
42
|
+
#[unsafe(no_mangle)]
|
|
43
|
+
pub unsafe extern "C" fn rdx_graph_diagnostics(pointer: GraphPointer) -> *mut DiagnosticArray {
|
|
44
|
+
with_graph(pointer, |graph| {
|
|
45
|
+
let entries = graph
|
|
46
|
+
.all_diagnostics()
|
|
47
|
+
.iter()
|
|
48
|
+
.map(|diagnostic| {
|
|
49
|
+
let document = graph.documents().get(diagnostic.uri_id()).unwrap();
|
|
50
|
+
let location = create_location_for_uri_and_offset(graph, document, diagnostic.offset());
|
|
51
|
+
|
|
52
|
+
DiagnosticEntry {
|
|
53
|
+
rule: CString::new(diagnostic.rule().to_string())
|
|
54
|
+
.unwrap()
|
|
55
|
+
.into_raw()
|
|
56
|
+
.cast_const(),
|
|
57
|
+
message: CString::new(diagnostic.message()).unwrap().into_raw().cast_const(),
|
|
58
|
+
location,
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.collect::<Vec<DiagnosticEntry>>();
|
|
62
|
+
|
|
63
|
+
DiagnosticArray::from_vec(entries)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Frees a diagnostic array previously returned by `rdx_graph_diagnostics`.
|
|
68
|
+
///
|
|
69
|
+
/// # Safety
|
|
70
|
+
///
|
|
71
|
+
/// - `ptr` must be a valid pointer previously returned by `rdx_graph_diagnostics`.
|
|
72
|
+
/// - `ptr` must not be used after being freed.
|
|
73
|
+
#[unsafe(no_mangle)]
|
|
74
|
+
pub unsafe extern "C" fn rdx_diagnostics_free(ptr: *mut DiagnosticArray) {
|
|
75
|
+
if ptr.is_null() {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let array = unsafe { Box::from_raw(ptr) };
|
|
80
|
+
if !array.items.is_null() && array.len > 0 {
|
|
81
|
+
let slice_ptr = ptr::slice_from_raw_parts_mut(array.items, array.len);
|
|
82
|
+
let mut boxed_slice: Box<[DiagnosticEntry]> = unsafe { Box::from_raw(slice_ptr) };
|
|
83
|
+
|
|
84
|
+
for entry in &mut *boxed_slice {
|
|
85
|
+
if !entry.rule.is_null() {
|
|
86
|
+
let _ = unsafe { CString::from_raw(entry.rule.cast_mut()) };
|
|
87
|
+
}
|
|
88
|
+
if !entry.message.is_null() {
|
|
89
|
+
let _ = unsafe { CString::from_raw(entry.message.cast_mut()) };
|
|
90
|
+
}
|
|
91
|
+
if !entry.location.is_null() {
|
|
92
|
+
unsafe { crate::location_api::rdx_location_free(entry.location) };
|
|
93
|
+
entry.location = ptr::null_mut();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// boxed_slice drops here, releasing the buffer
|
|
97
|
+
}
|
|
98
|
+
// array drops here
|
|
99
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//! This file provides the C API for Document accessors
|
|
2
|
+
|
|
3
|
+
use libc::c_char;
|
|
4
|
+
use std::ffi::CString;
|
|
5
|
+
use std::ptr;
|
|
6
|
+
|
|
7
|
+
use crate::definition_api::{DefinitionKind, DefinitionsIter, rdx_definitions_iter_new_from_ids};
|
|
8
|
+
use crate::graph_api::{GraphPointer, with_graph};
|
|
9
|
+
use rubydex::model::ids::UriId;
|
|
10
|
+
|
|
11
|
+
/// Returns the UTF-8 URI string for a document id.
|
|
12
|
+
/// Caller must free with `free_c_string`.
|
|
13
|
+
///
|
|
14
|
+
/// # Safety
|
|
15
|
+
///
|
|
16
|
+
/// Assumes pointer is valid.
|
|
17
|
+
///
|
|
18
|
+
/// # Panics
|
|
19
|
+
///
|
|
20
|
+
/// This function will panic if the URI pointer is invalid.
|
|
21
|
+
#[unsafe(no_mangle)]
|
|
22
|
+
pub unsafe extern "C" fn rdx_document_uri(pointer: GraphPointer, uri_id: i64) -> *const c_char {
|
|
23
|
+
with_graph(pointer, |graph| {
|
|
24
|
+
let uri_id = UriId::new(uri_id);
|
|
25
|
+
if let Some(doc) = graph.documents().get(&uri_id) {
|
|
26
|
+
CString::new(doc.uri()).unwrap().into_raw().cast_const()
|
|
27
|
+
} else {
|
|
28
|
+
ptr::null()
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// An iterator over definition IDs and kinds for a given document (URI)
|
|
34
|
+
///
|
|
35
|
+
/// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
|
|
36
|
+
// Use shared DefinitionsIter directly in signatures
|
|
37
|
+
/// Creates a new iterator over definition IDs for a given document by snapshotting the current set of IDs.
|
|
38
|
+
///
|
|
39
|
+
/// # Safety
|
|
40
|
+
///
|
|
41
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
42
|
+
/// - The returned pointer must be freed with `rdx_document_definitions_iter_free`.
|
|
43
|
+
#[unsafe(no_mangle)]
|
|
44
|
+
pub unsafe extern "C" fn rdx_document_definitions_iter_new(pointer: GraphPointer, uri_id: i64) -> *mut DefinitionsIter {
|
|
45
|
+
// Snapshot the IDs and kinds at iterator creation to avoid borrowing across FFI calls
|
|
46
|
+
with_graph(pointer, |graph| {
|
|
47
|
+
let uri_id = UriId::new(uri_id);
|
|
48
|
+
if let Some(doc) = graph.documents().get(&uri_id) {
|
|
49
|
+
rdx_definitions_iter_new_from_ids(graph, doc.definitions())
|
|
50
|
+
} else {
|
|
51
|
+
DefinitionsIter::new(Vec::<(i64, DefinitionKind)>::new().into_boxed_slice())
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
}
|