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
|
@@ -1,679 +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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
$declaration_name,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
#[
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
.
|
|
371
|
-
.
|
|
372
|
-
.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
)
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
$
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
)
|
|
593
|
-
.
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
assert_eq!($
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
#[cfg(test)]
|
|
602
|
-
#[macro_export]
|
|
603
|
-
macro_rules!
|
|
604
|
-
($context:expr, $
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
assert_eq!(
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
context.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
+
}
|