rubydex 0.1.0.beta11 → 0.1.0.beta13
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/LICENSE.txt +23 -23
- data/README.md +125 -125
- data/THIRD_PARTY_LICENSES.html +2018 -945
- data/exe/rdx +47 -47
- data/ext/rubydex/declaration.c +453 -388
- data/ext/rubydex/declaration.h +23 -23
- data/ext/rubydex/definition.c +284 -197
- data/ext/rubydex/definition.h +28 -28
- data/ext/rubydex/diagnostic.c +6 -6
- data/ext/rubydex/diagnostic.h +11 -11
- data/ext/rubydex/document.c +97 -98
- data/ext/rubydex/document.h +10 -10
- data/ext/rubydex/extconf.rb +146 -127
- data/ext/rubydex/graph.c +701 -512
- data/ext/rubydex/graph.h +10 -10
- data/ext/rubydex/handle.h +44 -44
- data/ext/rubydex/location.c +22 -22
- data/ext/rubydex/location.h +15 -15
- data/ext/rubydex/reference.c +123 -104
- data/ext/rubydex/reference.h +15 -16
- data/ext/rubydex/rubydex.c +22 -22
- data/ext/rubydex/utils.c +108 -86
- data/ext/rubydex/utils.h +34 -28
- data/lib/rubydex/comment.rb +17 -17
- data/lib/rubydex/declaration.rb +11 -0
- data/lib/rubydex/diagnostic.rb +21 -21
- data/lib/rubydex/failures.rb +15 -15
- data/lib/rubydex/graph.rb +98 -92
- data/lib/rubydex/keyword.rb +17 -0
- data/lib/rubydex/keyword_parameter.rb +13 -0
- data/lib/rubydex/location.rb +90 -90
- data/lib/rubydex/mixin.rb +22 -0
- data/lib/rubydex/version.rb +5 -5
- data/lib/rubydex.rb +24 -20
- data/rbi/rubydex.rbi +425 -310
- data/rust/Cargo.lock +1851 -1851
- data/rust/Cargo.toml +29 -29
- data/rust/about.toml +10 -10
- data/rust/{about.hbs → about_templates/about.hbs} +81 -78
- data/rust/about_templates/mingw_licenses.hbs +1071 -0
- data/rust/rubydex/Cargo.toml +42 -42
- data/rust/rubydex/src/compile_assertions.rs +13 -13
- data/rust/rubydex/src/diagnostic.rs +110 -109
- data/rust/rubydex/src/errors.rs +28 -28
- data/rust/rubydex/src/indexing/local_graph.rs +224 -224
- data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -1554
- data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -6753
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
- data/rust/rubydex/src/indexing.rs +210 -210
- data/rust/rubydex/src/integrity.rs +279 -278
- data/rust/rubydex/src/job_queue.rs +199 -205
- data/rust/rubydex/src/lib.rs +17 -17
- data/rust/rubydex/src/listing.rs +371 -272
- data/rust/rubydex/src/main.rs +160 -160
- data/rust/rubydex/src/model/built_in.rs +83 -0
- data/rust/rubydex/src/model/comment.rs +24 -24
- data/rust/rubydex/src/model/declaration.rs +679 -588
- data/rust/rubydex/src/model/definitions.rs +1682 -1602
- data/rust/rubydex/src/model/document.rs +222 -252
- data/rust/rubydex/src/model/encoding.rs +22 -22
- data/rust/rubydex/src/model/graph.rs +3782 -3556
- data/rust/rubydex/src/model/id.rs +110 -110
- data/rust/rubydex/src/model/identity_maps.rs +58 -58
- data/rust/rubydex/src/model/ids.rs +60 -38
- data/rust/rubydex/src/model/keywords.rs +256 -256
- data/rust/rubydex/src/model/name.rs +298 -298
- data/rust/rubydex/src/model/references.rs +111 -111
- data/rust/rubydex/src/model/string_ref.rs +50 -50
- data/rust/rubydex/src/model/visibility.rs +41 -41
- data/rust/rubydex/src/model.rs +15 -14
- data/rust/rubydex/src/offset.rs +147 -147
- data/rust/rubydex/src/position.rs +6 -6
- data/rust/rubydex/src/query.rs +1841 -1700
- data/rust/rubydex/src/resolution.rs +1852 -5895
- data/rust/rubydex/src/resolution_tests.rs +4701 -0
- data/rust/rubydex/src/stats/memory.rs +71 -71
- data/rust/rubydex/src/stats/orphan_report.rs +264 -263
- data/rust/rubydex/src/stats/timer.rs +127 -127
- data/rust/rubydex/src/stats.rs +11 -11
- data/rust/rubydex/src/test_utils/context.rs +226 -226
- data/rust/rubydex/src/test_utils/graph_test.rs +730 -679
- data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -602
- data/rust/rubydex/src/test_utils.rs +52 -52
- data/rust/rubydex/src/visualization/dot.rs +192 -176
- data/rust/rubydex/src/visualization.rs +6 -6
- data/rust/rubydex/tests/cli.rs +185 -167
- data/rust/rubydex-mcp/Cargo.toml +28 -28
- data/rust/rubydex-mcp/src/main.rs +48 -48
- data/rust/rubydex-mcp/src/server.rs +1145 -1145
- data/rust/rubydex-mcp/src/tools.rs +49 -49
- data/rust/rubydex-mcp/tests/mcp.rs +302 -302
- data/rust/rubydex-sys/Cargo.toml +20 -20
- data/rust/rubydex-sys/build.rs +14 -14
- data/rust/rubydex-sys/cbindgen.toml +12 -12
- data/rust/rubydex-sys/src/declaration_api.rs +485 -469
- data/rust/rubydex-sys/src/definition_api.rs +443 -352
- data/rust/rubydex-sys/src/diagnostic_api.rs +99 -99
- data/rust/rubydex-sys/src/document_api.rs +85 -54
- data/rust/rubydex-sys/src/graph_api.rs +1017 -700
- data/rust/rubydex-sys/src/lib.rs +79 -9
- data/rust/rubydex-sys/src/location_api.rs +79 -79
- data/rust/rubydex-sys/src/name_api.rs +187 -135
- data/rust/rubydex-sys/src/reference_api.rs +267 -195
- data/rust/rubydex-sys/src/utils.rs +70 -70
- data/rust/rustfmt.toml +2 -2
- metadata +16 -9
- data/lib/rubydex/librubydex_sys.so +0 -0
data/rust/rubydex-sys/src/lib.rs
CHANGED
|
@@ -1,9 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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;
|
|
@@ -1,79 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,135 +1,187 @@
|
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let
|
|
123
|
-
assert_eq!(StringId::from("
|
|
124
|
-
assert!(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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. Returns `None` if the constant name contains no valid identifier parts (e.g.: `""`, `"::"`, `"Foo::"`).
|
|
10
|
+
pub fn nesting_stack_to_name_id(
|
|
11
|
+
graph: &mut Graph,
|
|
12
|
+
const_name: &str,
|
|
13
|
+
nesting: Vec<String>,
|
|
14
|
+
) -> Option<(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
|
+
process_qualified_name(
|
|
21
|
+
graph,
|
|
22
|
+
&entry,
|
|
23
|
+
&mut current_name,
|
|
24
|
+
&mut current_nesting,
|
|
25
|
+
&mut names_to_untrack,
|
|
26
|
+
);
|
|
27
|
+
current_nesting = current_name.as_ref().copied();
|
|
28
|
+
current_name = ParentScope::None;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process_qualified_name(
|
|
32
|
+
graph,
|
|
33
|
+
const_name,
|
|
34
|
+
&mut current_name,
|
|
35
|
+
&mut current_nesting,
|
|
36
|
+
&mut names_to_untrack,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
let (ParentScope::Some(name_id) | ParentScope::Attached(name_id)) = current_name else {
|
|
40
|
+
return None;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
Some((name_id, names_to_untrack))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Processes a qualified name (e.g., `"Foo::Bar"` or `"<Foo>"`) by splitting on `"::"` and registering each part in the
|
|
47
|
+
/// graph. Singleton class names (starting with `<`) use `ParentScope::Attached` and `nesting=None`, matching how the
|
|
48
|
+
/// indexer creates them. When a singleton is the first part (i.e., `current_name` has no parent), `current_nesting` is
|
|
49
|
+
/// used as the attachment point.
|
|
50
|
+
fn process_qualified_name(
|
|
51
|
+
graph: &mut Graph,
|
|
52
|
+
qualified_name: &str,
|
|
53
|
+
current_name: &mut ParentScope,
|
|
54
|
+
current_nesting: &mut Option<NameId>,
|
|
55
|
+
names_to_untrack: &mut Vec<NameId>,
|
|
56
|
+
) {
|
|
57
|
+
for part in qualified_name.split("::") {
|
|
58
|
+
if part.is_empty() {
|
|
59
|
+
*current_name = ParentScope::TopLevel;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let (parent_scope, nesting_for_part) = if part.starts_with('<') {
|
|
64
|
+
let attached = match *current_name {
|
|
65
|
+
ParentScope::Some(id) | ParentScope::Attached(id) => ParentScope::Attached(id),
|
|
66
|
+
_ => current_nesting.map_or(ParentScope::None, ParentScope::Attached),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
(attached, None)
|
|
70
|
+
} else {
|
|
71
|
+
(*current_name, *current_nesting)
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let str_id = graph.intern_string(part.to_owned());
|
|
75
|
+
let name_id = graph.add_name(Name::new(str_id, parent_scope, nesting_for_part));
|
|
76
|
+
names_to_untrack.push(name_id);
|
|
77
|
+
*current_name = ParentScope::Some(name_id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#[cfg(test)]
|
|
82
|
+
mod tests {
|
|
83
|
+
use rubydex::model::ids::StringId;
|
|
84
|
+
|
|
85
|
+
use super::*;
|
|
86
|
+
|
|
87
|
+
#[test]
|
|
88
|
+
fn nesting_is_converted_to_name_id() {
|
|
89
|
+
let mut graph = Graph::new();
|
|
90
|
+
|
|
91
|
+
let (name_id, _) = nesting_stack_to_name_id(
|
|
92
|
+
&mut graph,
|
|
93
|
+
"Some::CONST",
|
|
94
|
+
vec!["Foo".into(), "Bar::Zip".into(), "Qux".into()],
|
|
95
|
+
)
|
|
96
|
+
.unwrap();
|
|
97
|
+
|
|
98
|
+
let const_name = graph.names().get(&name_id).unwrap();
|
|
99
|
+
assert_eq!(StringId::from("CONST"), *const_name.str());
|
|
100
|
+
|
|
101
|
+
let some_name = graph
|
|
102
|
+
.names()
|
|
103
|
+
.get(&const_name.parent_scope().expect("Parent scope should exist"))
|
|
104
|
+
.unwrap();
|
|
105
|
+
assert_eq!(StringId::from("Some"), *some_name.str());
|
|
106
|
+
assert_eq!(const_name.nesting(), some_name.nesting());
|
|
107
|
+
|
|
108
|
+
let qux_name = graph.names().get(&some_name.nesting().unwrap()).unwrap();
|
|
109
|
+
assert_eq!(StringId::from("Qux"), *qux_name.str());
|
|
110
|
+
assert!(qux_name.parent_scope().is_none());
|
|
111
|
+
|
|
112
|
+
let zip_name = graph.names().get(&qux_name.nesting().unwrap()).unwrap();
|
|
113
|
+
assert_eq!(StringId::from("Zip"), *zip_name.str());
|
|
114
|
+
|
|
115
|
+
let bar_name = graph
|
|
116
|
+
.names()
|
|
117
|
+
.get(&zip_name.parent_scope().expect("Parent scope should exist"))
|
|
118
|
+
.unwrap();
|
|
119
|
+
assert_eq!(StringId::from("Bar"), *bar_name.str());
|
|
120
|
+
assert_eq!(zip_name.nesting(), bar_name.nesting());
|
|
121
|
+
|
|
122
|
+
let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
|
|
123
|
+
assert_eq!(StringId::from("Foo"), *foo_name.str());
|
|
124
|
+
assert!(foo_name.parent_scope().is_none());
|
|
125
|
+
assert!(foo_name.nesting().is_none());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
fn top_level_reference_is_converted_to_name_id() {
|
|
130
|
+
let mut graph = Graph::new();
|
|
131
|
+
|
|
132
|
+
let (name_id, _) = nesting_stack_to_name_id(&mut graph, "::CONST", vec!["Foo".into()]).unwrap();
|
|
133
|
+
|
|
134
|
+
let const_name = graph.names().get(&name_id).unwrap();
|
|
135
|
+
assert_eq!(StringId::from("CONST"), *const_name.str());
|
|
136
|
+
assert!(const_name.parent_scope().is_top_level());
|
|
137
|
+
|
|
138
|
+
let foo_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
|
|
139
|
+
assert_eq!(StringId::from("Foo"), *foo_name.str());
|
|
140
|
+
assert!(foo_name.nesting().is_none());
|
|
141
|
+
assert!(foo_name.parent_scope().is_none());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[test]
|
|
145
|
+
fn singleton_class_names_use_attached_parent_scope() {
|
|
146
|
+
let mut graph = Graph::new();
|
|
147
|
+
|
|
148
|
+
let (name_id, _) = nesting_stack_to_name_id(&mut graph, "CONST", vec!["Foo".into(), "<Foo>".into()]).unwrap();
|
|
149
|
+
|
|
150
|
+
let const_name = graph.names().get(&name_id).unwrap();
|
|
151
|
+
assert_eq!(StringId::from("CONST"), *const_name.str());
|
|
152
|
+
|
|
153
|
+
// The nesting should be <Foo> with an Attached parent scope
|
|
154
|
+
let singleton_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
|
|
155
|
+
assert_eq!(StringId::from("<Foo>"), *singleton_name.str());
|
|
156
|
+
assert!(
|
|
157
|
+
matches!(singleton_name.parent_scope(), ParentScope::Attached(_)),
|
|
158
|
+
"Expected ParentScope::Attached, got {}",
|
|
159
|
+
singleton_name.parent_scope()
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// The attached parent should be Foo
|
|
163
|
+
let foo_id = singleton_name.parent_scope().expect("Attached should have an id");
|
|
164
|
+
let foo_name = graph.names().get(&foo_id).unwrap();
|
|
165
|
+
assert_eq!(StringId::from("Foo"), *foo_name.str());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#[test]
|
|
169
|
+
fn top_level_nesting_is_converted_to_name_id() {
|
|
170
|
+
let mut graph = Graph::new();
|
|
171
|
+
|
|
172
|
+
let (name_id, _) = nesting_stack_to_name_id(&mut graph, "CONST", vec!["Foo".into(), "::Bar".into()]).unwrap();
|
|
173
|
+
|
|
174
|
+
let const_name = graph.names().get(&name_id).unwrap();
|
|
175
|
+
assert_eq!(StringId::from("CONST"), *const_name.str());
|
|
176
|
+
assert!(const_name.parent_scope().is_none());
|
|
177
|
+
|
|
178
|
+
let bar_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
|
|
179
|
+
assert_eq!(StringId::from("Bar"), *bar_name.str());
|
|
180
|
+
assert!(bar_name.parent_scope().is_top_level());
|
|
181
|
+
|
|
182
|
+
let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
|
|
183
|
+
assert_eq!(StringId::from("Foo"), *foo_name.str());
|
|
184
|
+
assert!(foo_name.parent_scope().is_none());
|
|
185
|
+
assert!(foo_name.nesting().is_none());
|
|
186
|
+
}
|
|
187
|
+
}
|