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.
- checksums.yaml +7 -0
- data/LICENSE.txt +23 -0
- data/README.md +125 -0
- data/THIRD_PARTY_LICENSES.html +4562 -0
- data/exe/rdx +47 -0
- data/ext/rubydex/declaration.c +453 -0
- data/ext/rubydex/declaration.h +23 -0
- data/ext/rubydex/definition.c +284 -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 +97 -0
- data/ext/rubydex/document.h +10 -0
- data/ext/rubydex/extconf.rb +138 -0
- data/ext/rubydex/graph.c +681 -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 +123 -0
- data/ext/rubydex/reference.h +15 -0
- data/ext/rubydex/rubydex.c +22 -0
- data/ext/rubydex/utils.c +108 -0
- data/ext/rubydex/utils.h +34 -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/comment.rb +17 -0
- data/lib/rubydex/diagnostic.rb +21 -0
- data/lib/rubydex/failures.rb +15 -0
- data/lib/rubydex/graph.rb +98 -0
- data/lib/rubydex/keyword.rb +17 -0
- data/lib/rubydex/keyword_parameter.rb +13 -0
- data/lib/rubydex/librubydex_sys.so +0 -0
- data/lib/rubydex/location.rb +90 -0
- data/lib/rubydex/mixin.rb +22 -0
- data/lib/rubydex/version.rb +5 -0
- data/lib/rubydex.rb +23 -0
- data/rbi/rubydex.rbi +422 -0
- data/rust/Cargo.lock +1851 -0
- data/rust/Cargo.toml +29 -0
- data/rust/about.hbs +78 -0
- data/rust/about.toml +10 -0
- data/rust/rubydex/Cargo.toml +42 -0
- data/rust/rubydex/src/compile_assertions.rs +13 -0
- data/rust/rubydex/src/diagnostic.rs +110 -0
- data/rust/rubydex/src/errors.rs +28 -0
- data/rust/rubydex/src/indexing/local_graph.rs +224 -0
- data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -0
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
- data/rust/rubydex/src/indexing.rs +210 -0
- data/rust/rubydex/src/integrity.rs +279 -0
- data/rust/rubydex/src/job_queue.rs +205 -0
- data/rust/rubydex/src/lib.rs +17 -0
- data/rust/rubydex/src/listing.rs +371 -0
- data/rust/rubydex/src/main.rs +160 -0
- data/rust/rubydex/src/model/built_in.rs +83 -0
- data/rust/rubydex/src/model/comment.rs +24 -0
- data/rust/rubydex/src/model/declaration.rs +671 -0
- data/rust/rubydex/src/model/definitions.rs +1682 -0
- data/rust/rubydex/src/model/document.rs +222 -0
- data/rust/rubydex/src/model/encoding.rs +22 -0
- data/rust/rubydex/src/model/graph.rs +3754 -0
- data/rust/rubydex/src/model/id.rs +110 -0
- data/rust/rubydex/src/model/identity_maps.rs +58 -0
- data/rust/rubydex/src/model/ids.rs +60 -0
- data/rust/rubydex/src/model/keywords.rs +256 -0
- data/rust/rubydex/src/model/name.rs +298 -0
- data/rust/rubydex/src/model/references.rs +111 -0
- data/rust/rubydex/src/model/string_ref.rs +50 -0
- data/rust/rubydex/src/model/visibility.rs +41 -0
- data/rust/rubydex/src/model.rs +15 -0
- data/rust/rubydex/src/offset.rs +147 -0
- data/rust/rubydex/src/position.rs +6 -0
- data/rust/rubydex/src/query.rs +1841 -0
- data/rust/rubydex/src/resolution.rs +6517 -0
- data/rust/rubydex/src/stats/memory.rs +71 -0
- data/rust/rubydex/src/stats/orphan_report.rs +264 -0
- data/rust/rubydex/src/stats/timer.rs +127 -0
- data/rust/rubydex/src/stats.rs +11 -0
- data/rust/rubydex/src/test_utils/context.rs +226 -0
- data/rust/rubydex/src/test_utils/graph_test.rs +730 -0
- data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -0
- data/rust/rubydex/src/test_utils.rs +52 -0
- data/rust/rubydex/src/visualization/dot.rs +192 -0
- data/rust/rubydex/src/visualization.rs +6 -0
- data/rust/rubydex/tests/cli.rs +185 -0
- data/rust/rubydex-mcp/Cargo.toml +28 -0
- data/rust/rubydex-mcp/src/main.rs +48 -0
- data/rust/rubydex-mcp/src/server.rs +1145 -0
- data/rust/rubydex-mcp/src/tools.rs +49 -0
- data/rust/rubydex-mcp/tests/mcp.rs +302 -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 +485 -0
- data/rust/rubydex-sys/src/definition_api.rs +443 -0
- data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
- data/rust/rubydex-sys/src/document_api.rs +85 -0
- data/rust/rubydex-sys/src/graph_api.rs +948 -0
- data/rust/rubydex-sys/src/lib.rs +79 -0
- data/rust/rubydex-sys/src/location_api.rs +79 -0
- data/rust/rubydex-sys/src/name_api.rs +135 -0
- data/rust/rubydex-sys/src/reference_api.rs +267 -0
- data/rust/rubydex-sys/src/utils.rs +70 -0
- data/rust/rustfmt.toml +2 -0
- metadata +159 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
use super::normalize_indentation;
|
|
2
|
+
#[cfg(test)]
|
|
3
|
+
use crate::diagnostic::Rule;
|
|
4
|
+
use crate::indexing::{self, LanguageId};
|
|
5
|
+
use crate::model::graph::{Graph, NameDependent};
|
|
6
|
+
use crate::model::ids::{NameId, StringId};
|
|
7
|
+
use crate::resolution::Resolver;
|
|
8
|
+
|
|
9
|
+
#[derive(Default)]
|
|
10
|
+
pub struct GraphTest {
|
|
11
|
+
graph: Graph,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl GraphTest {
|
|
15
|
+
#[must_use]
|
|
16
|
+
pub fn new() -> Self {
|
|
17
|
+
Self { graph: Graph::new() }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[must_use]
|
|
21
|
+
pub fn graph(&self) -> &Graph {
|
|
22
|
+
&self.graph
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[must_use]
|
|
26
|
+
pub fn into_graph(self) -> Graph {
|
|
27
|
+
self.graph
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Indexes a Ruby source
|
|
31
|
+
pub fn index_uri(&mut self, uri: &str, source: &str) {
|
|
32
|
+
let source = normalize_indentation(source);
|
|
33
|
+
indexing::index_source(&mut self.graph, uri, &source, &LanguageId::Ruby);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Indexes an RBS source
|
|
37
|
+
pub fn index_rbs_uri(&mut self, uri: &str, source: &str) {
|
|
38
|
+
let source = normalize_indentation(source);
|
|
39
|
+
indexing::index_source(&mut self.graph, uri, &source, &LanguageId::Rbs);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub fn delete_uri(&mut self, uri: &str) {
|
|
43
|
+
self.graph.delete_document(uri);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn resolve(&mut self) {
|
|
47
|
+
let mut resolver = Resolver::new(&mut self.graph);
|
|
48
|
+
resolver.resolve();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Name dependents helpers (shared with LocalGraphTest for assert_dependents! macro)
|
|
52
|
+
|
|
53
|
+
/// # Panics
|
|
54
|
+
///
|
|
55
|
+
/// Panics if no names match the given path.
|
|
56
|
+
#[must_use]
|
|
57
|
+
pub fn find_name_ids(&self, path: &str) -> Vec<NameId> {
|
|
58
|
+
let (parent, name) = match path.rsplit_once("::") {
|
|
59
|
+
Some((p, n)) => (Some(p), n),
|
|
60
|
+
None => (None, path),
|
|
61
|
+
};
|
|
62
|
+
let target_str_id = StringId::from(name);
|
|
63
|
+
let ids: Vec<NameId> = self
|
|
64
|
+
.graph()
|
|
65
|
+
.names()
|
|
66
|
+
.iter()
|
|
67
|
+
.filter(|(_, name_ref)| {
|
|
68
|
+
if *name_ref.str() != target_str_id {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
match parent {
|
|
72
|
+
None => name_ref.parent_scope().as_ref().is_none(),
|
|
73
|
+
Some(p) => name_ref.parent_scope().as_ref().is_some_and(|ps_id| {
|
|
74
|
+
let ps = self.graph().names().get(ps_id).unwrap();
|
|
75
|
+
*ps.str() == StringId::from(p)
|
|
76
|
+
}),
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
.map(|(id, _)| *id)
|
|
80
|
+
.collect();
|
|
81
|
+
assert!(!ids.is_empty(), "could not find name `{path}`");
|
|
82
|
+
ids
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[must_use]
|
|
86
|
+
pub fn name_dependents_for(&self, name_id: NameId) -> Vec<NameDependent> {
|
|
87
|
+
self.graph()
|
|
88
|
+
.name_dependents()
|
|
89
|
+
.get(&name_id)
|
|
90
|
+
.cloned()
|
|
91
|
+
.unwrap_or_default()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// # Panics
|
|
95
|
+
///
|
|
96
|
+
/// Panics if the name's string is not in the strings map.
|
|
97
|
+
#[must_use]
|
|
98
|
+
pub fn name_str(&self, name_id: &NameId) -> Option<&str> {
|
|
99
|
+
self.graph()
|
|
100
|
+
.names()
|
|
101
|
+
.get(name_id)
|
|
102
|
+
.map(|n| self.graph().strings().get(n.str()).unwrap().as_str())
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Returns the unqualified name string for a `NameDependent`, if available.
|
|
106
|
+
#[must_use]
|
|
107
|
+
pub fn dependent_name_str(&self, dep: &NameDependent) -> Option<&str> {
|
|
108
|
+
match dep {
|
|
109
|
+
NameDependent::ChildName(id) | NameDependent::NestedName(id) => self.name_str(id),
|
|
110
|
+
NameDependent::Definition(id) => self
|
|
111
|
+
.graph()
|
|
112
|
+
.definitions()
|
|
113
|
+
.get(id)
|
|
114
|
+
.and_then(|d| d.name_id())
|
|
115
|
+
.and_then(|name_id| self.name_str(name_id)),
|
|
116
|
+
NameDependent::Reference(id) => self
|
|
117
|
+
.graph()
|
|
118
|
+
.constant_references()
|
|
119
|
+
.get(id)
|
|
120
|
+
.and_then(|r| self.name_str(r.name_id())),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// # Panics
|
|
125
|
+
///
|
|
126
|
+
/// Panics if a diagnostic points to an invalid document
|
|
127
|
+
#[cfg(test)]
|
|
128
|
+
#[must_use]
|
|
129
|
+
pub fn format_diagnostics(&self, ignore_rules: &[Rule]) -> Vec<String> {
|
|
130
|
+
let mut diagnostics: Vec<_> = self
|
|
131
|
+
.graph()
|
|
132
|
+
.all_diagnostics()
|
|
133
|
+
.into_iter()
|
|
134
|
+
.filter(|d| !ignore_rules.contains(d.rule()))
|
|
135
|
+
.collect();
|
|
136
|
+
|
|
137
|
+
diagnostics.sort_by_key(|d| {
|
|
138
|
+
let uri = self.graph().documents().get(d.uri_id()).unwrap().uri();
|
|
139
|
+
(uri, d.offset())
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
diagnostics
|
|
143
|
+
.iter()
|
|
144
|
+
.map(|d| {
|
|
145
|
+
let document = self.graph().documents().get(d.uri_id()).unwrap();
|
|
146
|
+
d.formatted(document)
|
|
147
|
+
})
|
|
148
|
+
.collect()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[cfg(test)]
|
|
153
|
+
#[macro_export]
|
|
154
|
+
macro_rules! assert_declaration_exists {
|
|
155
|
+
($context:expr, $declaration_name:expr) => {
|
|
156
|
+
assert!(
|
|
157
|
+
$context
|
|
158
|
+
.graph()
|
|
159
|
+
.declarations()
|
|
160
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_name))
|
|
161
|
+
.is_some(),
|
|
162
|
+
"Expected declaration `{}` to exist",
|
|
163
|
+
$declaration_name
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#[cfg(test)]
|
|
169
|
+
#[macro_export]
|
|
170
|
+
macro_rules! assert_declaration_kind_eq {
|
|
171
|
+
($context:expr, $declaration_name:expr, $expected_kind:expr) => {
|
|
172
|
+
let declaration = $context
|
|
173
|
+
.graph()
|
|
174
|
+
.declarations()
|
|
175
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_name))
|
|
176
|
+
.unwrap();
|
|
177
|
+
assert_eq!(
|
|
178
|
+
declaration.kind(),
|
|
179
|
+
$expected_kind,
|
|
180
|
+
"Expected declaration `{}` to be a {}, got {}",
|
|
181
|
+
$declaration_name,
|
|
182
|
+
$expected_kind,
|
|
183
|
+
declaration.kind()
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
#[cfg(test)]
|
|
189
|
+
#[macro_export]
|
|
190
|
+
macro_rules! assert_declaration_does_not_exist {
|
|
191
|
+
($context:expr, $declaration_name:expr) => {
|
|
192
|
+
assert!(
|
|
193
|
+
$context
|
|
194
|
+
.graph()
|
|
195
|
+
.declarations()
|
|
196
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_name))
|
|
197
|
+
.is_none(),
|
|
198
|
+
"Expected declaration `{}` to not exist",
|
|
199
|
+
$declaration_name
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#[cfg(test)]
|
|
205
|
+
#[macro_export]
|
|
206
|
+
macro_rules! assert_declaration_definitions_count_eq {
|
|
207
|
+
($context:expr, $declaration_name:expr, $expected_definitions:expr) => {
|
|
208
|
+
let declaration = $context
|
|
209
|
+
.graph()
|
|
210
|
+
.declarations()
|
|
211
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_name))
|
|
212
|
+
.unwrap();
|
|
213
|
+
|
|
214
|
+
assert_eq!(
|
|
215
|
+
declaration.definitions().len(),
|
|
216
|
+
$expected_definitions,
|
|
217
|
+
"Expected exactly {} definitions for `{}`, but got {}",
|
|
218
|
+
$expected_definitions,
|
|
219
|
+
$declaration_name,
|
|
220
|
+
declaration.definitions().len()
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#[cfg(test)]
|
|
226
|
+
#[macro_export]
|
|
227
|
+
macro_rules! assert_constant_alias_target_eq {
|
|
228
|
+
($context:expr, $alias_name:expr, $target_name:expr) => {{
|
|
229
|
+
let decl_id = $crate::model::ids::DeclarationId::from($alias_name);
|
|
230
|
+
let target = $context
|
|
231
|
+
.graph()
|
|
232
|
+
.alias_targets(&decl_id)
|
|
233
|
+
.and_then(|t| t.first().copied());
|
|
234
|
+
assert_eq!(
|
|
235
|
+
target,
|
|
236
|
+
Some($crate::model::ids::DeclarationId::from($target_name)),
|
|
237
|
+
"Expected alias '{}' to have primary target '{}'",
|
|
238
|
+
$alias_name,
|
|
239
|
+
$target_name
|
|
240
|
+
);
|
|
241
|
+
}};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
#[cfg(test)]
|
|
245
|
+
#[macro_export]
|
|
246
|
+
macro_rules! assert_no_constant_alias_target {
|
|
247
|
+
($context:expr, $alias_name:expr) => {{
|
|
248
|
+
let decl_id = $crate::model::ids::DeclarationId::from($alias_name);
|
|
249
|
+
let targets = $context.graph().alias_targets(&decl_id).unwrap_or_default();
|
|
250
|
+
assert!(
|
|
251
|
+
targets.is_empty(),
|
|
252
|
+
"Expected no alias target for '{}', but found {:?}",
|
|
253
|
+
$alias_name,
|
|
254
|
+
targets
|
|
255
|
+
);
|
|
256
|
+
}};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#[cfg(test)]
|
|
260
|
+
#[macro_export]
|
|
261
|
+
macro_rules! assert_alias_targets_contain {
|
|
262
|
+
($context:expr, $alias_name:expr, $($target_name:expr),+ $(,)?) => {{
|
|
263
|
+
let decl_id = $crate::model::ids::DeclarationId::from($alias_name);
|
|
264
|
+
let targets = $context.graph().alias_targets(&decl_id).unwrap_or_default();
|
|
265
|
+
$(
|
|
266
|
+
let expected_id = $crate::model::ids::DeclarationId::from($target_name);
|
|
267
|
+
assert!(
|
|
268
|
+
targets.contains(&expected_id),
|
|
269
|
+
"Expected alias '{}' to contain target '{}', but targets were {:?}",
|
|
270
|
+
$alias_name,
|
|
271
|
+
$target_name,
|
|
272
|
+
targets
|
|
273
|
+
);
|
|
274
|
+
)+
|
|
275
|
+
}};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/// Asserts that a declaration has a constant reference at the specified location
|
|
279
|
+
///
|
|
280
|
+
/// This macro:
|
|
281
|
+
/// 1. Parses the location string into `(uri, start_offset, end_offset)`
|
|
282
|
+
/// 2. Finds the declaration by name
|
|
283
|
+
/// 3. Finds a constant reference to that declaration at the given uri and start offset
|
|
284
|
+
/// 4. Asserts the end offset matches
|
|
285
|
+
///
|
|
286
|
+
/// Location format: "uri:start_line:start_column-end_line:end_column"
|
|
287
|
+
/// Example: `<file:///foo.rb:3:0-3:5>`
|
|
288
|
+
#[cfg(test)]
|
|
289
|
+
#[macro_export]
|
|
290
|
+
macro_rules! assert_constant_reference_to {
|
|
291
|
+
($context:expr, $declaration_name:expr, $location:expr) => {
|
|
292
|
+
let mut all_references = $context
|
|
293
|
+
.graph()
|
|
294
|
+
.constant_references()
|
|
295
|
+
.values()
|
|
296
|
+
.map(|reference| {
|
|
297
|
+
(
|
|
298
|
+
reference,
|
|
299
|
+
format!(
|
|
300
|
+
"{}:{}",
|
|
301
|
+
$context.graph().documents().get(&reference.uri_id()).unwrap().uri(),
|
|
302
|
+
reference
|
|
303
|
+
.offset()
|
|
304
|
+
.to_display_range($context.graph().documents().get(&reference.uri_id()).unwrap())
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
})
|
|
308
|
+
.collect::<Vec<_>>();
|
|
309
|
+
|
|
310
|
+
all_references.sort_by_key(|(_, reference_location)| reference_location.clone());
|
|
311
|
+
|
|
312
|
+
let reference_at_location = all_references
|
|
313
|
+
.iter()
|
|
314
|
+
.find(|(_, reference_location)| reference_location == $location)
|
|
315
|
+
.map(|(reference, _)| reference)
|
|
316
|
+
.expect(&format!(
|
|
317
|
+
"No constant reference at `{}`, found references at {:?}",
|
|
318
|
+
$location,
|
|
319
|
+
all_references
|
|
320
|
+
.iter()
|
|
321
|
+
.map(|(_reference, reference_location)| reference_location)
|
|
322
|
+
.collect::<Vec<_>>()
|
|
323
|
+
));
|
|
324
|
+
|
|
325
|
+
let reference_name = $context.graph().names().get(reference_at_location.name_id()).unwrap();
|
|
326
|
+
let NameRef::Resolved(resolved_name) = reference_name else {
|
|
327
|
+
panic!("Reference to found at `{}` is unresolved", $location);
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
let resolved_name_name = $context
|
|
331
|
+
.graph()
|
|
332
|
+
.declarations()
|
|
333
|
+
.get(resolved_name.declaration_id())
|
|
334
|
+
.unwrap()
|
|
335
|
+
.name();
|
|
336
|
+
assert_eq!(
|
|
337
|
+
resolved_name_name, $declaration_name,
|
|
338
|
+
"Expected reference at `{}` to be resolved to `{}`, but got `{}`",
|
|
339
|
+
$location, $declaration_name, resolved_name_name
|
|
340
|
+
);
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
#[cfg(test)]
|
|
345
|
+
#[macro_export]
|
|
346
|
+
macro_rules! assert_declaration_references_count_eq {
|
|
347
|
+
($context:expr, $declaration_name:expr, $expected_references:expr) => {
|
|
348
|
+
let declaration = $context
|
|
349
|
+
.graph()
|
|
350
|
+
.declarations()
|
|
351
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_name))
|
|
352
|
+
.unwrap();
|
|
353
|
+
|
|
354
|
+
let count = declaration.reference_count();
|
|
355
|
+
|
|
356
|
+
assert_eq!(
|
|
357
|
+
count, $expected_references,
|
|
358
|
+
"Expected exactly {} references for `{}`, but got {}",
|
|
359
|
+
$expected_references, $declaration_name, count,
|
|
360
|
+
);
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#[cfg(test)]
|
|
365
|
+
#[macro_export]
|
|
366
|
+
macro_rules! assert_constant_reference_unresolved {
|
|
367
|
+
($context:expr, $unqualified_name:expr) => {
|
|
368
|
+
let reference_name = $context
|
|
369
|
+
.graph()
|
|
370
|
+
.constant_references()
|
|
371
|
+
.values()
|
|
372
|
+
.find_map(|r| {
|
|
373
|
+
let name = $context.graph().names().get(r.name_id()).unwrap();
|
|
374
|
+
if $context.graph().strings().get(name.str()).unwrap().as_str() == $unqualified_name {
|
|
375
|
+
Some(name)
|
|
376
|
+
} else {
|
|
377
|
+
None
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
.unwrap_or_else(|| panic!("No constant reference with unqualified name `{}`", $unqualified_name));
|
|
381
|
+
|
|
382
|
+
assert!(
|
|
383
|
+
matches!(reference_name, $crate::model::name::NameRef::Unresolved(_)),
|
|
384
|
+
"Expected constant reference `{}` to be unresolved, but it was resolved",
|
|
385
|
+
$unqualified_name
|
|
386
|
+
);
|
|
387
|
+
};
|
|
388
|
+
($context:expr, $unqualified_name:expr, $location:expr) => {
|
|
389
|
+
let mut all_references = $context
|
|
390
|
+
.graph()
|
|
391
|
+
.constant_references()
|
|
392
|
+
.values()
|
|
393
|
+
.map(|reference| {
|
|
394
|
+
(
|
|
395
|
+
reference,
|
|
396
|
+
format!(
|
|
397
|
+
"{}:{}",
|
|
398
|
+
$context.graph().documents().get(&reference.uri_id()).unwrap().uri(),
|
|
399
|
+
reference
|
|
400
|
+
.offset()
|
|
401
|
+
.to_display_range($context.graph().documents().get(&reference.uri_id()).unwrap())
|
|
402
|
+
),
|
|
403
|
+
)
|
|
404
|
+
})
|
|
405
|
+
.collect::<Vec<_>>();
|
|
406
|
+
|
|
407
|
+
all_references.sort_by_key(|(_, reference_location)| reference_location.clone());
|
|
408
|
+
|
|
409
|
+
let reference_at_location = all_references
|
|
410
|
+
.iter()
|
|
411
|
+
.find(|(_, reference_location)| reference_location == $location)
|
|
412
|
+
.map(|(reference, _)| reference)
|
|
413
|
+
.expect(&format!(
|
|
414
|
+
"No constant reference at `{}`, found references at {:?}",
|
|
415
|
+
$location,
|
|
416
|
+
all_references
|
|
417
|
+
.iter()
|
|
418
|
+
.map(|(_reference, reference_location)| reference_location)
|
|
419
|
+
.collect::<Vec<_>>()
|
|
420
|
+
));
|
|
421
|
+
|
|
422
|
+
let reference_name = $context.graph().names().get(reference_at_location.name_id()).unwrap();
|
|
423
|
+
assert!(
|
|
424
|
+
matches!(reference_name, $crate::model::name::NameRef::Unresolved(_)),
|
|
425
|
+
"Expected constant reference at `{}` to be unresolved, but it was resolved to `{}`",
|
|
426
|
+
$location,
|
|
427
|
+
if let $crate::model::name::NameRef::Resolved(resolved) = reference_name {
|
|
428
|
+
$context
|
|
429
|
+
.graph()
|
|
430
|
+
.declarations()
|
|
431
|
+
.get(resolved.declaration_id())
|
|
432
|
+
.unwrap()
|
|
433
|
+
.name()
|
|
434
|
+
.to_string()
|
|
435
|
+
} else {
|
|
436
|
+
String::new()
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
#[cfg(test)]
|
|
443
|
+
#[macro_export]
|
|
444
|
+
macro_rules! assert_ancestors_eq {
|
|
445
|
+
// Arm with mixed Complete/Partial entries: ["Foo", Partial("M1"), "Object"]
|
|
446
|
+
($context:expr, $name:expr, [$($entry:tt $( ($partial_name:expr) )?),* $(,)?]) => {{
|
|
447
|
+
let declaration = $context
|
|
448
|
+
.graph()
|
|
449
|
+
.declarations()
|
|
450
|
+
.get(&$crate::model::ids::DeclarationId::from($name))
|
|
451
|
+
.unwrap();
|
|
452
|
+
|
|
453
|
+
let actual = match declaration.as_namespace().unwrap().ancestors() {
|
|
454
|
+
$crate::model::declaration::Ancestors::Complete(a)
|
|
455
|
+
| $crate::model::declaration::Ancestors::Cyclic(a)
|
|
456
|
+
| $crate::model::declaration::Ancestors::Partial(a) => a,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
let actual_strs: Vec<String> = actual.iter().map(|a| match a {
|
|
460
|
+
$crate::model::declaration::Ancestor::Complete(id) => {
|
|
461
|
+
$context.graph().declarations().get(id).unwrap().name().to_string()
|
|
462
|
+
}
|
|
463
|
+
$crate::model::declaration::Ancestor::Partial(name_id) => {
|
|
464
|
+
let name = $context.graph().names().get(name_id).unwrap();
|
|
465
|
+
format!("Partial({})", $context.graph().strings().get(name.str()).unwrap().as_str())
|
|
466
|
+
}
|
|
467
|
+
}).collect();
|
|
468
|
+
|
|
469
|
+
let expected_strs: Vec<String> = vec![
|
|
470
|
+
$($crate::assert_ancestors_eq!(@str $entry $( ($partial_name) )?)),*
|
|
471
|
+
];
|
|
472
|
+
|
|
473
|
+
assert_eq!(
|
|
474
|
+
expected_strs, actual_strs,
|
|
475
|
+
"Incorrect ancestors for {}",
|
|
476
|
+
$name
|
|
477
|
+
);
|
|
478
|
+
}};
|
|
479
|
+
|
|
480
|
+
// Arm for variable expressions (e.g., `empty_ancestors`): all entries assumed Complete
|
|
481
|
+
($context:expr, $name:expr, $expected:expr) => {{
|
|
482
|
+
let declaration = $context
|
|
483
|
+
.graph()
|
|
484
|
+
.declarations()
|
|
485
|
+
.get(&$crate::model::ids::DeclarationId::from($name))
|
|
486
|
+
.unwrap();
|
|
487
|
+
|
|
488
|
+
let expected_ancestors: Vec<$crate::model::declaration::Ancestor> = $expected
|
|
489
|
+
.iter()
|
|
490
|
+
.map(|n| {
|
|
491
|
+
$crate::model::declaration::Ancestor::Complete($crate::model::ids::DeclarationId::from(*n))
|
|
492
|
+
})
|
|
493
|
+
.collect();
|
|
494
|
+
|
|
495
|
+
let actual = match declaration.as_namespace().unwrap().ancestors() {
|
|
496
|
+
$crate::model::declaration::Ancestors::Complete(a)
|
|
497
|
+
| $crate::model::declaration::Ancestors::Cyclic(a)
|
|
498
|
+
| $crate::model::declaration::Ancestors::Partial(a) => a,
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
assert_eq!(
|
|
502
|
+
expected_ancestors, *actual,
|
|
503
|
+
"Incorrect ancestors for {}",
|
|
504
|
+
$name
|
|
505
|
+
);
|
|
506
|
+
}};
|
|
507
|
+
|
|
508
|
+
// Internal: Partial("name") → "Partial(name)" string
|
|
509
|
+
(@str Partial ($name:expr)) => {
|
|
510
|
+
format!("Partial({})", $name)
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// Internal: "name" → "name" string
|
|
514
|
+
(@str $name:expr) => {
|
|
515
|
+
$name.to_string()
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#[cfg(test)]
|
|
520
|
+
#[macro_export]
|
|
521
|
+
macro_rules! assert_descendants {
|
|
522
|
+
($context:expr, $parent:expr, $descendants:expr) => {
|
|
523
|
+
let parent = $context
|
|
524
|
+
.graph()
|
|
525
|
+
.declarations()
|
|
526
|
+
.get(&$crate::model::ids::DeclarationId::from($parent))
|
|
527
|
+
.unwrap();
|
|
528
|
+
let actual = match parent {
|
|
529
|
+
$crate::model::declaration::Declaration::Namespace($crate::model::declaration::Namespace::Class(class)) => {
|
|
530
|
+
class.descendants().iter().cloned().collect::<Vec<_>>()
|
|
531
|
+
}
|
|
532
|
+
$crate::model::declaration::Declaration::Namespace($crate::model::declaration::Namespace::Module(
|
|
533
|
+
module,
|
|
534
|
+
)) => module.descendants().iter().cloned().collect::<Vec<_>>(),
|
|
535
|
+
$crate::model::declaration::Declaration::Namespace(
|
|
536
|
+
$crate::model::declaration::Namespace::SingletonClass(singleton),
|
|
537
|
+
) => singleton.descendants().iter().cloned().collect::<Vec<_>>(),
|
|
538
|
+
_ => panic!("Tried to get descendants for a declaration that isn't a namespace"),
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
for descendant in &$descendants {
|
|
542
|
+
let descendant_id = $crate::model::ids::DeclarationId::from(*descendant);
|
|
543
|
+
|
|
544
|
+
assert!(
|
|
545
|
+
actual.contains(&descendant_id),
|
|
546
|
+
"Expected '{}' to be a descendant of '{}'",
|
|
547
|
+
$context.graph().declarations().get(&descendant_id).unwrap().name(),
|
|
548
|
+
parent.name()
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
#[cfg(test)]
|
|
555
|
+
#[macro_export]
|
|
556
|
+
macro_rules! assert_members_eq {
|
|
557
|
+
($context:expr, $declaration_id:expr, $expected_members:expr) => {
|
|
558
|
+
let mut actual_members = $context
|
|
559
|
+
.graph()
|
|
560
|
+
.declarations()
|
|
561
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_id))
|
|
562
|
+
.unwrap()
|
|
563
|
+
.as_namespace()
|
|
564
|
+
.unwrap()
|
|
565
|
+
.members()
|
|
566
|
+
.iter()
|
|
567
|
+
.map(|(str_id, _)| $context.graph().strings().get(str_id).unwrap().as_str())
|
|
568
|
+
.collect::<Vec<_>>();
|
|
569
|
+
|
|
570
|
+
actual_members.sort();
|
|
571
|
+
|
|
572
|
+
assert_eq!($expected_members, actual_members.as_slice());
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
#[cfg(test)]
|
|
577
|
+
#[macro_export]
|
|
578
|
+
macro_rules! assert_no_members {
|
|
579
|
+
($context:expr, $declaration_id:expr) => {
|
|
580
|
+
assert_members_eq!($context, $declaration_id, [] as [&str; 0]);
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
#[cfg(test)]
|
|
585
|
+
#[macro_export]
|
|
586
|
+
macro_rules! assert_owner_eq {
|
|
587
|
+
($context:expr, $declaration_id:expr, $expected_owner_name:expr) => {
|
|
588
|
+
let actual_owner_id = $context
|
|
589
|
+
.graph()
|
|
590
|
+
.declarations()
|
|
591
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_id))
|
|
592
|
+
.unwrap()
|
|
593
|
+
.owner_id();
|
|
594
|
+
|
|
595
|
+
let actual_owner_name = $context.graph().declarations().get(actual_owner_id).unwrap().name();
|
|
596
|
+
|
|
597
|
+
assert_eq!($expected_owner_name, actual_owner_name);
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
#[cfg(test)]
|
|
602
|
+
#[macro_export]
|
|
603
|
+
macro_rules! assert_singleton_class_eq {
|
|
604
|
+
($context:expr, $declaration_id:expr, $expected_singleton_class_name:expr) => {
|
|
605
|
+
let declaration = $context
|
|
606
|
+
.graph()
|
|
607
|
+
.declarations()
|
|
608
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_id))
|
|
609
|
+
.unwrap();
|
|
610
|
+
|
|
611
|
+
assert_eq!(
|
|
612
|
+
$expected_singleton_class_name,
|
|
613
|
+
$context
|
|
614
|
+
.graph()
|
|
615
|
+
.declarations()
|
|
616
|
+
.get(declaration.as_namespace().unwrap().singleton_class().unwrap())
|
|
617
|
+
.unwrap()
|
|
618
|
+
.name()
|
|
619
|
+
);
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
#[cfg(test)]
|
|
624
|
+
#[macro_export]
|
|
625
|
+
macro_rules! assert_instance_variables_eq {
|
|
626
|
+
($context:expr, $declaration_id:expr, $expected_instance_variables:expr) => {
|
|
627
|
+
let mut actual_instance_variables = $context
|
|
628
|
+
.graph()
|
|
629
|
+
.declarations()
|
|
630
|
+
.get(&$crate::model::ids::DeclarationId::from($declaration_id))
|
|
631
|
+
.unwrap()
|
|
632
|
+
.as_namespace()
|
|
633
|
+
.unwrap()
|
|
634
|
+
.members()
|
|
635
|
+
.iter()
|
|
636
|
+
.filter_map(
|
|
637
|
+
|(str_id, member_id)| match $context.graph().declarations().get(member_id) {
|
|
638
|
+
Some($crate::model::declaration::Declaration::InstanceVariable(_)) => {
|
|
639
|
+
Some($context.graph().strings().get(str_id).unwrap().as_str())
|
|
640
|
+
}
|
|
641
|
+
_ => None,
|
|
642
|
+
},
|
|
643
|
+
)
|
|
644
|
+
.collect::<Vec<_>>();
|
|
645
|
+
|
|
646
|
+
actual_instance_variables.sort();
|
|
647
|
+
|
|
648
|
+
assert_eq!($expected_instance_variables, actual_instance_variables.as_slice());
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
#[cfg(test)]
|
|
653
|
+
#[macro_export]
|
|
654
|
+
macro_rules! assert_diagnostics_eq {
|
|
655
|
+
($context:expr, $expected_diagnostics:expr) => {{
|
|
656
|
+
assert_eq!($expected_diagnostics, $context.format_diagnostics(&[]).as_slice());
|
|
657
|
+
}};
|
|
658
|
+
($context:expr, $expected_diagnostics:expr, $ignore_rules:expr) => {{
|
|
659
|
+
assert_eq!($expected_diagnostics, $context.format_diagnostics($ignore_rules));
|
|
660
|
+
}};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
#[cfg(test)]
|
|
664
|
+
#[macro_export]
|
|
665
|
+
macro_rules! assert_no_diagnostics {
|
|
666
|
+
($context:expr) => {{
|
|
667
|
+
let diagnostics = $context.format_diagnostics(&[]);
|
|
668
|
+
assert!(diagnostics.is_empty(), "expected no diagnostics, got {:?}", diagnostics);
|
|
669
|
+
}};
|
|
670
|
+
($context:expr, $ignore_rules:expr) => {{
|
|
671
|
+
let diagnostics = $context.format_diagnostics($ignore_rules);
|
|
672
|
+
assert!(diagnostics.is_empty(), "expected no diagnostics, got {:?}", diagnostics);
|
|
673
|
+
}};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
#[cfg(test)]
|
|
677
|
+
mod tests {
|
|
678
|
+
use super::*;
|
|
679
|
+
|
|
680
|
+
#[test]
|
|
681
|
+
fn test_index_uri_with_single_line() {
|
|
682
|
+
let mut context = GraphTest::new();
|
|
683
|
+
|
|
684
|
+
context.index_uri("file://method.rb", "class Foo; end");
|
|
685
|
+
context.resolve();
|
|
686
|
+
|
|
687
|
+
let foo_defs = context.graph.get("Foo").unwrap();
|
|
688
|
+
assert_eq!(foo_defs.len(), 1);
|
|
689
|
+
assert_eq!(foo_defs[0].offset().start(), 0);
|
|
690
|
+
assert_eq!(foo_defs[0].offset().end(), 14);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
#[test]
|
|
694
|
+
fn test_index_uri_with_multiple_lines() {
|
|
695
|
+
let mut context = GraphTest::new();
|
|
696
|
+
|
|
697
|
+
context.index_uri("file://method.rb", {
|
|
698
|
+
"
|
|
699
|
+
class Foo
|
|
700
|
+
class Bar; end
|
|
701
|
+
end
|
|
702
|
+
"
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
context.resolve();
|
|
706
|
+
|
|
707
|
+
let foo_defs = context.graph.get("Foo").unwrap();
|
|
708
|
+
assert_eq!(foo_defs.len(), 1);
|
|
709
|
+
assert_eq!(foo_defs[0].offset().start(), 0);
|
|
710
|
+
assert_eq!(foo_defs[0].offset().end(), 30);
|
|
711
|
+
|
|
712
|
+
let bar_defs = context.graph.get("Foo::Bar").unwrap();
|
|
713
|
+
assert_eq!(bar_defs.len(), 1);
|
|
714
|
+
assert_eq!(bar_defs[0].offset().start(), 12);
|
|
715
|
+
assert_eq!(bar_defs[0].offset().end(), 26);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
#[test]
|
|
719
|
+
fn test_index_uri_with_new_lines() {
|
|
720
|
+
let mut context = GraphTest::new();
|
|
721
|
+
|
|
722
|
+
context.index_uri("file://method.rb", "\n\nclass Foo; end");
|
|
723
|
+
context.resolve();
|
|
724
|
+
|
|
725
|
+
let foo_defs = context.graph.get("Foo").unwrap();
|
|
726
|
+
assert_eq!(foo_defs.len(), 1);
|
|
727
|
+
assert_eq!(foo_defs[0].offset().start(), 2);
|
|
728
|
+
assert_eq!(foo_defs[0].offset().end(), 16);
|
|
729
|
+
}
|
|
730
|
+
}
|