rubydex 0.4.0 → 0.4.2
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/README.md +9 -14
- data/THIRD_PARTY_LICENSES.html +13 -24
- data/ext/rubydex/definition.c +34 -0
- data/ext/rubydex/definition.h +3 -0
- data/ext/rubydex/diagnostic.c +2 -49
- data/ext/rubydex/extconf.rb +4 -2
- data/ext/rubydex/graph.c +59 -11
- data/lib/rubydex/cli/command/dead_code.rb +59 -0
- data/lib/rubydex/cli/command/lint/explain.rb +8 -6
- data/lib/rubydex/cli/command/lint.rb +10 -4
- data/lib/rubydex/linter/rule_loader.rb +1 -1
- data/lib/rubydex/mcp_server/tools/codebase_stats_tool.rb +1 -1
- data/lib/rubydex/mcp_server/tools/find_constant_references_tool.rb +4 -4
- data/lib/rubydex/mcp_server/tools/find_dead_code_candidates_tool.rb +33 -0
- data/lib/rubydex/mcp_server/tools/get_declaration_tool.rb +2 -2
- data/lib/rubydex/mcp_server/tools/get_descendants_tool.rb +4 -4
- data/lib/rubydex/mcp_server/tools/get_file_declarations_tool.rb +2 -2
- data/lib/rubydex/mcp_server/tools/search_declarations_tool.rb +6 -6
- data/lib/rubydex/mcp_server.rb +24 -12
- data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
- data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
- data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
- data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
- data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
- data/lib/rubydex/rules/parse_error.rb +25 -0
- data/lib/rubydex/rules/parse_warning.rb +28 -0
- data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
- data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
- data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
- data/lib/rubydex/skill.rb +1 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +2 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +19 -4
- data/rbi/rubydex.rbi +32 -130
- data/rust/Cargo.lock +17 -11
- data/rust/Cargo.toml +3 -0
- data/rust/rubydex/Cargo.toml +6 -2
- data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
- data/rust/rubydex/src/compile_assertions.rs +2 -0
- data/rust/rubydex/src/config.rs +1 -1
- data/rust/rubydex/src/diagnostic.rs +142 -51
- data/rust/rubydex/src/indexing/rbs_indexer.rs +4 -4
- data/rust/rubydex/src/indexing/ruby_indexer.rs +26 -18
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +42 -2
- data/rust/rubydex/src/indexing.rs +6 -6
- data/rust/rubydex/src/listing.rs +12 -12
- data/rust/rubydex/src/model/built_in.rs +1 -1
- data/rust/rubydex/src/model/comment.rs +4 -4
- data/rust/rubydex/src/model/declaration.rs +25 -17
- data/rust/rubydex/src/model/document.rs +11 -11
- data/rust/rubydex/src/model/graph.rs +3 -3
- data/rust/rubydex/src/offset.rs +1 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +29 -19
- data/rust/rubydex/src/query.rs +193 -2
- data/rust/rubydex/src/test_utils/graph_test.rs +2 -2
- data/rust/rubydex/src/test_utils/local_graph_test.rs +13 -6
- data/rust/rubydex-sys/Cargo.toml +2 -2
- data/rust/rubydex-sys/src/definition_api.rs +69 -2
- data/rust/rubydex-sys/src/diagnostic_api.rs +0 -35
- data/rust/rubydex-sys/src/graph_api.rs +193 -45
- data/rust/rubydex-sys/src/name_api.rs +219 -48
- metadata +191 -180
|
@@ -900,7 +900,7 @@ mod class_and_module_tests {
|
|
|
900
900
|
assert_def_name_eq!(&context, def, "Baz");
|
|
901
901
|
assert_def_name_offset_eq!(&context, def, "3:11-3:14");
|
|
902
902
|
assert!(def.superclass_ref().is_none());
|
|
903
|
-
|
|
903
|
+
assert_eq!(def.members(), []);
|
|
904
904
|
|
|
905
905
|
assert_definition_at!(&context, "2:3-4:6", Class, |parent_nesting| {
|
|
906
906
|
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
@@ -948,7 +948,7 @@ mod class_and_module_tests {
|
|
|
948
948
|
assert_def_name_eq!(&context, def, "Quuux");
|
|
949
949
|
assert_def_name_offset_eq!(&context, def, "3:13-3:18");
|
|
950
950
|
assert!(def.superclass_ref().is_none());
|
|
951
|
-
|
|
951
|
+
assert_eq!(def.members(), []);
|
|
952
952
|
|
|
953
953
|
assert_definition_at!(&context, "2:3-4:6", Class, |parent_nesting| {
|
|
954
954
|
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
@@ -1250,6 +1250,46 @@ mod method_tests {
|
|
|
1250
1250
|
});
|
|
1251
1251
|
}
|
|
1252
1252
|
|
|
1253
|
+
#[test]
|
|
1254
|
+
fn index_def_node_with_no_block_parameter() {
|
|
1255
|
+
let context = index_source({
|
|
1256
|
+
"
|
|
1257
|
+
def foo(a, &nil); end
|
|
1258
|
+
"
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
assert_no_local_diagnostics!(&context);
|
|
1262
|
+
|
|
1263
|
+
assert_definition_at!(&context, "1:1-1:22", Method, |def| {
|
|
1264
|
+
assert_simple_signature!(def, |params| {
|
|
1265
|
+
assert_eq!(params.len(), 1);
|
|
1266
|
+
assert_parameter!(¶ms[0], RequiredPositional, |param| {
|
|
1267
|
+
assert_string_eq!(context, param.str(), "a");
|
|
1268
|
+
});
|
|
1269
|
+
});
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
#[test]
|
|
1274
|
+
fn index_def_node_with_trailing_comma_in_parameters() {
|
|
1275
|
+
let context = index_source({
|
|
1276
|
+
"
|
|
1277
|
+
def foo(a,); end
|
|
1278
|
+
"
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
assert_no_local_diagnostics!(&context);
|
|
1282
|
+
|
|
1283
|
+
assert_definition_at!(&context, "1:1-1:17", Method, |def| {
|
|
1284
|
+
assert_simple_signature!(def, |params| {
|
|
1285
|
+
assert_eq!(params.len(), 1);
|
|
1286
|
+
assert_parameter!(¶ms[0], RequiredPositional, |param| {
|
|
1287
|
+
assert_string_eq!(context, param.str(), "a");
|
|
1288
|
+
});
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1253
1293
|
#[test]
|
|
1254
1294
|
fn index_nested_method_definitions() {
|
|
1255
1295
|
let context = index_source({
|
|
@@ -101,7 +101,7 @@ impl Job for IndexingJob {
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
let language = self.path.extension().map_or(LanguageId::Ruby, LanguageId::from);
|
|
104
|
-
let local_graph = build_local_graph(url.to_string(), &source, &language, self.backend);
|
|
104
|
+
let local_graph = build_local_graph(url.to_string().into(), &source, &language, self.backend);
|
|
105
105
|
|
|
106
106
|
self.local_graph_tx
|
|
107
107
|
.send(local_graph)
|
|
@@ -110,8 +110,8 @@ impl Job for IndexingJob {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/// Indexes a single source string in memory, dispatching to the appropriate indexer based on `language_id`.
|
|
113
|
-
pub fn index_source(graph: &mut Graph, uri:
|
|
114
|
-
let local_graph = build_local_graph(uri
|
|
113
|
+
pub fn index_source(graph: &mut Graph, uri: Box<str>, source: &str, language_id: &LanguageId) {
|
|
114
|
+
let local_graph = build_local_graph(uri, source, language_id, IndexerBackend::RubyIndexer);
|
|
115
115
|
graph.consume_document_changes(local_graph);
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -153,7 +153,7 @@ pub fn index_files(graph: &mut Graph, paths: Vec<PathBuf>, backend: IndexerBacke
|
|
|
153
153
|
|
|
154
154
|
/// Indexes a source string using the appropriate indexer for the given language.
|
|
155
155
|
#[must_use]
|
|
156
|
-
pub fn build_local_graph(uri:
|
|
156
|
+
pub fn build_local_graph(uri: Box<str>, source: &str, language: &LanguageId, backend: IndexerBackend) -> LocalGraph {
|
|
157
157
|
match language {
|
|
158
158
|
LanguageId::Ruby => match backend {
|
|
159
159
|
IndexerBackend::RubyIndexer => {
|
|
@@ -203,7 +203,7 @@ mod tests {
|
|
|
203
203
|
let mut graph = Graph::new();
|
|
204
204
|
let errors = index_files(&mut graph, vec![relative_to_pwd.clone()], IndexerBackend::RubyIndexer);
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
assert_eq!(errors.as_slice(), []);
|
|
207
207
|
assert_eq!(graph.documents().len(), 2);
|
|
208
208
|
}
|
|
209
209
|
|
|
@@ -228,7 +228,7 @@ mod tests {
|
|
|
228
228
|
assert_eq!(6, graph.definitions().len());
|
|
229
229
|
assert_eq!(2, graph.documents().len());
|
|
230
230
|
|
|
231
|
-
index_source(&mut graph,
|
|
231
|
+
index_source(&mut graph, uri.into(), "", &LanguageId::Ruby);
|
|
232
232
|
|
|
233
233
|
assert_eq!(5, graph.definitions().len());
|
|
234
234
|
assert_eq!(2, graph.documents().len());
|
data/rust/rubydex/src/listing.rs
CHANGED
|
@@ -211,7 +211,7 @@ mod tests {
|
|
|
211
211
|
|
|
212
212
|
let (files, errors) = collect_document_paths(&context, &["foo", "bar"]);
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
assert_eq!(errors.as_slice(), []);
|
|
215
215
|
|
|
216
216
|
assert_eq!(
|
|
217
217
|
files,
|
|
@@ -236,7 +236,7 @@ mod tests {
|
|
|
236
236
|
|
|
237
237
|
let (files, errors) = collect_document_paths(&context, &["bar"]);
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
assert_eq!(errors.as_slice(), []);
|
|
240
240
|
|
|
241
241
|
assert_eq!(
|
|
242
242
|
files,
|
|
@@ -260,7 +260,7 @@ mod tests {
|
|
|
260
260
|
|
|
261
261
|
let (files, errors) = collect_document_paths(&context, &["lib", "sig", "config.ru"]);
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
assert_eq!(errors.as_slice(), []);
|
|
264
264
|
|
|
265
265
|
assert_eq!(
|
|
266
266
|
[
|
|
@@ -287,7 +287,7 @@ mod tests {
|
|
|
287
287
|
&HashSet::new(),
|
|
288
288
|
);
|
|
289
289
|
|
|
290
|
-
|
|
290
|
+
assert_eq!(files, [] as [std::path::PathBuf; 0]);
|
|
291
291
|
|
|
292
292
|
assert_eq!(
|
|
293
293
|
errors,
|
|
@@ -315,8 +315,8 @@ mod tests {
|
|
|
315
315
|
|
|
316
316
|
let (files, errors) = collect_file_paths(vec![relative_root.to_string_lossy().into_owned()], &HashSet::new());
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
|
|
318
|
+
assert_eq!(errors.as_slice(), []);
|
|
319
|
+
assert_ne!(files, [] as [std::path::PathBuf; 0]);
|
|
320
320
|
assert!(
|
|
321
321
|
files.iter().all(|path| path.is_absolute()),
|
|
322
322
|
"expected only absolute paths, got {files:?}"
|
|
@@ -336,7 +336,7 @@ mod tests {
|
|
|
336
336
|
|
|
337
337
|
let (files, errors) = collect_document_paths_with_exclusions(&context, &["included", "excluded"], &excluded);
|
|
338
338
|
|
|
339
|
-
|
|
339
|
+
assert_eq!(errors.as_slice(), []);
|
|
340
340
|
assert_eq!(files, [included.to_str().unwrap().to_string()]);
|
|
341
341
|
}
|
|
342
342
|
|
|
@@ -353,7 +353,7 @@ mod tests {
|
|
|
353
353
|
|
|
354
354
|
let (files, errors) = collect_document_paths_with_exclusions(&context, &["root"], &excluded);
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
assert_eq!(errors.as_slice(), []);
|
|
357
357
|
assert_eq!(files, [kept.to_str().unwrap().to_string()]);
|
|
358
358
|
}
|
|
359
359
|
|
|
@@ -374,7 +374,7 @@ mod tests {
|
|
|
374
374
|
|
|
375
375
|
let (files, errors) = collect_document_paths(&context, &["project"]);
|
|
376
376
|
|
|
377
|
-
|
|
377
|
+
assert_eq!(errors.as_slice(), []);
|
|
378
378
|
// The symlink is indexed at its own path, not resolved to the target.
|
|
379
379
|
let alias = PathBuf::from("project").join("alias.rb");
|
|
380
380
|
assert_eq!(files, [alias.to_str().unwrap().to_string()]);
|
|
@@ -398,7 +398,7 @@ mod tests {
|
|
|
398
398
|
|
|
399
399
|
let (files, errors) = collect_document_paths(&context, &["project"]);
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
assert_eq!(errors.as_slice(), []);
|
|
402
402
|
// The symlinked directory is not followed, so `outside/bar.rb` is never reached.
|
|
403
403
|
assert_eq!(files, [kept.to_str().unwrap().to_string()]);
|
|
404
404
|
}
|
|
@@ -415,7 +415,7 @@ mod tests {
|
|
|
415
415
|
|
|
416
416
|
let (files, errors) = collect_document_paths(&context, &["link"]);
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
assert_eq!(errors.as_slice(), []);
|
|
419
419
|
// The requested root is traversed; files are indexed under the requested (symlink) path.
|
|
420
420
|
let foo = PathBuf::from("link").join("foo.rb");
|
|
421
421
|
assert_eq!(files, [foo.to_str().unwrap().to_string()]);
|
|
@@ -458,7 +458,7 @@ mod tests {
|
|
|
458
458
|
|
|
459
459
|
let (files, errors) = collect_document_paths_with_exclusions(&context, &["lib"], &excluded);
|
|
460
460
|
|
|
461
|
-
|
|
461
|
+
assert_eq!(errors.as_slice(), []);
|
|
462
462
|
assert_eq!(files, [kept.to_str().unwrap().to_string()]);
|
|
463
463
|
}
|
|
464
464
|
}
|
|
@@ -48,7 +48,7 @@ pub fn add_built_in_data(graph: &mut Graph) {
|
|
|
48
48
|
class Class < Module
|
|
49
49
|
end
|
|
50
50
|
";
|
|
51
|
-
indexing::index_source(graph, uri.
|
|
51
|
+
indexing::index_source(graph, uri.to_string().into(), source, &LanguageId::Rbs);
|
|
52
52
|
|
|
53
53
|
// Creating declarations eagerly is still necessary because we need to associate correct ownership data no matter in
|
|
54
54
|
// what order we discover classes and modules
|
|
@@ -4,13 +4,13 @@ use crate::offset::Offset;
|
|
|
4
4
|
#[derive(Debug, Clone)]
|
|
5
5
|
pub struct Comment {
|
|
6
6
|
offset: Offset,
|
|
7
|
-
string:
|
|
7
|
+
string: Box<str>,
|
|
8
8
|
}
|
|
9
|
-
assert_mem_size!(Comment,
|
|
9
|
+
assert_mem_size!(Comment, 24);
|
|
10
10
|
|
|
11
11
|
impl Comment {
|
|
12
12
|
#[must_use]
|
|
13
|
-
pub fn new(offset: Offset, string:
|
|
13
|
+
pub fn new(offset: Offset, string: Box<str>) -> Self {
|
|
14
14
|
Self { offset, string }
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -20,7 +20,7 @@ impl Comment {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
#[must_use]
|
|
23
|
-
pub fn string(&self) -> &
|
|
23
|
+
pub fn string(&self) -> &str {
|
|
24
24
|
&self.string
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -119,7 +119,7 @@ pub struct DeclarationCore<T> {
|
|
|
119
119
|
/// The list of definition IDs that compose this declaration
|
|
120
120
|
definition_ids: Vec<DefinitionId>,
|
|
121
121
|
/// The set of references that are made to this declaration
|
|
122
|
-
references:
|
|
122
|
+
references: Vec<T>,
|
|
123
123
|
/// The ID of the owner of this declaration
|
|
124
124
|
owner_id: DeclarationId,
|
|
125
125
|
}
|
|
@@ -130,7 +130,7 @@ impl<T: Eq + Hash> DeclarationCore<T> {
|
|
|
130
130
|
Self {
|
|
131
131
|
name: name.into_boxed_str(),
|
|
132
132
|
definition_ids: Vec::new(),
|
|
133
|
-
references:
|
|
133
|
+
references: Vec::new(),
|
|
134
134
|
owner_id,
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -152,16 +152,24 @@ impl<T: Eq + Hash> DeclarationCore<T> {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
#[must_use]
|
|
155
|
-
pub fn references(&self) -> &
|
|
155
|
+
pub fn references(&self) -> &[T] {
|
|
156
156
|
&self.references
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
pub fn add_reference(&mut self, reference_id: T) {
|
|
160
|
-
|
|
160
|
+
debug_assert!(
|
|
161
|
+
!self.references.contains(&reference_id),
|
|
162
|
+
"Cannot add the same exact reference to a declaration twice. Duplicate reference IDs"
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
self.references.push(reference_id);
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
pub fn remove_reference(&mut self, reference_id: &T) {
|
|
164
|
-
self.references.
|
|
169
|
+
if let Some(pos) = self.references.iter().position(|id| id == reference_id) {
|
|
170
|
+
self.references.swap_remove(pos);
|
|
171
|
+
self.references.shrink_to_fit();
|
|
172
|
+
}
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
#[must_use]
|
|
@@ -493,7 +501,7 @@ impl Declaration {
|
|
|
493
501
|
/// Returns the constant reference IDs for declarations that track constant references (`Namespace`, `Constant`,
|
|
494
502
|
/// `ConstantAlias`). Returns `None` for other declaration types.
|
|
495
503
|
#[must_use]
|
|
496
|
-
pub fn constant_references(&self) -> Option<&
|
|
504
|
+
pub fn constant_references(&self) -> Option<&[ConstantReferenceId]> {
|
|
497
505
|
match self {
|
|
498
506
|
Declaration::Namespace(it) => Some(it.references()),
|
|
499
507
|
Declaration::Constant(it) | Declaration::ConstantAlias(it) => Some(it.references()),
|
|
@@ -559,7 +567,7 @@ impl Namespace {
|
|
|
559
567
|
}
|
|
560
568
|
|
|
561
569
|
#[must_use]
|
|
562
|
-
pub fn references(&self) -> &
|
|
570
|
+
pub fn references(&self) -> &[ConstantReferenceId] {
|
|
563
571
|
all_namespaces!(self, it => &it.core.references)
|
|
564
572
|
}
|
|
565
573
|
|
|
@@ -674,31 +682,31 @@ impl Namespace {
|
|
|
674
682
|
}
|
|
675
683
|
|
|
676
684
|
namespace_declaration!(Class, ClassDeclaration);
|
|
677
|
-
assert_mem_size!(ClassDeclaration,
|
|
685
|
+
assert_mem_size!(ClassDeclaration, 176);
|
|
678
686
|
namespace_declaration!(Module, ModuleDeclaration);
|
|
679
|
-
assert_mem_size!(ModuleDeclaration,
|
|
687
|
+
assert_mem_size!(ModuleDeclaration, 176);
|
|
680
688
|
namespace_declaration!(SingletonClass, SingletonClassDeclaration);
|
|
681
|
-
assert_mem_size!(SingletonClassDeclaration,
|
|
689
|
+
assert_mem_size!(SingletonClassDeclaration, 176);
|
|
682
690
|
namespace_declaration!(Todo, TodoDeclaration);
|
|
683
|
-
assert_mem_size!(TodoDeclaration,
|
|
691
|
+
assert_mem_size!(TodoDeclaration, 176);
|
|
684
692
|
|
|
685
693
|
pub type ConstantDeclaration = DeclarationCore<ConstantReferenceId>;
|
|
686
|
-
assert_mem_size!(ConstantDeclaration,
|
|
694
|
+
assert_mem_size!(ConstantDeclaration, 72);
|
|
687
695
|
|
|
688
696
|
pub type MethodDeclaration = DeclarationCore<MethodReferenceId>;
|
|
689
|
-
assert_mem_size!(MethodDeclaration,
|
|
697
|
+
assert_mem_size!(MethodDeclaration, 72);
|
|
690
698
|
|
|
691
699
|
pub type GlobalVariableDeclaration = DeclarationCore<GlobalVariableReferenceId>;
|
|
692
|
-
assert_mem_size!(GlobalVariableDeclaration,
|
|
700
|
+
assert_mem_size!(GlobalVariableDeclaration, 72);
|
|
693
701
|
|
|
694
702
|
pub type InstanceVariableDeclaration = DeclarationCore<InstanceVariableReferenceId>;
|
|
695
|
-
assert_mem_size!(InstanceVariableDeclaration,
|
|
703
|
+
assert_mem_size!(InstanceVariableDeclaration, 72);
|
|
696
704
|
|
|
697
705
|
pub type ClassVariableDeclaration = DeclarationCore<ClassVariableReferenceId>;
|
|
698
|
-
assert_mem_size!(ClassVariableDeclaration,
|
|
706
|
+
assert_mem_size!(ClassVariableDeclaration, 72);
|
|
699
707
|
|
|
700
708
|
pub type ConstantAliasDeclaration = DeclarationCore<ConstantReferenceId>;
|
|
701
|
-
assert_mem_size!(ConstantAliasDeclaration,
|
|
709
|
+
assert_mem_size!(ConstantAliasDeclaration, 72);
|
|
702
710
|
|
|
703
711
|
#[cfg(test)]
|
|
704
712
|
mod tests {
|
|
@@ -12,7 +12,7 @@ use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId};
|
|
|
12
12
|
// definitions and references discovered in it
|
|
13
13
|
#[derive(Debug)]
|
|
14
14
|
pub struct Document {
|
|
15
|
-
uri:
|
|
15
|
+
uri: Box<str>,
|
|
16
16
|
line_index: LineIndex,
|
|
17
17
|
definition_ids: Vec<DefinitionId>,
|
|
18
18
|
method_reference_ids: Vec<MethodReferenceId>,
|
|
@@ -20,11 +20,11 @@ pub struct Document {
|
|
|
20
20
|
diagnostics: Vec<Diagnostic>,
|
|
21
21
|
content_hash: u64,
|
|
22
22
|
}
|
|
23
|
-
assert_mem_size!(Document,
|
|
23
|
+
assert_mem_size!(Document, 176);
|
|
24
24
|
|
|
25
25
|
impl Document {
|
|
26
26
|
#[must_use]
|
|
27
|
-
pub fn new(uri:
|
|
27
|
+
pub fn new(uri: Box<str>, source: &str) -> Self {
|
|
28
28
|
Self {
|
|
29
29
|
uri,
|
|
30
30
|
line_index: LineIndex::new(source),
|
|
@@ -176,7 +176,7 @@ mod tests {
|
|
|
176
176
|
#[test]
|
|
177
177
|
#[should_panic(expected = "Cannot add the same exact definition to a document twice. Duplicate definition IDs")]
|
|
178
178
|
fn inserting_duplicate_definitions() {
|
|
179
|
-
let mut document = Document::new("file:///foo.rb".
|
|
179
|
+
let mut document = Document::new("file:///foo.rb".into(), "class Foo; end");
|
|
180
180
|
let def_id = DefinitionId::new(123);
|
|
181
181
|
|
|
182
182
|
document.add_definition(def_id);
|
|
@@ -187,7 +187,7 @@ mod tests {
|
|
|
187
187
|
|
|
188
188
|
#[test]
|
|
189
189
|
fn tracking_references() {
|
|
190
|
-
let mut document = Document::new("file:///foo.rb".
|
|
190
|
+
let mut document = Document::new("file:///foo.rb".into(), "class Foo; end");
|
|
191
191
|
let method_ref = MethodReferenceId::new(1);
|
|
192
192
|
let constant_ref = ConstantReferenceId::new(2);
|
|
193
193
|
|
|
@@ -202,7 +202,7 @@ mod tests {
|
|
|
202
202
|
fn require_path() {
|
|
203
203
|
let root = test_root();
|
|
204
204
|
let path = root.join("lib").join("foo").join("bar.rb");
|
|
205
|
-
let uri = Url::from_file_path(path).unwrap().to_string();
|
|
205
|
+
let uri = Url::from_file_path(path).unwrap().to_string().into();
|
|
206
206
|
let load_paths = [root.join("lib")];
|
|
207
207
|
|
|
208
208
|
let document = Document::new(uri, "");
|
|
@@ -213,7 +213,7 @@ mod tests {
|
|
|
213
213
|
fn require_path_first_load_path_wins() {
|
|
214
214
|
let root = test_root();
|
|
215
215
|
let path = root.join("a").join("b").join("foo.rb");
|
|
216
|
-
let uri = Url::from_file_path(path).unwrap().to_string();
|
|
216
|
+
let uri = Url::from_file_path(path).unwrap().to_string().into();
|
|
217
217
|
let load_path_a = root.join("a");
|
|
218
218
|
let load_path_ab = root.join("a").join("b");
|
|
219
219
|
let load_paths = [load_path_a, load_path_ab];
|
|
@@ -228,18 +228,18 @@ mod tests {
|
|
|
228
228
|
let load_paths = [root.join("lib")];
|
|
229
229
|
|
|
230
230
|
// non-file URI
|
|
231
|
-
let document = Document::new("untitled:Untitled-1".
|
|
231
|
+
let document = Document::new("untitled:Untitled-1".into(), "");
|
|
232
232
|
assert!(document.require_path(&load_paths).is_none());
|
|
233
233
|
|
|
234
234
|
// non-.rb extension
|
|
235
235
|
let path = root.join("lib").join("foo.so");
|
|
236
|
-
let uri = Url::from_file_path(path).unwrap().to_string();
|
|
236
|
+
let uri = Url::from_file_path(path).unwrap().to_string().into();
|
|
237
237
|
let document = Document::new(uri, "");
|
|
238
238
|
assert!(document.require_path(&load_paths).is_none());
|
|
239
239
|
|
|
240
240
|
// /libfoo is not under /lib - should not match due to partial directory name
|
|
241
241
|
let path = root.join("libfoo").join("bar.rb");
|
|
242
|
-
let uri = Url::from_file_path(path).unwrap().to_string();
|
|
242
|
+
let uri = Url::from_file_path(path).unwrap().to_string().into();
|
|
243
243
|
let document = Document::new(uri, "");
|
|
244
244
|
assert!(document.require_path(&load_paths).is_none());
|
|
245
245
|
}
|
|
@@ -248,7 +248,7 @@ mod tests {
|
|
|
248
248
|
fn require_path_with_spaces() {
|
|
249
249
|
let root = test_root();
|
|
250
250
|
let path = root.join("lib").join("space foo").join("bar.rb");
|
|
251
|
-
let uri = Url::from_file_path(path).unwrap().to_string();
|
|
251
|
+
let uri = Url::from_file_path(path).unwrap().to_string().into();
|
|
252
252
|
let load_paths = [root.join("lib")];
|
|
253
253
|
|
|
254
254
|
let document = Document::new(uri, "");
|
|
@@ -1759,7 +1759,7 @@ mod tests {
|
|
|
1759
1759
|
assert_eq!(context.graph().constant_references.len(), 4);
|
|
1760
1760
|
{
|
|
1761
1761
|
let declaration = context.graph().declarations().get(&DeclarationId::from("Foo")).unwrap();
|
|
1762
|
-
|
|
1762
|
+
assert_eq!(declaration.as_namespace().unwrap().references(), []);
|
|
1763
1763
|
}
|
|
1764
1764
|
}
|
|
1765
1765
|
|
|
@@ -2090,14 +2090,14 @@ mod tests {
|
|
|
2090
2090
|
let definitions = context.graph().get("CommentedClass").unwrap();
|
|
2091
2091
|
let def = definitions.first().unwrap();
|
|
2092
2092
|
assert_eq!(
|
|
2093
|
-
def.comments().iter().map(Comment::string).collect::<Vec<&
|
|
2093
|
+
def.comments().iter().map(Comment::string).collect::<Vec<&str>>(),
|
|
2094
2094
|
["# This is a class comment", "# Multi-line comment"]
|
|
2095
2095
|
);
|
|
2096
2096
|
|
|
2097
2097
|
let definitions = context.graph().get("CommentedModule").unwrap();
|
|
2098
2098
|
let def = definitions.first().unwrap();
|
|
2099
2099
|
assert_eq!(
|
|
2100
|
-
def.comments().iter().map(Comment::string).collect::<Vec<&
|
|
2100
|
+
def.comments().iter().map(Comment::string).collect::<Vec<&str>>(),
|
|
2101
2101
|
["# Module comment"]
|
|
2102
2102
|
);
|
|
2103
2103
|
|
data/rust/rubydex/src/offset.rs
CHANGED
|
@@ -30,21 +30,9 @@ impl Offset {
|
|
|
30
30
|
Self { start, end }
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/// # Panics
|
|
34
|
-
///
|
|
35
|
-
/// This function can panic if the Prism location offsets do not fit into a u32
|
|
36
33
|
#[must_use]
|
|
37
34
|
pub fn from_prism_location(location: &ruby_prism::Location) -> Self {
|
|
38
|
-
Self::new(
|
|
39
|
-
location
|
|
40
|
-
.start_offset()
|
|
41
|
-
.try_into()
|
|
42
|
-
.expect("Expected usize `start` to fit in `u32`"),
|
|
43
|
-
location
|
|
44
|
-
.end_offset()
|
|
45
|
-
.try_into()
|
|
46
|
-
.expect("Expected usize `end` to fit in `u32`"),
|
|
47
|
-
)
|
|
35
|
+
Self::new(location.start(), location.end())
|
|
48
36
|
}
|
|
49
37
|
|
|
50
38
|
/// # Panics
|
|
@@ -87,8 +87,8 @@ pub struct RubyOperationBuilder<'a> {
|
|
|
87
87
|
|
|
88
88
|
impl<'a> RubyOperationBuilder<'a> {
|
|
89
89
|
#[must_use]
|
|
90
|
-
pub fn new(uri:
|
|
91
|
-
let uri_id = UriId::from(
|
|
90
|
+
pub fn new(uri: Box<str>, source: &'a str) -> Self {
|
|
91
|
+
let uri_id = UriId::from(&*uri);
|
|
92
92
|
|
|
93
93
|
Self {
|
|
94
94
|
uri_id,
|
|
@@ -112,7 +112,7 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
112
112
|
self.add_diagnostic(
|
|
113
113
|
Rule::ParseError,
|
|
114
114
|
Offset::from_prism_location(&error.location()),
|
|
115
|
-
error.message().to_string(),
|
|
115
|
+
String::from_utf8_lossy(error.message()).to_string(),
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -120,7 +120,7 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
120
120
|
self.add_diagnostic(
|
|
121
121
|
Rule::ParseWarning,
|
|
122
122
|
Offset::from_prism_location(&warning.location()),
|
|
123
|
-
warning.message().to_string(),
|
|
123
|
+
String::from_utf8_lossy(warning.message()).to_string(),
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -489,6 +489,7 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
489
489
|
|
|
490
490
|
// -- Parameters --
|
|
491
491
|
|
|
492
|
+
#[allow(clippy::too_many_lines)]
|
|
492
493
|
fn collect_parameters(&mut self, node: &ruby_prism::DefNode) -> Vec<Parameter> {
|
|
493
494
|
let mut parameters: Vec<Parameter> = Vec::new();
|
|
494
495
|
|
|
@@ -588,7 +589,9 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
588
589
|
}
|
|
589
590
|
}
|
|
590
591
|
|
|
591
|
-
if let Some(block) = parameters_list.block()
|
|
592
|
+
if let Some(block) = parameters_list.block()
|
|
593
|
+
&& let Some(block) = block.as_block_parameter_node()
|
|
594
|
+
{
|
|
592
595
|
let location = block.name_loc().unwrap_or_else(|| block.location());
|
|
593
596
|
let str_id = self.intern_string(Self::location_to_string(&location));
|
|
594
597
|
parameters.push(Parameter::Block(ParameterStruct::new(
|
|
@@ -1036,8 +1039,8 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
1036
1039
|
match arg {
|
|
1037
1040
|
ruby_prism::Node::SymbolNode { .. } => {
|
|
1038
1041
|
let symbol = arg.as_symbol_node().unwrap();
|
|
1039
|
-
let
|
|
1040
|
-
Some((Self::location_to_string(&
|
|
1042
|
+
let content_loc = symbol.content_loc();
|
|
1043
|
+
Some((Self::location_to_string(&content_loc), content_loc))
|
|
1041
1044
|
}
|
|
1042
1045
|
ruby_prism::Node::StringNode { .. } => {
|
|
1043
1046
|
let string = arg.as_string_node().unwrap();
|
|
@@ -1380,7 +1383,7 @@ impl CommentGroup {
|
|
|
1380
1383
|
|
|
1381
1384
|
fn accepts(&self, next: &ruby_prism::Comment, source: &str) -> bool {
|
|
1382
1385
|
let current_end_offset = self.end_offset;
|
|
1383
|
-
let next_line_start_offset = next.location().
|
|
1386
|
+
let next_line_start_offset = next.location().start() as usize;
|
|
1384
1387
|
let between = &source.as_bytes()[current_end_offset..next_line_start_offset];
|
|
1385
1388
|
if !between.iter().all(|&b| b.is_ascii_whitespace()) {
|
|
1386
1389
|
return false;
|
|
@@ -1389,14 +1392,14 @@ impl CommentGroup {
|
|
|
1389
1392
|
}
|
|
1390
1393
|
|
|
1391
1394
|
fn add_comment(&mut self, comment: &ruby_prism::Comment) {
|
|
1392
|
-
self.end_offset = comment.location().
|
|
1395
|
+
self.end_offset = comment.location().end() as usize;
|
|
1393
1396
|
let text = String::from_utf8_lossy(comment.location().as_slice()).to_string();
|
|
1394
1397
|
if text.lines().any(|line| line.starts_with("# @deprecated")) {
|
|
1395
1398
|
self.deprecated = true;
|
|
1396
1399
|
}
|
|
1397
1400
|
self.comments.push(Comment::new(
|
|
1398
1401
|
Offset::from_prism_location(&comment.location()),
|
|
1399
|
-
text.trim().
|
|
1402
|
+
text.trim().into(),
|
|
1400
1403
|
));
|
|
1401
1404
|
}
|
|
1402
1405
|
|
|
@@ -1783,10 +1786,17 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
1783
1786
|
});
|
|
1784
1787
|
};
|
|
1785
1788
|
|
|
1786
|
-
let message_loc = node.
|
|
1787
|
-
|
|
1789
|
+
let message_loc = if node.name().as_slice().is_empty() {
|
|
1790
|
+
// Incomplete calls like `Foo.` have no message. We still index them using the call operator location, so
|
|
1791
|
+
// that the receiver's singleton class gets created, which completion depends on
|
|
1792
|
+
node.call_operator_loc()
|
|
1793
|
+
} else {
|
|
1794
|
+
node.message_loc()
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1797
|
+
let Some(message_loc) = message_loc else {
|
|
1788
1798
|
return;
|
|
1789
|
-
}
|
|
1799
|
+
};
|
|
1790
1800
|
|
|
1791
1801
|
let message = String::from_utf8_lossy(node.name().as_slice()).to_string();
|
|
1792
1802
|
|
|
@@ -2005,11 +2015,11 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
2005
2015
|
self.visit(&receiver);
|
|
2006
2016
|
}
|
|
2007
2017
|
|
|
2008
|
-
self.index_method_reference(message.clone(), &
|
|
2018
|
+
self.index_method_reference(message.clone(), &message_loc, method_receiver);
|
|
2009
2019
|
|
|
2010
2020
|
match message.as_str() {
|
|
2011
2021
|
">" | "<" | ">=" | "<=" => {
|
|
2012
|
-
self.index_method_reference("<=>".to_string(), &
|
|
2022
|
+
self.index_method_reference("<=>".to_string(), &message_loc, method_receiver);
|
|
2013
2023
|
}
|
|
2014
2024
|
_ => {}
|
|
2015
2025
|
}
|
|
@@ -2134,7 +2144,7 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
2134
2144
|
match expression {
|
|
2135
2145
|
ruby_prism::Node::SymbolNode { .. } => {
|
|
2136
2146
|
let symbol = expression.as_symbol_node().unwrap();
|
|
2137
|
-
let name = Self::location_to_string(&symbol.
|
|
2147
|
+
let name = Self::location_to_string(&symbol.content_loc());
|
|
2138
2148
|
self.index_method_reference(name, &node.location(), None);
|
|
2139
2149
|
}
|
|
2140
2150
|
_ => {
|
|
@@ -2146,13 +2156,13 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
2146
2156
|
|
|
2147
2157
|
fn visit_alias_method_node(&mut self, node: &ruby_prism::AliasMethodNode<'_>) {
|
|
2148
2158
|
let mut new_name = if let Some(symbol_node) = node.new_name().as_symbol_node() {
|
|
2149
|
-
Self::location_to_string(&symbol_node.
|
|
2159
|
+
Self::location_to_string(&symbol_node.content_loc())
|
|
2150
2160
|
} else {
|
|
2151
2161
|
Self::location_to_string(&node.new_name().location())
|
|
2152
2162
|
};
|
|
2153
2163
|
|
|
2154
2164
|
let mut old_name = if let Some(symbol_node) = node.old_name().as_symbol_node() {
|
|
2155
|
-
Self::location_to_string(&symbol_node.
|
|
2165
|
+
Self::location_to_string(&symbol_node.content_loc())
|
|
2156
2166
|
} else {
|
|
2157
2167
|
Self::location_to_string(&node.old_name().location())
|
|
2158
2168
|
};
|
|
@@ -2229,7 +2239,7 @@ mod tests {
|
|
|
2229
2239
|
|
|
2230
2240
|
fn build_operations(source: &str) -> OperationBuilderResult {
|
|
2231
2241
|
let source = crate::test_utils::normalize_indentation(source);
|
|
2232
|
-
let builder = RubyOperationBuilder::new("file:///test.rb".
|
|
2242
|
+
let builder = RubyOperationBuilder::new("file:///test.rb".into(), &source);
|
|
2233
2243
|
builder.build()
|
|
2234
2244
|
}
|
|
2235
2245
|
|