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,493 @@
|
|
|
1
|
+
//! This file provides the C API for the Graph object
|
|
2
|
+
|
|
3
|
+
use crate::reference_api::{ReferenceKind, ReferencesIter};
|
|
4
|
+
use crate::{name_api, utils};
|
|
5
|
+
use libc::{c_char, c_void};
|
|
6
|
+
use rubydex::model::encoding::Encoding;
|
|
7
|
+
use rubydex::model::graph::Graph;
|
|
8
|
+
use rubydex::model::ids::DeclarationId;
|
|
9
|
+
use rubydex::resolution::Resolver;
|
|
10
|
+
use rubydex::{indexing, listing, query};
|
|
11
|
+
use std::ffi::CString;
|
|
12
|
+
use std::{mem, ptr};
|
|
13
|
+
|
|
14
|
+
pub type GraphPointer = *mut c_void;
|
|
15
|
+
|
|
16
|
+
/// Creates a new graph within a mutex. This is meant to be used when creating new Graph objects in Ruby
|
|
17
|
+
#[unsafe(no_mangle)]
|
|
18
|
+
pub extern "C" fn rdx_graph_new() -> GraphPointer {
|
|
19
|
+
Box::into_raw(Box::new(Graph::new())) as GraphPointer
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Frees a Graph through its pointer
|
|
23
|
+
#[unsafe(no_mangle)]
|
|
24
|
+
pub extern "C" fn rdx_graph_free(pointer: GraphPointer) {
|
|
25
|
+
unsafe {
|
|
26
|
+
let _ = Box::from_raw(pointer.cast::<Graph>());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn with_graph<F, T>(pointer: GraphPointer, action: F) -> T
|
|
31
|
+
where
|
|
32
|
+
F: FnOnce(&mut Graph) -> T,
|
|
33
|
+
{
|
|
34
|
+
let mut graph = unsafe { Box::from_raw(pointer.cast::<Graph>()) };
|
|
35
|
+
let result = action(&mut graph);
|
|
36
|
+
mem::forget(graph);
|
|
37
|
+
result
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Searches the graph based on query and returns all declarations that match
|
|
41
|
+
///
|
|
42
|
+
/// # Safety
|
|
43
|
+
///
|
|
44
|
+
/// Expects both the graph and the query pointers to be valid
|
|
45
|
+
#[unsafe(no_mangle)]
|
|
46
|
+
pub unsafe extern "C" fn rdx_graph_declarations_search(
|
|
47
|
+
pointer: GraphPointer,
|
|
48
|
+
c_query: *const c_char,
|
|
49
|
+
) -> *mut DeclarationsIter {
|
|
50
|
+
let Ok(query) = (unsafe { utils::convert_char_ptr_to_string(c_query) }) else {
|
|
51
|
+
return ptr::null_mut();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let ids = with_graph(pointer, |graph| {
|
|
55
|
+
query::declaration_search(graph, &query)
|
|
56
|
+
.into_iter()
|
|
57
|
+
.map(|id| *id)
|
|
58
|
+
.collect::<Vec<i64>>()
|
|
59
|
+
.into_boxed_slice()
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
Box::into_raw(Box::new(DeclarationsIter { ids, index: 0 }))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// # Panics
|
|
66
|
+
///
|
|
67
|
+
/// Will panic if the nesting cannot be transformed into a vector of strings
|
|
68
|
+
///
|
|
69
|
+
/// # Safety
|
|
70
|
+
///
|
|
71
|
+
/// Assumes that the `const_name` and `nesting` pointer are valid
|
|
72
|
+
#[unsafe(no_mangle)]
|
|
73
|
+
pub unsafe extern "C" fn rdx_graph_resolve_constant(
|
|
74
|
+
pointer: GraphPointer,
|
|
75
|
+
const_name: *const c_char,
|
|
76
|
+
nesting: *const *const c_char,
|
|
77
|
+
count: usize,
|
|
78
|
+
) -> *const i64 {
|
|
79
|
+
with_graph(pointer, |graph| {
|
|
80
|
+
let nesting: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(nesting, count).unwrap() };
|
|
81
|
+
let const_name: String = unsafe { utils::convert_char_ptr_to_string(const_name).unwrap() };
|
|
82
|
+
let (name_id, names_to_untrack) = name_api::nesting_stack_to_name_id(graph, &const_name, nesting);
|
|
83
|
+
|
|
84
|
+
let mut resolver = Resolver::new(graph);
|
|
85
|
+
|
|
86
|
+
let resolved_id = match resolver.resolve_constant(name_id) {
|
|
87
|
+
Some(id) => Box::into_raw(Box::new(*id)).cast_const(),
|
|
88
|
+
None => ptr::null(),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for name_id in names_to_untrack {
|
|
92
|
+
graph.untrack_name(name_id);
|
|
93
|
+
}
|
|
94
|
+
resolved_id
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Indexes all given file paths in parallel using the provided Graph pointer
|
|
99
|
+
///
|
|
100
|
+
/// # Panics
|
|
101
|
+
///
|
|
102
|
+
/// Will panic if the given array of C string file paths cannot be converted to a Vec<String>
|
|
103
|
+
///
|
|
104
|
+
/// # Safety
|
|
105
|
+
///
|
|
106
|
+
/// This function is unsafe because it dereferences raw pointers coming from C. The caller has to ensure that the Ruby
|
|
107
|
+
/// VM will not free the pointers related to the string array while they are in use by Rust
|
|
108
|
+
#[unsafe(no_mangle)]
|
|
109
|
+
pub unsafe extern "C" fn rdx_index_all(
|
|
110
|
+
pointer: GraphPointer,
|
|
111
|
+
file_paths: *const *const c_char,
|
|
112
|
+
count: usize,
|
|
113
|
+
) -> *const c_char {
|
|
114
|
+
let file_paths: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(file_paths, count).unwrap() };
|
|
115
|
+
let (file_paths, errors) = listing::collect_file_paths(file_paths);
|
|
116
|
+
|
|
117
|
+
if !errors.is_empty() {
|
|
118
|
+
let error_messages = errors
|
|
119
|
+
.iter()
|
|
120
|
+
.map(std::string::ToString::to_string)
|
|
121
|
+
.collect::<Vec<_>>()
|
|
122
|
+
.join("\n");
|
|
123
|
+
|
|
124
|
+
return CString::new(error_messages).unwrap().into_raw().cast_const();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
with_graph(pointer, |graph| {
|
|
128
|
+
let errors = indexing::index_files(graph, file_paths);
|
|
129
|
+
|
|
130
|
+
if !errors.is_empty() {
|
|
131
|
+
let error_messages = errors
|
|
132
|
+
.iter()
|
|
133
|
+
.map(std::string::ToString::to_string)
|
|
134
|
+
.collect::<Vec<_>>()
|
|
135
|
+
.join("\n");
|
|
136
|
+
|
|
137
|
+
return CString::new(error_messages).unwrap().into_raw().cast_const();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ptr::null()
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Runs the resolver to compute declarations, ownership and related structures
|
|
145
|
+
#[unsafe(no_mangle)]
|
|
146
|
+
pub extern "C" fn rdx_graph_resolve(pointer: GraphPointer) {
|
|
147
|
+
with_graph(pointer, |graph| {
|
|
148
|
+
let mut resolver = Resolver::new(graph);
|
|
149
|
+
resolver.resolve_all();
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// # Safety
|
|
154
|
+
///
|
|
155
|
+
/// Expects both the graph pointer and encoding string pointer to be valid
|
|
156
|
+
#[unsafe(no_mangle)]
|
|
157
|
+
pub unsafe extern "C" fn rdx_graph_set_encoding(pointer: GraphPointer, encoding_str: *const c_char) -> bool {
|
|
158
|
+
let Ok(encoding) = (unsafe { utils::convert_char_ptr_to_string(encoding_str) }) else {
|
|
159
|
+
return false;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
let encoding_variant = match encoding.as_str() {
|
|
163
|
+
"utf8" => Encoding::Utf8,
|
|
164
|
+
"utf16" => Encoding::Utf16,
|
|
165
|
+
"utf32" => Encoding::Utf32,
|
|
166
|
+
_ => {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
with_graph(pointer, |graph| {
|
|
172
|
+
graph.set_encoding(encoding_variant);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
true
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/// An iterator over declaration IDs
|
|
179
|
+
///
|
|
180
|
+
/// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
|
|
181
|
+
#[derive(Debug)]
|
|
182
|
+
pub struct DeclarationsIter {
|
|
183
|
+
/// The snapshot of declaration IDs
|
|
184
|
+
ids: Box<[i64]>,
|
|
185
|
+
/// The current index of the iterator
|
|
186
|
+
index: usize,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Creates a new iterator over declaration IDs by snapshotting the current set of IDs.
|
|
190
|
+
///
|
|
191
|
+
/// # Safety
|
|
192
|
+
///
|
|
193
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
194
|
+
/// - The returned pointer must be freed with `rdx_graph_declarations_iter_free`.
|
|
195
|
+
///
|
|
196
|
+
/// # Panics
|
|
197
|
+
///
|
|
198
|
+
/// Will panic if acquiring a read lock on the graph's declarations fails
|
|
199
|
+
#[unsafe(no_mangle)]
|
|
200
|
+
pub unsafe extern "C" fn rdx_graph_declarations_iter_new(pointer: GraphPointer) -> *mut DeclarationsIter {
|
|
201
|
+
// Snapshot the IDs at iterator creation to avoid borrowing across FFI calls
|
|
202
|
+
let ids = with_graph(pointer, |graph| {
|
|
203
|
+
graph
|
|
204
|
+
.declarations()
|
|
205
|
+
.keys()
|
|
206
|
+
.map(|name_id| **name_id)
|
|
207
|
+
.collect::<Vec<i64>>()
|
|
208
|
+
.into_boxed_slice()
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
Box::into_raw(Box::new(DeclarationsIter { ids, index: 0 }))
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Returns the total number of IDs in the iterator snapshot.
|
|
215
|
+
///
|
|
216
|
+
/// # Safety
|
|
217
|
+
///
|
|
218
|
+
/// - `iter` must be a valid pointer previously returned by `rdx_graph_declarations_iter_new`.
|
|
219
|
+
#[unsafe(no_mangle)]
|
|
220
|
+
pub unsafe extern "C" fn rdx_graph_declarations_iter_len(iter: *const DeclarationsIter) -> usize {
|
|
221
|
+
if iter.is_null() {
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
unsafe { (&*iter).ids.len() }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/// Advances the iterator and writes the next ID into `out_id`.
|
|
229
|
+
/// Returns `true` if an ID was written, or `false` if the iterator is exhausted or inputs are invalid.
|
|
230
|
+
///
|
|
231
|
+
/// # Safety
|
|
232
|
+
///
|
|
233
|
+
/// - `iter` must be a valid pointer previously returned by `rdx_graph_declarations_iter_new`.
|
|
234
|
+
/// - `out_id` must be a valid, writable pointer.
|
|
235
|
+
#[unsafe(no_mangle)]
|
|
236
|
+
pub unsafe extern "C" fn rdx_graph_declarations_iter_next(iter: *mut DeclarationsIter, out_id: *mut i64) -> bool {
|
|
237
|
+
if iter.is_null() || out_id.is_null() {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let it = unsafe { &mut *iter };
|
|
242
|
+
if it.index >= it.ids.len() {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let id = it.ids[it.index];
|
|
247
|
+
it.index += 1;
|
|
248
|
+
unsafe { *out_id = id };
|
|
249
|
+
|
|
250
|
+
true
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// Frees an iterator created by `rdx_graph_declarations_iter_new`.
|
|
254
|
+
///
|
|
255
|
+
/// # Safety
|
|
256
|
+
///
|
|
257
|
+
/// - `iter` must be a pointer previously returned by `rdx_graph_declarations_iter_new`.
|
|
258
|
+
/// - `iter` must not be used after being freed.
|
|
259
|
+
#[unsafe(no_mangle)]
|
|
260
|
+
pub unsafe extern "C" fn rdx_graph_declarations_iter_free(iter: *mut DeclarationsIter) {
|
|
261
|
+
if iter.is_null() {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
unsafe {
|
|
266
|
+
let _ = Box::from_raw(iter);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/// An iterator over document (URI) IDs
|
|
271
|
+
///
|
|
272
|
+
/// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
|
|
273
|
+
#[derive(Debug)]
|
|
274
|
+
pub struct DocumentsIter {
|
|
275
|
+
/// The snapshot of document (URI) IDs
|
|
276
|
+
ids: Box<[i64]>,
|
|
277
|
+
/// The current index of the iterator
|
|
278
|
+
index: usize,
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/// Creates a new iterator over document (URI) IDs by snapshotting the current set of IDs.
|
|
282
|
+
///
|
|
283
|
+
/// # Safety
|
|
284
|
+
///
|
|
285
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
286
|
+
/// - The returned pointer must be freed with `rdx_graph_documents_iter_free`.
|
|
287
|
+
#[unsafe(no_mangle)]
|
|
288
|
+
pub unsafe extern "C" fn rdx_graph_documents_iter_new(pointer: GraphPointer) -> *mut DocumentsIter {
|
|
289
|
+
// Snapshot the IDs at iterator creation to avoid borrowing across FFI calls
|
|
290
|
+
let ids = with_graph(pointer, |graph| {
|
|
291
|
+
graph
|
|
292
|
+
.documents()
|
|
293
|
+
.keys()
|
|
294
|
+
.map(|uri_id| **uri_id)
|
|
295
|
+
.collect::<Vec<i64>>()
|
|
296
|
+
.into_boxed_slice()
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
Box::into_raw(Box::new(DocumentsIter { ids, index: 0 }))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/// Returns the total number of IDs in the iterator snapshot.
|
|
303
|
+
///
|
|
304
|
+
/// # Safety
|
|
305
|
+
///
|
|
306
|
+
/// - `iter` must be a valid pointer previously returned by `rdx_graph_documents_iter_new`.
|
|
307
|
+
#[unsafe(no_mangle)]
|
|
308
|
+
pub unsafe extern "C" fn rdx_graph_documents_iter_len(iter: *const DocumentsIter) -> usize {
|
|
309
|
+
if iter.is_null() {
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
unsafe { (&*iter).ids.len() }
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// Advances the iterator and writes the next ID into `out_id`.
|
|
317
|
+
/// Returns `true` if an ID was written, or `false` if the iterator is exhausted or inputs are invalid.
|
|
318
|
+
///
|
|
319
|
+
/// # Safety
|
|
320
|
+
///
|
|
321
|
+
/// - `iter` must be a valid pointer previously returned by `rdx_graph_documents_iter_new`.
|
|
322
|
+
/// - `out_id` must be a valid, writable pointer.
|
|
323
|
+
#[unsafe(no_mangle)]
|
|
324
|
+
pub unsafe extern "C" fn rdx_graph_documents_iter_next(iter: *mut DocumentsIter, out_id: *mut i64) -> bool {
|
|
325
|
+
if iter.is_null() || out_id.is_null() {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let it = unsafe { &mut *iter };
|
|
330
|
+
if it.index >= it.ids.len() {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let id = it.ids[it.index];
|
|
335
|
+
it.index += 1;
|
|
336
|
+
unsafe { *out_id = id };
|
|
337
|
+
|
|
338
|
+
true
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/// Frees an iterator created by `rdx_graph_documents_iter_new`.
|
|
342
|
+
///
|
|
343
|
+
/// # Safety
|
|
344
|
+
///
|
|
345
|
+
/// - `iter` must be a pointer previously returned by `rdx_graph_documents_iter_new`.
|
|
346
|
+
/// - `iter` must not be used after being freed.
|
|
347
|
+
#[unsafe(no_mangle)]
|
|
348
|
+
pub unsafe extern "C" fn rdx_graph_documents_iter_free(iter: *mut DocumentsIter) {
|
|
349
|
+
if iter.is_null() {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
unsafe {
|
|
354
|
+
let _ = Box::from_raw(iter);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/// Attempts to resolve a declaration from a fully-qualified name string.
|
|
359
|
+
/// Returns a pointer to the internal ID if it exists, or NULL if it does not.
|
|
360
|
+
///
|
|
361
|
+
/// # Safety
|
|
362
|
+
/// - `pointer` must be a valid `GraphPointer`
|
|
363
|
+
/// - `name` must be a valid, null-terminated UTF-8 string
|
|
364
|
+
/// - `out_id` must be a valid, writable pointer
|
|
365
|
+
///
|
|
366
|
+
/// # Panics
|
|
367
|
+
///
|
|
368
|
+
/// Will panic if acquiring a read lock on the graph's declarations fails
|
|
369
|
+
#[unsafe(no_mangle)]
|
|
370
|
+
pub unsafe extern "C" fn rdx_graph_get_declaration(pointer: GraphPointer, name: *const c_char) -> *const i64 {
|
|
371
|
+
let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
|
|
372
|
+
return ptr::null();
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
with_graph(pointer, |graph| {
|
|
376
|
+
// TODO: We should perform name resolution instead of accessing the graph with the canonical ID
|
|
377
|
+
let decl_id = DeclarationId::from(name_str.as_str());
|
|
378
|
+
if graph.declarations().contains_key(&decl_id) {
|
|
379
|
+
Box::into_raw(Box::new(*decl_id)).cast_const()
|
|
380
|
+
} else {
|
|
381
|
+
ptr::null()
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/// Creates a new iterator over constant references by snapshotting the current set of (id, kind) pairs.
|
|
387
|
+
///
|
|
388
|
+
/// # Safety
|
|
389
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
390
|
+
#[unsafe(no_mangle)]
|
|
391
|
+
pub unsafe extern "C" fn rdx_graph_constant_references_iter_new(pointer: GraphPointer) -> *mut ReferencesIter {
|
|
392
|
+
with_graph(pointer, |graph| {
|
|
393
|
+
let refs: Vec<(i64, ReferenceKind)> = graph
|
|
394
|
+
.constant_references()
|
|
395
|
+
.keys()
|
|
396
|
+
.map(|id| (**id, ReferenceKind::Constant))
|
|
397
|
+
.collect();
|
|
398
|
+
|
|
399
|
+
ReferencesIter::new(refs.into_boxed_slice())
|
|
400
|
+
})
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/// Creates a new iterator over method references by snapshotting the current set of (id, kind) pairs.
|
|
404
|
+
///
|
|
405
|
+
/// # Safety
|
|
406
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
407
|
+
#[unsafe(no_mangle)]
|
|
408
|
+
pub unsafe extern "C" fn rdx_graph_method_references_iter_new(pointer: GraphPointer) -> *mut ReferencesIter {
|
|
409
|
+
with_graph(pointer, |graph| {
|
|
410
|
+
let refs: Vec<(i64, ReferenceKind)> = graph
|
|
411
|
+
.method_references()
|
|
412
|
+
.keys()
|
|
413
|
+
.map(|id| (**id, ReferenceKind::Method))
|
|
414
|
+
.collect();
|
|
415
|
+
|
|
416
|
+
ReferencesIter::new(refs.into_boxed_slice())
|
|
417
|
+
})
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#[cfg(test)]
|
|
421
|
+
mod tests {
|
|
422
|
+
use rubydex::indexing::ruby_indexer::RubyIndexer;
|
|
423
|
+
|
|
424
|
+
use super::*;
|
|
425
|
+
|
|
426
|
+
#[test]
|
|
427
|
+
fn names_are_untracked_after_resolving_constant() {
|
|
428
|
+
let mut indexer = RubyIndexer::new(
|
|
429
|
+
"file:///foo.rb".into(),
|
|
430
|
+
"
|
|
431
|
+
class Foo
|
|
432
|
+
BAR = 1
|
|
433
|
+
end
|
|
434
|
+
",
|
|
435
|
+
);
|
|
436
|
+
indexer.index();
|
|
437
|
+
|
|
438
|
+
let mut graph = Graph::new();
|
|
439
|
+
graph.update(indexer.local_graph());
|
|
440
|
+
let mut resolver = Resolver::new(&mut graph);
|
|
441
|
+
resolver.resolve_all();
|
|
442
|
+
|
|
443
|
+
assert_eq!(
|
|
444
|
+
1,
|
|
445
|
+
graph
|
|
446
|
+
.names()
|
|
447
|
+
.iter()
|
|
448
|
+
.find_map(|(_, name)| {
|
|
449
|
+
if graph.strings().get(name.str()).unwrap().as_str() == "BAR" {
|
|
450
|
+
Some(name)
|
|
451
|
+
} else {
|
|
452
|
+
None
|
|
453
|
+
}
|
|
454
|
+
})
|
|
455
|
+
.unwrap()
|
|
456
|
+
.ref_count()
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
let graph_ptr = Box::into_raw(Box::new(graph)) as GraphPointer;
|
|
460
|
+
|
|
461
|
+
// Build the nesting array: ["Foo"] since BAR is inside class Foo
|
|
462
|
+
let nesting_strings: Vec<CString> = vec![CString::new("Foo").unwrap()];
|
|
463
|
+
let nesting_ptrs: Vec<*const c_char> = nesting_strings.iter().map(|s| s.as_ptr()).collect();
|
|
464
|
+
|
|
465
|
+
unsafe {
|
|
466
|
+
let id = rdx_graph_resolve_constant(
|
|
467
|
+
graph_ptr,
|
|
468
|
+
CString::new("BAR").unwrap().as_ptr(),
|
|
469
|
+
nesting_ptrs.as_ptr(),
|
|
470
|
+
nesting_ptrs.len(),
|
|
471
|
+
);
|
|
472
|
+
assert_eq!(*id, *DeclarationId::from("Foo::BAR"));
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
let graph = unsafe { Box::from_raw(graph_ptr.cast::<Graph>()) };
|
|
476
|
+
|
|
477
|
+
assert_eq!(
|
|
478
|
+
1,
|
|
479
|
+
graph
|
|
480
|
+
.names()
|
|
481
|
+
.iter()
|
|
482
|
+
.find_map(|(_, name)| {
|
|
483
|
+
if graph.strings().get(name.str()).unwrap().as_str() == "BAR" {
|
|
484
|
+
Some(name)
|
|
485
|
+
} else {
|
|
486
|
+
None
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
.unwrap()
|
|
490
|
+
.ref_count()
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
@@ -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 + 1,
|
|
41
|
+
end_line: wide_end_pos.line + 1,
|
|
42
|
+
start_column: wide_start_pos.col + 1,
|
|
43
|
+
end_column: wide_end_pos.col + 1,
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
Location {
|
|
47
|
+
uri: CString::new(document.uri()).unwrap().into_raw().cast_const(),
|
|
48
|
+
start_line: start_pos.line + 1,
|
|
49
|
+
end_line: end_pos.line + 1,
|
|
50
|
+
start_column: start_pos.col + 1,
|
|
51
|
+
end_column: end_pos.col + 1,
|
|
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,81 @@
|
|
|
1
|
+
use rubydex::model::{graph::Graph, ids::NameId, name::Name};
|
|
2
|
+
|
|
3
|
+
/// Takes a constant name and a nesting stack (e.g.: `["Foo", "Bar::Baz", "Qux"]`) and transforms it into a `NameId`,
|
|
4
|
+
/// registering each required part in the graph. Returns the `NameId` and a list of name ids that need to be untracked
|
|
5
|
+
/// afterwards
|
|
6
|
+
///
|
|
7
|
+
/// # Panics
|
|
8
|
+
///
|
|
9
|
+
/// Should not panic because `const_name` will always be turned into a name
|
|
10
|
+
pub fn nesting_stack_to_name_id(graph: &mut Graph, const_name: &str, nesting: Vec<String>) -> (NameId, Vec<NameId>) {
|
|
11
|
+
let mut current_nesting = None;
|
|
12
|
+
let mut current_name = None;
|
|
13
|
+
let mut names_to_untrack = Vec::new();
|
|
14
|
+
|
|
15
|
+
for entry in nesting {
|
|
16
|
+
for part in entry.split("::").map(String::from) {
|
|
17
|
+
let str_id = graph.intern_string(part);
|
|
18
|
+
let name_id = graph.add_name(Name::new(str_id, current_name, current_nesting));
|
|
19
|
+
names_to_untrack.push(name_id);
|
|
20
|
+
let new_name = Some(name_id);
|
|
21
|
+
current_name = new_name;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
current_nesting = current_name;
|
|
25
|
+
current_name = None;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for part in const_name.split("::").map(String::from) {
|
|
29
|
+
let str_id = graph.intern_string(part);
|
|
30
|
+
let name_id = graph.add_name(Name::new(str_id, current_name, current_nesting));
|
|
31
|
+
names_to_untrack.push(name_id);
|
|
32
|
+
let new_name = Some(name_id);
|
|
33
|
+
current_name = new_name;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
(
|
|
37
|
+
current_name.expect("The NameId cannot be None since it contains at least `const_name`"),
|
|
38
|
+
names_to_untrack,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[cfg(test)]
|
|
43
|
+
mod tests {
|
|
44
|
+
use rubydex::model::ids::StringId;
|
|
45
|
+
|
|
46
|
+
use super::*;
|
|
47
|
+
|
|
48
|
+
#[test]
|
|
49
|
+
fn nesting_is_converted_to_name_id() {
|
|
50
|
+
let mut graph = Graph::new();
|
|
51
|
+
|
|
52
|
+
let (name_id, _) = nesting_stack_to_name_id(
|
|
53
|
+
&mut graph,
|
|
54
|
+
"Some::CONST",
|
|
55
|
+
vec!["Foo".into(), "Bar::Zip".into(), "Qux".into()],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
let const_name = graph.names().get(&name_id).unwrap();
|
|
59
|
+
assert_eq!(StringId::from("CONST"), *const_name.str());
|
|
60
|
+
|
|
61
|
+
let some_name = graph.names().get(&const_name.parent_scope().unwrap()).unwrap();
|
|
62
|
+
assert_eq!(StringId::from("Some"), *some_name.str());
|
|
63
|
+
assert_eq!(const_name.nesting(), some_name.nesting());
|
|
64
|
+
|
|
65
|
+
let qux_name = graph.names().get(&some_name.nesting().unwrap()).unwrap();
|
|
66
|
+
assert_eq!(StringId::from("Qux"), *qux_name.str());
|
|
67
|
+
assert!(qux_name.parent_scope().is_none());
|
|
68
|
+
|
|
69
|
+
let zip_name = graph.names().get(&qux_name.nesting().unwrap()).unwrap();
|
|
70
|
+
assert_eq!(StringId::from("Zip"), *zip_name.str());
|
|
71
|
+
|
|
72
|
+
let bar_name = graph.names().get(&zip_name.parent_scope().unwrap()).unwrap();
|
|
73
|
+
assert_eq!(StringId::from("Bar"), *bar_name.str());
|
|
74
|
+
assert_eq!(zip_name.nesting(), bar_name.nesting());
|
|
75
|
+
|
|
76
|
+
let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
|
|
77
|
+
assert_eq!(StringId::from("Foo"), *foo_name.str());
|
|
78
|
+
assert!(foo_name.parent_scope().is_none());
|
|
79
|
+
assert!(foo_name.nesting().is_none());
|
|
80
|
+
}
|
|
81
|
+
}
|