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,1551 @@
|
|
|
1
|
+
//! Visit the RBS AST and create type definitions.
|
|
2
|
+
|
|
3
|
+
use core::panic;
|
|
4
|
+
|
|
5
|
+
use ruby_rbs::node::{
|
|
6
|
+
self, AliasKind, ClassNode, CommentNode, ConstantNode, ExtendNode, FunctionTypeNode, GlobalNode, IncludeNode,
|
|
7
|
+
ModuleNode, Node, NodeList, PrependNode, TypeNameNode, Visit,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
use crate::diagnostic::Rule;
|
|
11
|
+
use crate::indexing::local_graph::LocalGraph;
|
|
12
|
+
use crate::model::comment::Comment;
|
|
13
|
+
use crate::model::definitions::{
|
|
14
|
+
ClassDefinition, ConstantDefinition, Definition, DefinitionFlags, ExtendDefinition, GlobalVariableDefinition,
|
|
15
|
+
IncludeDefinition, MethodAliasDefinition, MethodDefinition, Mixin, ModuleDefinition, Parameter, ParameterStruct,
|
|
16
|
+
PrependDefinition, Receiver, Signature, Signatures,
|
|
17
|
+
};
|
|
18
|
+
use crate::model::document::Document;
|
|
19
|
+
use crate::model::ids::{ConstantReferenceId, DefinitionId, NameId, StringId, UriId};
|
|
20
|
+
use crate::model::name::{Name, ParentScope};
|
|
21
|
+
use crate::model::references::ConstantReference;
|
|
22
|
+
use crate::model::visibility::Visibility;
|
|
23
|
+
use crate::offset::Offset;
|
|
24
|
+
|
|
25
|
+
pub struct RBSIndexer<'a> {
|
|
26
|
+
uri_id: UriId,
|
|
27
|
+
local_graph: LocalGraph,
|
|
28
|
+
source: &'a str,
|
|
29
|
+
nesting_stack: Vec<DefinitionId>,
|
|
30
|
+
current_visibility: Visibility,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl<'a> RBSIndexer<'a> {
|
|
34
|
+
#[must_use]
|
|
35
|
+
pub fn new(uri: String, source: &'a str) -> Self {
|
|
36
|
+
let uri_id = UriId::from(&uri);
|
|
37
|
+
let local_graph = LocalGraph::new(uri_id, Document::new(uri, source));
|
|
38
|
+
|
|
39
|
+
Self {
|
|
40
|
+
uri_id,
|
|
41
|
+
local_graph,
|
|
42
|
+
source,
|
|
43
|
+
nesting_stack: Vec::new(),
|
|
44
|
+
current_visibility: Visibility::Public,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#[must_use]
|
|
49
|
+
pub fn local_graph(self) -> LocalGraph {
|
|
50
|
+
self.local_graph
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub fn index(&mut self) {
|
|
54
|
+
let Ok(signature) = node::parse(self.source) else {
|
|
55
|
+
self.local_graph.add_diagnostic(
|
|
56
|
+
Rule::ParseError,
|
|
57
|
+
Offset::new(0, 0),
|
|
58
|
+
"Failed to parse RBS document".to_string(),
|
|
59
|
+
);
|
|
60
|
+
return;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
self.visit(&signature.as_node());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Converts an RBS `TypeNameNode` into a rubydex `NameId`.
|
|
67
|
+
///
|
|
68
|
+
/// Walks the namespace path (e.g. `Foo::Bar` in `Foo::Bar::Baz`) to build
|
|
69
|
+
/// a `ParentScope` chain, then creates the final `Name` for the leaf segment.
|
|
70
|
+
fn index_type_name(&mut self, type_name: &TypeNameNode, nesting_name_id: Option<NameId>) -> NameId {
|
|
71
|
+
let namespace = type_name.namespace();
|
|
72
|
+
|
|
73
|
+
let mut parent_scope = if namespace.absolute() {
|
|
74
|
+
ParentScope::TopLevel
|
|
75
|
+
} else {
|
|
76
|
+
ParentScope::None
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for path_node in namespace.path().iter() {
|
|
80
|
+
let Node::Symbol(symbol) = path_node else {
|
|
81
|
+
continue;
|
|
82
|
+
};
|
|
83
|
+
parent_scope = ParentScope::Some(self.intern_name(&symbol, parent_scope, nesting_name_id));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
self.intern_name(&type_name.name(), parent_scope, nesting_name_id)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fn intern_name(
|
|
90
|
+
&mut self,
|
|
91
|
+
symbol: &node::SymbolNode,
|
|
92
|
+
parent_scope: ParentScope,
|
|
93
|
+
nesting_name_id: Option<NameId>,
|
|
94
|
+
) -> NameId {
|
|
95
|
+
let string_id = self.local_graph.intern_string(symbol.as_str().to_owned());
|
|
96
|
+
self.local_graph
|
|
97
|
+
.add_name(Name::new(string_id, parent_scope, nesting_name_id))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fn parent_lexical_scope_id(&self) -> Option<DefinitionId> {
|
|
101
|
+
self.nesting_stack.last().copied()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn nesting_name_id(&self, lexical_nesting_id: Option<DefinitionId>) -> Option<NameId> {
|
|
105
|
+
lexical_nesting_id.map(|id| {
|
|
106
|
+
let owner = self
|
|
107
|
+
.local_graph
|
|
108
|
+
.definitions()
|
|
109
|
+
.get(&id)
|
|
110
|
+
.expect("owner definition should exist");
|
|
111
|
+
*owner.name_id().expect("nesting definition should have a name")
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn add_mixin_to_current_lexical_scope(&mut self, owner_id: DefinitionId, mixin: Mixin) {
|
|
116
|
+
let owner = self
|
|
117
|
+
.local_graph
|
|
118
|
+
.get_definition_mut(owner_id)
|
|
119
|
+
.expect("owner definition should exist");
|
|
120
|
+
|
|
121
|
+
match owner {
|
|
122
|
+
Definition::Class(class) => class.add_mixin(mixin),
|
|
123
|
+
Definition::Module(module) => module.add_mixin(mixin),
|
|
124
|
+
_ => unreachable!("RBS nesting stack only contains modules/classes"),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn index_mixin(&mut self, type_name: &TypeNameNode, mixin_fn: fn(ConstantReferenceId) -> Mixin) {
|
|
129
|
+
let Some(lexical_nesting_id) = self.parent_lexical_scope_id() else {
|
|
130
|
+
return;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
let nesting_name_id = self.nesting_name_id(Some(lexical_nesting_id));
|
|
134
|
+
let name_id = self.index_type_name(type_name, nesting_name_id);
|
|
135
|
+
let offset = Offset::from_rbs_location(&type_name.location());
|
|
136
|
+
|
|
137
|
+
let constant_ref_id =
|
|
138
|
+
self.local_graph
|
|
139
|
+
.add_constant_reference(ConstantReference::new(name_id, self.uri_id, offset));
|
|
140
|
+
|
|
141
|
+
self.add_mixin_to_current_lexical_scope(lexical_nesting_id, mixin_fn(constant_ref_id));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fn add_member_to_current_lexical_scope(&mut self, owner_id: DefinitionId, member_id: DefinitionId) {
|
|
145
|
+
let owner = self
|
|
146
|
+
.local_graph
|
|
147
|
+
.get_definition_mut(owner_id)
|
|
148
|
+
.expect("owner definition should exist");
|
|
149
|
+
|
|
150
|
+
match owner {
|
|
151
|
+
Definition::Module(module) => module.add_member(member_id),
|
|
152
|
+
Definition::Class(class) => class.add_member(member_id),
|
|
153
|
+
_ => unreachable!("RBS nesting stack only contains modules/classes"),
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[allow(clippy::cast_possible_truncation)]
|
|
158
|
+
fn collect_comments(&self, comment_node: Option<CommentNode>) -> Box<[Comment]> {
|
|
159
|
+
let Some(comment_node) = comment_node else {
|
|
160
|
+
return Box::new([]);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
let location = comment_node.location();
|
|
164
|
+
let start = location.start().cast_unsigned() as usize;
|
|
165
|
+
let end = location.end().cast_unsigned() as usize;
|
|
166
|
+
|
|
167
|
+
let comment_block = &self.source[start..end];
|
|
168
|
+
let lines: Vec<&str> = comment_block.split('\n').collect();
|
|
169
|
+
|
|
170
|
+
let mut comments = Vec::with_capacity(lines.len());
|
|
171
|
+
let mut current_offset = start as u32;
|
|
172
|
+
|
|
173
|
+
for (i, line) in lines.iter().enumerate() {
|
|
174
|
+
let mut size = 1;
|
|
175
|
+
let mut line = *line;
|
|
176
|
+
|
|
177
|
+
if line.ends_with('\r') {
|
|
178
|
+
line = line.trim_end_matches('\r');
|
|
179
|
+
size += 1;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let line_indent = if i == 0 {
|
|
183
|
+
0
|
|
184
|
+
} else {
|
|
185
|
+
line.len() - line.trim_start().len()
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Skip past indentation to the comment text
|
|
189
|
+
current_offset += line_indent as u32;
|
|
190
|
+
|
|
191
|
+
let line_text = line[line_indent..].to_string();
|
|
192
|
+
let line_bytes = line_text.len() as u32;
|
|
193
|
+
let offset = Offset::new(current_offset, current_offset + line_bytes);
|
|
194
|
+
comments.push(Comment::new(offset, line_text));
|
|
195
|
+
|
|
196
|
+
// Advance past current line text + \r (if present) + \n for the next line
|
|
197
|
+
if i < lines.len() - 1 {
|
|
198
|
+
current_offset += line_bytes + size;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
comments.into_boxed_slice()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fn register_definition(
|
|
206
|
+
&mut self,
|
|
207
|
+
definition: Definition,
|
|
208
|
+
lexical_nesting_id: Option<DefinitionId>,
|
|
209
|
+
) -> DefinitionId {
|
|
210
|
+
let definition_id = self.local_graph.add_definition(definition);
|
|
211
|
+
if let Some(id) = lexical_nesting_id {
|
|
212
|
+
self.add_member_to_current_lexical_scope(id, definition_id);
|
|
213
|
+
}
|
|
214
|
+
definition_id
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
|
218
|
+
fn source_at(&self, location: &node::RBSLocationRange) -> String {
|
|
219
|
+
let start = location.start() as usize;
|
|
220
|
+
let end = location.end() as usize;
|
|
221
|
+
self.source[start..end].to_string()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn intern_param(&mut self, param: &node::FunctionParamNode, default_name: &str) -> ParameterStruct {
|
|
225
|
+
if let Some(name_loc) = param.name_location() {
|
|
226
|
+
let str_id = self.local_graph.intern_string(self.source_at(&name_loc));
|
|
227
|
+
ParameterStruct::new(Offset::from_rbs_location(&name_loc), str_id)
|
|
228
|
+
} else {
|
|
229
|
+
let location = param.type_().location();
|
|
230
|
+
let str_id = self.local_graph.intern_string(default_name.to_string());
|
|
231
|
+
ParameterStruct::new(Offset::from_rbs_location(&location), str_id)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
fn collect_parameters(&mut self, function_node: &FunctionTypeNode) -> Vec<Parameter> {
|
|
236
|
+
let mut parameters = Vec::new();
|
|
237
|
+
let mut positional_index: usize = 0;
|
|
238
|
+
|
|
239
|
+
for node in function_node.required_positionals().iter() {
|
|
240
|
+
let Node::FunctionParam(param) = node else {
|
|
241
|
+
panic!("Expected FunctionParam node, found {node:?}")
|
|
242
|
+
};
|
|
243
|
+
let default_name = format!("arg{positional_index}");
|
|
244
|
+
parameters.push(Parameter::RequiredPositional(self.intern_param(¶m, &default_name)));
|
|
245
|
+
positional_index += 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for node in function_node.optional_positionals().iter() {
|
|
249
|
+
let Node::FunctionParam(param) = node else {
|
|
250
|
+
panic!("Expected FunctionParam node, found {node:?}")
|
|
251
|
+
};
|
|
252
|
+
let default_name = format!("arg{positional_index}");
|
|
253
|
+
parameters.push(Parameter::OptionalPositional(self.intern_param(¶m, &default_name)));
|
|
254
|
+
positional_index += 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if let Some(node) = function_node.rest_positionals() {
|
|
258
|
+
let Node::FunctionParam(param) = node else {
|
|
259
|
+
panic!("Expected FunctionParam node, found {node:?}")
|
|
260
|
+
};
|
|
261
|
+
parameters.push(Parameter::RestPositional(self.intern_param(¶m, "args")));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for node in function_node.trailing_positionals().iter() {
|
|
265
|
+
let Node::FunctionParam(param) = node else {
|
|
266
|
+
panic!("Expected FunctionParam node, found {node:?}")
|
|
267
|
+
};
|
|
268
|
+
let default_name = format!("arg{positional_index}");
|
|
269
|
+
parameters.push(Parameter::Post(self.intern_param(¶m, &default_name)));
|
|
270
|
+
positional_index += 1;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
for (key, _value) in function_node.required_keywords().iter() {
|
|
274
|
+
let name = self.source_at(&key.location());
|
|
275
|
+
let offset = Offset::from_rbs_location(&key.location());
|
|
276
|
+
let str_id = self.local_graph.intern_string(name);
|
|
277
|
+
parameters.push(Parameter::RequiredKeyword(ParameterStruct::new(offset, str_id)));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
for (key, _value) in function_node.optional_keywords().iter() {
|
|
281
|
+
let name = self.source_at(&key.location());
|
|
282
|
+
let offset = Offset::from_rbs_location(&key.location());
|
|
283
|
+
let str_id = self.local_graph.intern_string(name);
|
|
284
|
+
parameters.push(Parameter::OptionalKeyword(ParameterStruct::new(offset, str_id)));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if let Some(node) = function_node.rest_keywords() {
|
|
288
|
+
let Node::FunctionParam(param) = &node else {
|
|
289
|
+
panic!("Expected FunctionParam node, found {node:?}")
|
|
290
|
+
};
|
|
291
|
+
parameters.push(Parameter::RestKeyword(self.intern_param(param, "kwargs")));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
parameters
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
fn build_signatures(sigs: &mut Vec<Signature>) -> Signatures {
|
|
298
|
+
match sigs.len() {
|
|
299
|
+
0 => Signatures::Simple(Box::new([])),
|
|
300
|
+
1 => Signatures::Simple(sigs.pop().unwrap()),
|
|
301
|
+
_ => Signatures::Overloaded(std::mem::take(sigs).into_boxed_slice()),
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
fn collect_overload_signatures(&mut self, def_node: &node::MethodDefinitionNode) -> Signatures {
|
|
306
|
+
let mut sigs: Vec<Signature> = Vec::new();
|
|
307
|
+
|
|
308
|
+
for overload_node in def_node.overloads().iter() {
|
|
309
|
+
let Node::MethodDefinitionOverload(overload) = overload_node else {
|
|
310
|
+
panic!("Expected MethodDefinitionOverload node in overloads, found {overload_node:?}");
|
|
311
|
+
};
|
|
312
|
+
let Node::MethodType(method_type) = overload.method_type() else {
|
|
313
|
+
panic!(
|
|
314
|
+
"Expected MethodType node in overloads, found {:?}",
|
|
315
|
+
overload.method_type()
|
|
316
|
+
);
|
|
317
|
+
};
|
|
318
|
+
let mut params = match method_type.type_() {
|
|
319
|
+
Node::FunctionType(function_type) => self.collect_parameters(&function_type),
|
|
320
|
+
Node::UntypedFunctionType(_) => Vec::new(),
|
|
321
|
+
other => panic!("Expected FunctionType node in overloads, found {other:?}"),
|
|
322
|
+
};
|
|
323
|
+
if let Some(block) = method_type.block() {
|
|
324
|
+
let str_id = self.local_graph.intern_string("block".to_string());
|
|
325
|
+
let offset = Offset::from_rbs_location(&block.location());
|
|
326
|
+
params.push(Parameter::Block(ParameterStruct::new(offset, str_id)));
|
|
327
|
+
}
|
|
328
|
+
sigs.push(params.into_boxed_slice());
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
Self::build_signatures(&mut sigs)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// Registers two method definitions for `module_function` (SingletonInstance):
|
|
335
|
+
/// a public singleton method and a private instance method.
|
|
336
|
+
#[allow(clippy::too_many_arguments)]
|
|
337
|
+
fn register_singleton_instance_method(
|
|
338
|
+
&mut self,
|
|
339
|
+
str_id: StringId,
|
|
340
|
+
offset: Offset,
|
|
341
|
+
comments: Box<[Comment]>,
|
|
342
|
+
flags: DefinitionFlags,
|
|
343
|
+
lexical_nesting_id: Option<DefinitionId>,
|
|
344
|
+
signatures: Signatures,
|
|
345
|
+
) {
|
|
346
|
+
let singleton_def = Definition::Method(Box::new(MethodDefinition::new(
|
|
347
|
+
str_id,
|
|
348
|
+
self.uri_id,
|
|
349
|
+
offset.clone(),
|
|
350
|
+
comments.clone(),
|
|
351
|
+
flags.clone(),
|
|
352
|
+
lexical_nesting_id,
|
|
353
|
+
signatures.clone(),
|
|
354
|
+
Visibility::Public,
|
|
355
|
+
lexical_nesting_id.map(Receiver::SelfReceiver),
|
|
356
|
+
)));
|
|
357
|
+
self.register_definition(singleton_def, lexical_nesting_id);
|
|
358
|
+
|
|
359
|
+
let instance_def = Definition::Method(Box::new(MethodDefinition::new(
|
|
360
|
+
str_id,
|
|
361
|
+
self.uri_id,
|
|
362
|
+
offset,
|
|
363
|
+
comments,
|
|
364
|
+
flags,
|
|
365
|
+
lexical_nesting_id,
|
|
366
|
+
signatures,
|
|
367
|
+
Visibility::Private,
|
|
368
|
+
None,
|
|
369
|
+
)));
|
|
370
|
+
self.register_definition(instance_def, lexical_nesting_id);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/// Extracts definition flags from the list of RBS annotations.
|
|
374
|
+
///
|
|
375
|
+
/// panics when a non-annotation node is encountered in the list, since only annotations should be present.
|
|
376
|
+
fn flags(list: &NodeList) -> DefinitionFlags {
|
|
377
|
+
let mut flags = DefinitionFlags::empty();
|
|
378
|
+
|
|
379
|
+
for node in list.iter() {
|
|
380
|
+
if let Node::Annotation(annotation) = node {
|
|
381
|
+
let string = annotation.string();
|
|
382
|
+
let content = string.as_bytes();
|
|
383
|
+
if content == b"deprecated" || content.starts_with(b"deprecated:") {
|
|
384
|
+
flags |= DefinitionFlags::DEPRECATED;
|
|
385
|
+
}
|
|
386
|
+
} else {
|
|
387
|
+
panic!("Expected annotation node, found {node:?}");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
flags
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
impl Visit for RBSIndexer<'_> {
|
|
396
|
+
fn visit_class_node(&mut self, class_node: &ClassNode) {
|
|
397
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
398
|
+
let nesting_name_id = self.nesting_name_id(lexical_nesting_id);
|
|
399
|
+
|
|
400
|
+
let type_name = class_node.name();
|
|
401
|
+
let name_id = self.index_type_name(&type_name, nesting_name_id);
|
|
402
|
+
let offset = Offset::from_rbs_location(&class_node.location());
|
|
403
|
+
let name_offset = Offset::from_rbs_location(&type_name.name().location());
|
|
404
|
+
|
|
405
|
+
let comments = self.collect_comments(class_node.comment());
|
|
406
|
+
|
|
407
|
+
let superclass_ref = class_node.super_class().as_ref().map(|super_node| {
|
|
408
|
+
let type_name = super_node.name();
|
|
409
|
+
let name_id = self.index_type_name(&type_name, nesting_name_id);
|
|
410
|
+
let offset = Offset::from_rbs_location(&super_node.location());
|
|
411
|
+
self.local_graph
|
|
412
|
+
.add_constant_reference(ConstantReference::new(name_id, self.uri_id, offset))
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
let definition = Definition::Class(Box::new(ClassDefinition::new(
|
|
416
|
+
name_id,
|
|
417
|
+
self.uri_id,
|
|
418
|
+
offset,
|
|
419
|
+
name_offset,
|
|
420
|
+
comments,
|
|
421
|
+
Self::flags(&class_node.annotations()),
|
|
422
|
+
lexical_nesting_id,
|
|
423
|
+
superclass_ref,
|
|
424
|
+
)));
|
|
425
|
+
|
|
426
|
+
let definition_id = self.register_definition(definition, lexical_nesting_id);
|
|
427
|
+
self.nesting_stack.push(definition_id);
|
|
428
|
+
let saved_visibility = std::mem::replace(&mut self.current_visibility, Visibility::Public);
|
|
429
|
+
|
|
430
|
+
for member in class_node.members().iter() {
|
|
431
|
+
self.visit(&member);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
self.current_visibility = saved_visibility;
|
|
435
|
+
self.nesting_stack.pop();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
fn visit_module_node(&mut self, module_node: &ModuleNode) {
|
|
439
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
440
|
+
let nesting_name_id = self.nesting_name_id(lexical_nesting_id);
|
|
441
|
+
|
|
442
|
+
let type_name = module_node.name();
|
|
443
|
+
let name_id = self.index_type_name(&type_name, nesting_name_id);
|
|
444
|
+
let offset = Offset::from_rbs_location(&module_node.location());
|
|
445
|
+
let name_offset = Offset::from_rbs_location(&type_name.name().location());
|
|
446
|
+
|
|
447
|
+
let comments = self.collect_comments(module_node.comment());
|
|
448
|
+
|
|
449
|
+
let definition = Definition::Module(Box::new(ModuleDefinition::new(
|
|
450
|
+
name_id,
|
|
451
|
+
self.uri_id,
|
|
452
|
+
offset,
|
|
453
|
+
name_offset,
|
|
454
|
+
comments,
|
|
455
|
+
Self::flags(&module_node.annotations()),
|
|
456
|
+
lexical_nesting_id,
|
|
457
|
+
)));
|
|
458
|
+
|
|
459
|
+
let definition_id = self.register_definition(definition, lexical_nesting_id);
|
|
460
|
+
self.nesting_stack.push(definition_id);
|
|
461
|
+
let saved_visibility = std::mem::replace(&mut self.current_visibility, Visibility::Public);
|
|
462
|
+
|
|
463
|
+
for member in module_node.members().iter() {
|
|
464
|
+
self.visit(&member);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
self.current_visibility = saved_visibility;
|
|
468
|
+
self.nesting_stack.pop();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
fn visit_constant_node(&mut self, constant_node: &ConstantNode) {
|
|
472
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
473
|
+
let nesting_name_id = self.nesting_name_id(lexical_nesting_id);
|
|
474
|
+
|
|
475
|
+
let type_name = constant_node.name();
|
|
476
|
+
let name_id = self.index_type_name(&type_name, nesting_name_id);
|
|
477
|
+
let offset = Offset::from_rbs_location(&constant_node.location());
|
|
478
|
+
|
|
479
|
+
let comments = self.collect_comments(constant_node.comment());
|
|
480
|
+
|
|
481
|
+
let definition = Definition::Constant(Box::new(ConstantDefinition::new(
|
|
482
|
+
name_id,
|
|
483
|
+
self.uri_id,
|
|
484
|
+
offset,
|
|
485
|
+
comments,
|
|
486
|
+
Self::flags(&constant_node.annotations()),
|
|
487
|
+
lexical_nesting_id,
|
|
488
|
+
)));
|
|
489
|
+
|
|
490
|
+
self.register_definition(definition, lexical_nesting_id);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
fn visit_global_node(&mut self, global_node: &GlobalNode) {
|
|
494
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
495
|
+
|
|
496
|
+
let str_id = self.local_graph.intern_string(global_node.name().to_string());
|
|
497
|
+
let offset = Offset::from_rbs_location(&global_node.location());
|
|
498
|
+
|
|
499
|
+
let comments = self.collect_comments(global_node.comment());
|
|
500
|
+
|
|
501
|
+
let definition = Definition::GlobalVariable(Box::new(GlobalVariableDefinition::new(
|
|
502
|
+
str_id,
|
|
503
|
+
self.uri_id,
|
|
504
|
+
offset,
|
|
505
|
+
comments,
|
|
506
|
+
Self::flags(&global_node.annotations()),
|
|
507
|
+
lexical_nesting_id,
|
|
508
|
+
)));
|
|
509
|
+
|
|
510
|
+
self.register_definition(definition, lexical_nesting_id);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
fn visit_include_node(&mut self, include_node: &IncludeNode) {
|
|
514
|
+
self.index_mixin(&include_node.name(), |ref_id| {
|
|
515
|
+
Mixin::Include(IncludeDefinition::new(ref_id))
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
fn visit_prepend_node(&mut self, prepend_node: &PrependNode) {
|
|
520
|
+
self.index_mixin(&prepend_node.name(), |ref_id| {
|
|
521
|
+
Mixin::Prepend(PrependDefinition::new(ref_id))
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
fn visit_extend_node(&mut self, extend_node: &ExtendNode) {
|
|
526
|
+
self.index_mixin(&extend_node.name(), |ref_id| {
|
|
527
|
+
Mixin::Extend(ExtendDefinition::new(ref_id))
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
fn visit_alias_node(&mut self, alias_node: &node::AliasNode) {
|
|
532
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
533
|
+
|
|
534
|
+
let receiver = match alias_node.kind() {
|
|
535
|
+
AliasKind::Instance => None,
|
|
536
|
+
AliasKind::Singleton => lexical_nesting_id.map(Receiver::SelfReceiver),
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
let new_name = alias_node.new_name();
|
|
540
|
+
let old_name = alias_node.old_name();
|
|
541
|
+
|
|
542
|
+
let new_name_str_id = self.local_graph.intern_string(format!("{new_name}()"));
|
|
543
|
+
let old_name_str_id = self.local_graph.intern_string(format!("{old_name}()"));
|
|
544
|
+
|
|
545
|
+
let offset = Offset::from_rbs_location(&alias_node.location());
|
|
546
|
+
let comments = self.collect_comments(alias_node.comment());
|
|
547
|
+
|
|
548
|
+
let definition = Definition::MethodAlias(Box::new(MethodAliasDefinition::new(
|
|
549
|
+
new_name_str_id,
|
|
550
|
+
old_name_str_id,
|
|
551
|
+
self.uri_id,
|
|
552
|
+
offset,
|
|
553
|
+
comments,
|
|
554
|
+
Self::flags(&alias_node.annotations()),
|
|
555
|
+
lexical_nesting_id,
|
|
556
|
+
receiver,
|
|
557
|
+
)));
|
|
558
|
+
|
|
559
|
+
self.register_definition(definition, lexical_nesting_id);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
fn visit_method_definition_node(&mut self, def_node: &node::MethodDefinitionNode) {
|
|
563
|
+
let str_id = self.local_graph.intern_string(format!("{}()", def_node.name()));
|
|
564
|
+
let offset = Offset::from_rbs_location(&def_node.location());
|
|
565
|
+
let comments = self.collect_comments(def_node.comment());
|
|
566
|
+
let flags = Self::flags(&def_node.annotations());
|
|
567
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
568
|
+
let signatures = self.collect_overload_signatures(def_node);
|
|
569
|
+
|
|
570
|
+
if def_node.kind() == node::MethodDefinitionKind::SingletonInstance {
|
|
571
|
+
self.register_singleton_instance_method(str_id, offset, comments, flags, lexical_nesting_id, signatures);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
let (visibility, receiver) = match def_node.kind() {
|
|
576
|
+
node::MethodDefinitionKind::Instance => {
|
|
577
|
+
let vis = match def_node.visibility() {
|
|
578
|
+
node::MethodDefinitionVisibility::Private => Visibility::Private,
|
|
579
|
+
node::MethodDefinitionVisibility::Public => Visibility::Public,
|
|
580
|
+
node::MethodDefinitionVisibility::Unspecified => self.current_visibility,
|
|
581
|
+
};
|
|
582
|
+
(vis, None)
|
|
583
|
+
}
|
|
584
|
+
node::MethodDefinitionKind::Singleton => {
|
|
585
|
+
let vis = match def_node.visibility() {
|
|
586
|
+
node::MethodDefinitionVisibility::Private => Visibility::Private,
|
|
587
|
+
node::MethodDefinitionVisibility::Public | node::MethodDefinitionVisibility::Unspecified => {
|
|
588
|
+
Visibility::Public
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
(
|
|
592
|
+
vis,
|
|
593
|
+
Some(Receiver::SelfReceiver(
|
|
594
|
+
lexical_nesting_id.expect("Singleton method must have a lexical enclosing scope"),
|
|
595
|
+
)),
|
|
596
|
+
)
|
|
597
|
+
}
|
|
598
|
+
node::MethodDefinitionKind::SingletonInstance => unreachable!("handled above"),
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
let definition = Definition::Method(Box::new(MethodDefinition::new(
|
|
602
|
+
str_id,
|
|
603
|
+
self.uri_id,
|
|
604
|
+
offset,
|
|
605
|
+
comments,
|
|
606
|
+
flags,
|
|
607
|
+
lexical_nesting_id,
|
|
608
|
+
signatures,
|
|
609
|
+
visibility,
|
|
610
|
+
receiver,
|
|
611
|
+
)));
|
|
612
|
+
|
|
613
|
+
self.register_definition(definition, lexical_nesting_id);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
fn visit_public_node(&mut self, _public_node: &node::PublicNode) {
|
|
617
|
+
self.current_visibility = Visibility::Public;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
fn visit_private_node(&mut self, _private_node: &node::PrivateNode) {
|
|
621
|
+
self.current_visibility = Visibility::Private;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
#[cfg(test)]
|
|
626
|
+
mod tests {
|
|
627
|
+
use ruby_rbs::node::{self, Node, NodeList};
|
|
628
|
+
|
|
629
|
+
use crate::indexing::rbs_indexer::RBSIndexer;
|
|
630
|
+
use crate::model::definitions::{Definition, DefinitionFlags, Parameter, Signatures};
|
|
631
|
+
use crate::model::visibility::Visibility;
|
|
632
|
+
use crate::test_utils::LocalGraphTest;
|
|
633
|
+
use crate::{
|
|
634
|
+
assert_def_comments_eq, assert_def_mixins_eq, assert_def_name_eq, assert_def_name_offset_eq, assert_def_str_eq,
|
|
635
|
+
assert_def_superclass_ref_eq, assert_definition_at, assert_local_diagnostics_eq, assert_method_has_receiver,
|
|
636
|
+
assert_no_local_diagnostics, assert_offset_string, assert_string_eq,
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
macro_rules! assert_parameter {
|
|
640
|
+
($expr:expr, $variant:ident, |$param:ident| $body:block) => {
|
|
641
|
+
match $expr {
|
|
642
|
+
Parameter::$variant($param) => $body,
|
|
643
|
+
_ => panic!(
|
|
644
|
+
"parameter kind mismatch: expected `{}`, got `{:?}`",
|
|
645
|
+
stringify!($variant),
|
|
646
|
+
$expr
|
|
647
|
+
),
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
fn index_source(source: &str) -> LocalGraphTest {
|
|
653
|
+
LocalGraphTest::new_rbs("file:///foo.rbs", source)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
#[test]
|
|
657
|
+
fn index_source_with_errors() {
|
|
658
|
+
let context = index_source("module");
|
|
659
|
+
|
|
660
|
+
assert_local_diagnostics_eq!(&context, ["parse-error: Failed to parse RBS document (1:1-1:1)"]);
|
|
661
|
+
|
|
662
|
+
assert!(context.graph().definitions().is_empty());
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
#[test]
|
|
666
|
+
fn index_module_node() {
|
|
667
|
+
let context = index_source({
|
|
668
|
+
"
|
|
669
|
+
module Foo
|
|
670
|
+
module Bar
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
"
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
assert_no_local_diagnostics!(&context);
|
|
677
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
678
|
+
|
|
679
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |def| {
|
|
680
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
681
|
+
assert_def_name_offset_eq!(&context, def, "1:8-1:11");
|
|
682
|
+
assert_eq!(1, def.members().len());
|
|
683
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
assert_definition_at!(&context, "2:3-3:6", Module, |def| {
|
|
687
|
+
assert_def_name_eq!(&context, def, "Bar");
|
|
688
|
+
assert_def_name_offset_eq!(&context, def, "2:10-2:13");
|
|
689
|
+
|
|
690
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |parent_nesting| {
|
|
691
|
+
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
692
|
+
assert_eq!(parent_nesting.members()[0], def.id());
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
#[test]
|
|
698
|
+
fn index_module_node_with_qualified_name() {
|
|
699
|
+
let context = index_source({
|
|
700
|
+
"
|
|
701
|
+
module Foo
|
|
702
|
+
module Bar::Baz
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
"
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
assert_no_local_diagnostics!(&context);
|
|
709
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
710
|
+
|
|
711
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |def| {
|
|
712
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
713
|
+
assert_def_name_offset_eq!(&context, def, "1:8-1:11");
|
|
714
|
+
assert_eq!(1, def.members().len());
|
|
715
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
assert_definition_at!(&context, "2:3-3:6", Module, |def| {
|
|
719
|
+
assert_def_name_eq!(&context, def, "Bar::Baz");
|
|
720
|
+
assert_def_name_offset_eq!(&context, def, "2:15-2:18");
|
|
721
|
+
|
|
722
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |parent_nesting| {
|
|
723
|
+
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
724
|
+
assert_eq!(parent_nesting.members()[0], def.id());
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
#[test]
|
|
730
|
+
fn index_class_node() {
|
|
731
|
+
let context = index_source({
|
|
732
|
+
"
|
|
733
|
+
class Foo
|
|
734
|
+
class Bar
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
"
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
assert_no_local_diagnostics!(&context);
|
|
741
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
742
|
+
|
|
743
|
+
assert_definition_at!(&context, "1:1-4:4", Class, |def| {
|
|
744
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
745
|
+
assert_def_name_offset_eq!(&context, def, "1:7-1:10");
|
|
746
|
+
assert_eq!(1, def.members().len());
|
|
747
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
assert_definition_at!(&context, "2:3-3:6", Class, |def| {
|
|
751
|
+
assert_def_name_eq!(&context, def, "Bar");
|
|
752
|
+
assert_def_name_offset_eq!(&context, def, "2:9-2:12");
|
|
753
|
+
|
|
754
|
+
assert_definition_at!(&context, "1:1-4:4", Class, |parent_nesting| {
|
|
755
|
+
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
756
|
+
assert_eq!(parent_nesting.members()[0], def.id());
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
#[test]
|
|
762
|
+
fn index_class_node_with_superclass() {
|
|
763
|
+
let context = index_source({
|
|
764
|
+
"
|
|
765
|
+
class Foo < Bar
|
|
766
|
+
end
|
|
767
|
+
"
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
assert_no_local_diagnostics!(&context);
|
|
771
|
+
|
|
772
|
+
assert_definition_at!(&context, "1:1-2:4", Class, |def| {
|
|
773
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
774
|
+
assert_def_superclass_ref_eq!(&context, def, "Bar");
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
#[test]
|
|
779
|
+
fn index_constant_node() {
|
|
780
|
+
let context = index_source("FOO: String");
|
|
781
|
+
|
|
782
|
+
assert_no_local_diagnostics!(&context);
|
|
783
|
+
assert_eq!(context.graph().definitions().len(), 1);
|
|
784
|
+
|
|
785
|
+
assert_definition_at!(&context, "1:1-1:12", Constant, |def| {
|
|
786
|
+
assert_def_name_eq!(&context, def, "FOO");
|
|
787
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
#[test]
|
|
792
|
+
fn index_qualified_constant_node() {
|
|
793
|
+
let context = index_source("Foo::BAR: String");
|
|
794
|
+
|
|
795
|
+
assert_no_local_diagnostics!(&context);
|
|
796
|
+
assert_eq!(context.graph().definitions().len(), 1);
|
|
797
|
+
|
|
798
|
+
assert_definition_at!(&context, "1:1-1:17", Constant, |def| {
|
|
799
|
+
assert_def_name_eq!(&context, def, "Foo::BAR");
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
#[test]
|
|
804
|
+
fn index_constant_inside_class() {
|
|
805
|
+
let context = index_source({
|
|
806
|
+
"
|
|
807
|
+
class Foo
|
|
808
|
+
FOO: Integer
|
|
809
|
+
end
|
|
810
|
+
"
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
assert_no_local_diagnostics!(&context);
|
|
814
|
+
|
|
815
|
+
assert_definition_at!(&context, "1:1-3:4", Class, |class_def| {
|
|
816
|
+
assert_def_name_eq!(&context, class_def, "Foo");
|
|
817
|
+
assert_eq!(1, class_def.members().len());
|
|
818
|
+
|
|
819
|
+
assert_definition_at!(&context, "2:3-2:15", Constant, |def| {
|
|
820
|
+
assert_def_name_eq!(&context, def, "FOO");
|
|
821
|
+
assert_eq!(class_def.id(), def.lexical_nesting_id().unwrap());
|
|
822
|
+
assert_eq!(class_def.members()[0], def.id());
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
#[test]
|
|
828
|
+
fn index_constant_node_with_comment() {
|
|
829
|
+
let context = index_source({
|
|
830
|
+
"
|
|
831
|
+
# Some documentation
|
|
832
|
+
FOO: String
|
|
833
|
+
"
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
assert_no_local_diagnostics!(&context);
|
|
837
|
+
|
|
838
|
+
assert_definition_at!(&context, "2:1-2:12", Constant, |def| {
|
|
839
|
+
assert_def_name_eq!(&context, def, "FOO");
|
|
840
|
+
assert_def_comments_eq!(&context, def, ["# Some documentation"]);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
#[test]
|
|
845
|
+
fn index_global_node() {
|
|
846
|
+
let context = index_source({
|
|
847
|
+
"
|
|
848
|
+
$foo: String
|
|
849
|
+
|
|
850
|
+
# A global variable
|
|
851
|
+
$bar: Integer
|
|
852
|
+
"
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
assert_no_local_diagnostics!(&context);
|
|
856
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
857
|
+
|
|
858
|
+
assert_definition_at!(&context, "1:1-1:13", GlobalVariable, |def| {
|
|
859
|
+
assert_def_str_eq!(&context, def, "$foo");
|
|
860
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
assert_definition_at!(&context, "4:1-4:14", GlobalVariable, |def| {
|
|
864
|
+
assert_def_str_eq!(&context, def, "$bar");
|
|
865
|
+
assert_def_comments_eq!(&context, def, ["# A global variable"]);
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
#[test]
|
|
870
|
+
fn index_mixins() {
|
|
871
|
+
let context = index_source({
|
|
872
|
+
"
|
|
873
|
+
class Foo
|
|
874
|
+
include Bar
|
|
875
|
+
prepend Baz
|
|
876
|
+
extend Qux
|
|
877
|
+
end
|
|
878
|
+
"
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
assert_no_local_diagnostics!(&context);
|
|
882
|
+
|
|
883
|
+
assert_definition_at!(&context, "1:1-5:4", Class, |def| {
|
|
884
|
+
assert_def_mixins_eq!(&context, def, Include, ["Bar"]);
|
|
885
|
+
assert_def_mixins_eq!(&context, def, Prepend, ["Baz"]);
|
|
886
|
+
assert_def_mixins_eq!(&context, def, Extend, ["Qux"]);
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
#[test]
|
|
891
|
+
fn index_multiple_includes() {
|
|
892
|
+
let context = index_source({
|
|
893
|
+
"
|
|
894
|
+
module Foo
|
|
895
|
+
include Bar
|
|
896
|
+
include Baz
|
|
897
|
+
end
|
|
898
|
+
"
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
assert_no_local_diagnostics!(&context);
|
|
902
|
+
|
|
903
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |def| {
|
|
904
|
+
assert_def_mixins_eq!(&context, def, Include, ["Bar", "Baz"]);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
#[test]
|
|
909
|
+
fn index_include_qualified_name() {
|
|
910
|
+
let context = index_source({
|
|
911
|
+
"
|
|
912
|
+
class Foo
|
|
913
|
+
include Bar::Baz
|
|
914
|
+
end
|
|
915
|
+
"
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
assert_no_local_diagnostics!(&context);
|
|
919
|
+
|
|
920
|
+
assert_definition_at!(&context, "1:1-3:4", Class, |def| {
|
|
921
|
+
assert_def_mixins_eq!(&context, def, Include, ["Baz"]);
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
#[test]
|
|
926
|
+
fn index_class_and_module_nesting() {
|
|
927
|
+
let context = index_source({
|
|
928
|
+
"
|
|
929
|
+
module Foo
|
|
930
|
+
class Bar
|
|
931
|
+
end
|
|
932
|
+
end
|
|
933
|
+
"
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
assert_no_local_diagnostics!(&context);
|
|
937
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
938
|
+
|
|
939
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |module_def| {
|
|
940
|
+
assert_def_name_eq!(&context, module_def, "Foo");
|
|
941
|
+
assert_eq!(1, module_def.members().len());
|
|
942
|
+
|
|
943
|
+
assert_definition_at!(&context, "2:3-3:6", Class, |class_def| {
|
|
944
|
+
assert_eq!(module_def.members()[0], class_def.id());
|
|
945
|
+
assert_def_name_eq!(&context, class_def, "Bar");
|
|
946
|
+
assert_eq!(module_def.id(), class_def.lexical_nesting_id().unwrap());
|
|
947
|
+
});
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
#[test]
|
|
952
|
+
fn flags_test_deprecation() {
|
|
953
|
+
fn extract_annotations<F: FnOnce(&NodeList)>(annots: &[u8], f: F) {
|
|
954
|
+
let source = format!("{} module Foo end", std::str::from_utf8(annots).unwrap());
|
|
955
|
+
let Ok(signature) = node::parse(&source) else {
|
|
956
|
+
panic!("Failed to parse RBS source");
|
|
957
|
+
};
|
|
958
|
+
let decl = signature
|
|
959
|
+
.declarations()
|
|
960
|
+
.iter()
|
|
961
|
+
.next()
|
|
962
|
+
.expect("Expected at least one declaration");
|
|
963
|
+
let Node::Module(module_node) = decl else {
|
|
964
|
+
panic!("Expected a module declaration");
|
|
965
|
+
};
|
|
966
|
+
f(&module_node.annotations());
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
extract_annotations(b"%a{deprecated}", |list| {
|
|
970
|
+
assert!(RBSIndexer::flags(list).contains(DefinitionFlags::DEPRECATED));
|
|
971
|
+
});
|
|
972
|
+
extract_annotations(b"%a{deprecated: Some message here}", |list| {
|
|
973
|
+
assert!(RBSIndexer::flags(list).contains(DefinitionFlags::DEPRECATED));
|
|
974
|
+
});
|
|
975
|
+
extract_annotations(b"%a{deprecated} %a{pure}", |list| {
|
|
976
|
+
assert!(RBSIndexer::flags(list).contains(DefinitionFlags::DEPRECATED));
|
|
977
|
+
});
|
|
978
|
+
extract_annotations(b"", |list| {
|
|
979
|
+
assert!(!RBSIndexer::flags(list).contains(DefinitionFlags::DEPRECATED));
|
|
980
|
+
});
|
|
981
|
+
extract_annotations(b"%a{deprecatedxxxx}", |list| {
|
|
982
|
+
assert!(!RBSIndexer::flags(list).contains(DefinitionFlags::DEPRECATED));
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
#[test]
|
|
987
|
+
fn index_declarations_with_deprecation() {
|
|
988
|
+
let context = index_source({
|
|
989
|
+
"
|
|
990
|
+
%a{deprecated}
|
|
991
|
+
module Foo
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
%a{deprecated: Use Bar2 instead}
|
|
995
|
+
class Bar
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
%a(deprecated)
|
|
999
|
+
FOO: String
|
|
1000
|
+
|
|
1001
|
+
%a[deprecated]
|
|
1002
|
+
$BAR: String
|
|
1003
|
+
"
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
assert_no_local_diagnostics!(&context);
|
|
1007
|
+
|
|
1008
|
+
assert_definition_at!(&context, "2:1-3:4", Module, |def| {
|
|
1009
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
1010
|
+
assert!(def.flags().contains(DefinitionFlags::DEPRECATED));
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
assert_definition_at!(&context, "6:1-7:4", Class, |def| {
|
|
1014
|
+
assert_def_name_eq!(&context, def, "Bar");
|
|
1015
|
+
assert!(def.flags().contains(DefinitionFlags::DEPRECATED));
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
assert_definition_at!(&context, "10:1-10:12", Constant, |def| {
|
|
1019
|
+
assert_def_name_eq!(&context, def, "FOO");
|
|
1020
|
+
assert!(def.flags().contains(DefinitionFlags::DEPRECATED));
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
assert_definition_at!(&context, "13:1-13:13", GlobalVariable, |def| {
|
|
1024
|
+
assert_def_str_eq!(&context, def, "$BAR");
|
|
1025
|
+
assert!(def.flags().contains(DefinitionFlags::DEPRECATED));
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
#[test]
|
|
1030
|
+
fn index_alias_node() {
|
|
1031
|
+
let context = index_source({
|
|
1032
|
+
"
|
|
1033
|
+
class Foo
|
|
1034
|
+
# Some documentation
|
|
1035
|
+
alias bar baz
|
|
1036
|
+
end
|
|
1037
|
+
"
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
assert_no_local_diagnostics!(&context);
|
|
1041
|
+
|
|
1042
|
+
assert_definition_at!(&context, "1:1-4:4", Class, |class_def| {
|
|
1043
|
+
assert_eq!(1, class_def.members().len());
|
|
1044
|
+
|
|
1045
|
+
assert_definition_at!(&context, "3:3-3:16", MethodAlias, |def| {
|
|
1046
|
+
assert_string_eq!(&context, def.new_name_str_id(), "bar()");
|
|
1047
|
+
assert_string_eq!(&context, def.old_name_str_id(), "baz()");
|
|
1048
|
+
assert_def_comments_eq!(&context, def, ["# Some documentation"]);
|
|
1049
|
+
assert_eq!(class_def.id(), def.lexical_nesting_id().unwrap());
|
|
1050
|
+
});
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
#[test]
|
|
1055
|
+
fn index_alias_node_with_deprecation() {
|
|
1056
|
+
let context = index_source({
|
|
1057
|
+
"
|
|
1058
|
+
class Foo
|
|
1059
|
+
%a{deprecated}
|
|
1060
|
+
alias bar baz
|
|
1061
|
+
end
|
|
1062
|
+
"
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
assert_no_local_diagnostics!(&context);
|
|
1066
|
+
|
|
1067
|
+
assert_definition_at!(&context, "3:3-3:16", MethodAlias, |def| {
|
|
1068
|
+
assert!(def.flags().contains(DefinitionFlags::DEPRECATED));
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
#[test]
|
|
1073
|
+
fn index_alias_node_singleton() {
|
|
1074
|
+
let context = index_source({
|
|
1075
|
+
"
|
|
1076
|
+
class Foo
|
|
1077
|
+
alias self.bar self.baz
|
|
1078
|
+
end
|
|
1079
|
+
"
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
assert_no_local_diagnostics!(&context);
|
|
1083
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
1084
|
+
|
|
1085
|
+
assert_definition_at!(&context, "2:3-2:26", MethodAlias, |def| {
|
|
1086
|
+
assert_string_eq!(&context, def.new_name_str_id(), "bar()");
|
|
1087
|
+
assert_string_eq!(&context, def.old_name_str_id(), "baz()");
|
|
1088
|
+
assert_method_has_receiver!(&context, def, "Foo");
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
#[test]
|
|
1093
|
+
fn mixed_singleton_instance_alias_is_not_indexed() {
|
|
1094
|
+
// Mixed aliases (`alias self.x y` and `alias x self.y`) are not valid RBS.
|
|
1095
|
+
// Verify that no alias definitions are produced for these inputs.
|
|
1096
|
+
for source in [
|
|
1097
|
+
"
|
|
1098
|
+
class Foo
|
|
1099
|
+
alias self.bar baz
|
|
1100
|
+
end
|
|
1101
|
+
",
|
|
1102
|
+
"
|
|
1103
|
+
class Foo
|
|
1104
|
+
alias bar self.baz
|
|
1105
|
+
end
|
|
1106
|
+
",
|
|
1107
|
+
] {
|
|
1108
|
+
let context = index_source(source);
|
|
1109
|
+
let has_alias = context
|
|
1110
|
+
.graph()
|
|
1111
|
+
.definitions()
|
|
1112
|
+
.values()
|
|
1113
|
+
.any(|d| matches!(d, Definition::MethodAlias(_)));
|
|
1114
|
+
assert!(!has_alias, "Expected no alias definitions for: {source}");
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
#[test]
|
|
1119
|
+
fn split_multiline_comments() {
|
|
1120
|
+
let context = index_source({
|
|
1121
|
+
"
|
|
1122
|
+
# First line
|
|
1123
|
+
# Second line
|
|
1124
|
+
# Third line
|
|
1125
|
+
class Foo
|
|
1126
|
+
# A comment for Bar
|
|
1127
|
+
# Another line for Bar
|
|
1128
|
+
# One more line for Bar
|
|
1129
|
+
module Bar
|
|
1130
|
+
end
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
# splits strings at the \\n char
|
|
1134
|
+
BAZ: Integer
|
|
1135
|
+
"
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
assert_no_local_diagnostics!(&context);
|
|
1139
|
+
|
|
1140
|
+
assert_definition_at!(&context, "4:1-10:4", Class, |def| {
|
|
1141
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
1142
|
+
assert_def_comments_eq!(&context, def, ["# First line", "# Second line", "# Third line"]);
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
assert_definition_at!(&context, "8:3-9:6", Module, |def| {
|
|
1146
|
+
assert_def_name_eq!(&context, def, "Bar");
|
|
1147
|
+
assert_def_comments_eq!(
|
|
1148
|
+
&context,
|
|
1149
|
+
def,
|
|
1150
|
+
[
|
|
1151
|
+
"# A comment for Bar",
|
|
1152
|
+
"# Another line for Bar",
|
|
1153
|
+
"# One more line for Bar"
|
|
1154
|
+
]
|
|
1155
|
+
);
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
assert_definition_at!(&context, "13:1-13:13", Constant, |def| {
|
|
1159
|
+
assert_def_name_eq!(&context, def, "BAZ");
|
|
1160
|
+
assert_def_comments_eq!(&context, def, ["# splits strings at the \\n char"]);
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
#[test]
|
|
1165
|
+
fn split_multiline_comments_crlf() {
|
|
1166
|
+
// Build the indexer directly to bypass normalize_indentation, which strips \r
|
|
1167
|
+
let source = "# First line\r\n# Second line\r\nclass Foo\r\nend\r\n";
|
|
1168
|
+
let mut indexer = RBSIndexer::new("file:///foo.rbs".to_string(), source);
|
|
1169
|
+
indexer.index();
|
|
1170
|
+
let context = LocalGraphTest::from_local_graph("file:///foo.rbs", source, indexer.local_graph());
|
|
1171
|
+
|
|
1172
|
+
assert_no_local_diagnostics!(&context);
|
|
1173
|
+
|
|
1174
|
+
assert_definition_at!(&context, "3:1-4:4", Class, |def| {
|
|
1175
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
1176
|
+
assert_def_comments_eq!(&context, def, ["# First line", "# Second line"]);
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
#[test]
|
|
1181
|
+
fn index_method_definition() {
|
|
1182
|
+
let context = index_source({
|
|
1183
|
+
"
|
|
1184
|
+
class Foo
|
|
1185
|
+
def foo: () -> void
|
|
1186
|
+
|
|
1187
|
+
def bar: (?) -> void
|
|
1188
|
+
end
|
|
1189
|
+
"
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
assert_no_local_diagnostics!(&context);
|
|
1193
|
+
|
|
1194
|
+
assert_definition_at!(&context, "1:1-5:4", Class, |class_def| {
|
|
1195
|
+
assert_def_name_eq!(&context, class_def, "Foo");
|
|
1196
|
+
assert_eq!(2, class_def.members().len());
|
|
1197
|
+
|
|
1198
|
+
assert_definition_at!(&context, "2:3-2:22", Method, |def| {
|
|
1199
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1200
|
+
assert!(def.receiver().is_none());
|
|
1201
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1202
|
+
assert_eq!(class_def.id(), def.lexical_nesting_id().unwrap());
|
|
1203
|
+
assert_eq!(class_def.members()[0], def.id());
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
assert_definition_at!(&context, "4:3-4:23", Method, |def| {
|
|
1207
|
+
assert_def_str_eq!(&context, def, "bar()");
|
|
1208
|
+
assert!(def.receiver().is_none());
|
|
1209
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1210
|
+
assert_eq!(class_def.id(), def.lexical_nesting_id().unwrap());
|
|
1211
|
+
assert_eq!(class_def.members()[1], def.id());
|
|
1212
|
+
});
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
#[test]
|
|
1217
|
+
fn index_method_definition_with_parameters() {
|
|
1218
|
+
let context = index_source({
|
|
1219
|
+
"
|
|
1220
|
+
class Foo
|
|
1221
|
+
def foo: (String, ?Integer, *String, Symbol, name: String, ?age: Integer, **untyped) -> void
|
|
1222
|
+
|
|
1223
|
+
def bar: (String a, ?Integer b, *String c, Symbol d, name: String e, ?age: Integer f, **untyped rest) -> void
|
|
1224
|
+
end
|
|
1225
|
+
"
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
assert_no_local_diagnostics!(&context);
|
|
1229
|
+
|
|
1230
|
+
// Method without parameter names
|
|
1231
|
+
assert_definition_at!(&context, "2:3-2:95", Method, |def| {
|
|
1232
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1233
|
+
assert_eq!(def.signatures().as_slice()[0].len(), 7);
|
|
1234
|
+
|
|
1235
|
+
assert_parameter!(&def.signatures().as_slice()[0][0], RequiredPositional, |param| {
|
|
1236
|
+
assert_string_eq!(context, param.str(), "arg0");
|
|
1237
|
+
assert_offset_string!(context, param.offset(), "String");
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
assert_parameter!(&def.signatures().as_slice()[0][1], OptionalPositional, |param| {
|
|
1241
|
+
assert_string_eq!(context, param.str(), "arg1");
|
|
1242
|
+
assert_offset_string!(context, param.offset(), "Integer");
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
assert_parameter!(&def.signatures().as_slice()[0][2], RestPositional, |param| {
|
|
1246
|
+
assert_string_eq!(context, param.str(), "args");
|
|
1247
|
+
assert_offset_string!(context, param.offset(), "String");
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
assert_parameter!(&def.signatures().as_slice()[0][3], Post, |param| {
|
|
1251
|
+
assert_string_eq!(context, param.str(), "arg2");
|
|
1252
|
+
assert_offset_string!(context, param.offset(), "Symbol");
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
assert_parameter!(&def.signatures().as_slice()[0][4], RequiredKeyword, |param| {
|
|
1256
|
+
assert_string_eq!(context, param.str(), "name");
|
|
1257
|
+
assert_offset_string!(context, param.offset(), "name");
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
assert_parameter!(&def.signatures().as_slice()[0][5], OptionalKeyword, |param| {
|
|
1261
|
+
assert_string_eq!(context, param.str(), "age");
|
|
1262
|
+
assert_offset_string!(context, param.offset(), "age");
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
assert_parameter!(&def.signatures().as_slice()[0][6], RestKeyword, |param| {
|
|
1266
|
+
assert_string_eq!(context, param.str(), "kwargs");
|
|
1267
|
+
assert_offset_string!(context, param.offset(), "untyped");
|
|
1268
|
+
});
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
// Method with parameter names
|
|
1272
|
+
assert_definition_at!(&context, "4:3-4:112", Method, |def| {
|
|
1273
|
+
assert_def_str_eq!(&context, def, "bar()");
|
|
1274
|
+
assert_eq!(def.signatures().as_slice()[0].len(), 7);
|
|
1275
|
+
|
|
1276
|
+
assert_parameter!(&def.signatures().as_slice()[0][0], RequiredPositional, |param| {
|
|
1277
|
+
assert_string_eq!(context, param.str(), "a");
|
|
1278
|
+
assert_offset_string!(context, param.offset(), "a");
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
assert_parameter!(&def.signatures().as_slice()[0][1], OptionalPositional, |param| {
|
|
1282
|
+
assert_string_eq!(context, param.str(), "b");
|
|
1283
|
+
assert_offset_string!(context, param.offset(), "b");
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
assert_parameter!(&def.signatures().as_slice()[0][2], RestPositional, |param| {
|
|
1287
|
+
assert_string_eq!(context, param.str(), "c");
|
|
1288
|
+
assert_offset_string!(context, param.offset(), "c");
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
assert_parameter!(&def.signatures().as_slice()[0][3], Post, |param| {
|
|
1292
|
+
assert_string_eq!(context, param.str(), "d");
|
|
1293
|
+
assert_offset_string!(context, param.offset(), "d");
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
assert_parameter!(&def.signatures().as_slice()[0][4], RequiredKeyword, |param| {
|
|
1297
|
+
assert_string_eq!(context, param.str(), "name");
|
|
1298
|
+
assert_offset_string!(context, param.offset(), "name");
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
assert_parameter!(&def.signatures().as_slice()[0][5], OptionalKeyword, |param| {
|
|
1302
|
+
assert_string_eq!(context, param.str(), "age");
|
|
1303
|
+
assert_offset_string!(context, param.offset(), "age");
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
assert_parameter!(&def.signatures().as_slice()[0][6], RestKeyword, |param| {
|
|
1307
|
+
assert_string_eq!(context, param.str(), "rest");
|
|
1308
|
+
assert_offset_string!(context, param.offset(), "rest");
|
|
1309
|
+
});
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
#[test]
|
|
1314
|
+
fn index_method_definition_with_multiple_overloads() {
|
|
1315
|
+
let context = index_source({
|
|
1316
|
+
"
|
|
1317
|
+
class Foo
|
|
1318
|
+
def foo: (String) -> Integer
|
|
1319
|
+
| (Integer) -> String
|
|
1320
|
+
| (Symbol, String) -> void
|
|
1321
|
+
end
|
|
1322
|
+
"
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
assert_no_local_diagnostics!(&context);
|
|
1326
|
+
|
|
1327
|
+
assert_definition_at!(&context, "2:3-4:36", Method, |def| {
|
|
1328
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1329
|
+
let sigs = def.signatures().as_slice();
|
|
1330
|
+
assert_eq!(sigs.len(), 3);
|
|
1331
|
+
|
|
1332
|
+
// First overload: (String) -> Integer
|
|
1333
|
+
assert_eq!(sigs[0].len(), 1);
|
|
1334
|
+
assert_parameter!(&sigs[0][0], RequiredPositional, |param| {
|
|
1335
|
+
assert_string_eq!(context, param.str(), "arg0");
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
// Second overload: (Integer) -> String
|
|
1339
|
+
assert_eq!(sigs[1].len(), 1);
|
|
1340
|
+
assert_parameter!(&sigs[1][0], RequiredPositional, |param| {
|
|
1341
|
+
assert_string_eq!(context, param.str(), "arg0");
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
// Third overload: (Symbol, String) -> void
|
|
1345
|
+
assert_eq!(sigs[2].len(), 2);
|
|
1346
|
+
assert_parameter!(&sigs[2][0], RequiredPositional, |param| {
|
|
1347
|
+
assert_string_eq!(context, param.str(), "arg0");
|
|
1348
|
+
});
|
|
1349
|
+
assert_parameter!(&sigs[2][1], RequiredPositional, |param| {
|
|
1350
|
+
assert_string_eq!(context, param.str(), "arg1");
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
assert!(matches!(def.signatures(), Signatures::Overloaded(_)));
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
#[test]
|
|
1358
|
+
fn index_method_definition_with_dot_dot_dot() {
|
|
1359
|
+
let context = index_source({
|
|
1360
|
+
"
|
|
1361
|
+
class Foo
|
|
1362
|
+
def to_s: ...
|
|
1363
|
+
end
|
|
1364
|
+
"
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
assert_no_local_diagnostics!(&context);
|
|
1368
|
+
|
|
1369
|
+
assert_definition_at!(&context, "2:3-2:16", Method, |def| {
|
|
1370
|
+
assert_def_str_eq!(&context, def, "to_s()");
|
|
1371
|
+
let sigs = def.signatures().as_slice();
|
|
1372
|
+
assert_eq!(sigs.len(), 1);
|
|
1373
|
+
assert_eq!(sigs[0].len(), 0);
|
|
1374
|
+
assert!(matches!(def.signatures(), Signatures::Simple(_)));
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
#[test]
|
|
1379
|
+
fn index_method_definition_module_function() {
|
|
1380
|
+
let context = index_source({
|
|
1381
|
+
"
|
|
1382
|
+
class Foo
|
|
1383
|
+
def self?.foo: () -> void
|
|
1384
|
+
end
|
|
1385
|
+
"
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
assert_no_local_diagnostics!(&context);
|
|
1389
|
+
|
|
1390
|
+
let definitions = context.all_definitions_at("2:3-2:28");
|
|
1391
|
+
assert_eq!(definitions.len(), 2, "module_function should create two definitions");
|
|
1392
|
+
|
|
1393
|
+
let instance_method = definitions
|
|
1394
|
+
.iter()
|
|
1395
|
+
.find(|d| matches!(d, Definition::Method(m) if m.receiver().is_none()))
|
|
1396
|
+
.expect("should have instance method definition");
|
|
1397
|
+
let Definition::Method(instance_method) = instance_method else {
|
|
1398
|
+
panic!()
|
|
1399
|
+
};
|
|
1400
|
+
assert_def_str_eq!(&context, instance_method, "foo()");
|
|
1401
|
+
assert_eq!(instance_method.visibility(), &Visibility::Private);
|
|
1402
|
+
|
|
1403
|
+
let singleton_method = definitions
|
|
1404
|
+
.iter()
|
|
1405
|
+
.find(|d| matches!(d, Definition::Method(m) if m.receiver().is_some()))
|
|
1406
|
+
.expect("should have singleton method definition");
|
|
1407
|
+
let Definition::Method(singleton_method) = singleton_method else {
|
|
1408
|
+
panic!()
|
|
1409
|
+
};
|
|
1410
|
+
assert_def_str_eq!(&context, singleton_method, "foo()");
|
|
1411
|
+
assert_eq!(singleton_method.visibility(), &Visibility::Public);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
#[test]
|
|
1415
|
+
fn index_method_definition_singleton_method() {
|
|
1416
|
+
let context = index_source({
|
|
1417
|
+
"
|
|
1418
|
+
class Foo
|
|
1419
|
+
def self.foo: () -> void
|
|
1420
|
+
end
|
|
1421
|
+
"
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
assert_no_local_diagnostics!(&context);
|
|
1425
|
+
|
|
1426
|
+
assert_definition_at!(&context, "2:3-2:27", Method, |def| {
|
|
1427
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1428
|
+
let sigs = def.signatures().as_slice();
|
|
1429
|
+
assert_eq!(sigs.len(), 1);
|
|
1430
|
+
assert_eq!(sigs[0].len(), 0);
|
|
1431
|
+
assert!(matches!(def.signatures(), Signatures::Simple(_)));
|
|
1432
|
+
assert_method_has_receiver!(&context, def, "Foo");
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
#[test]
|
|
1437
|
+
fn index_method_definition_public_private_method() {
|
|
1438
|
+
let context = index_source({
|
|
1439
|
+
"
|
|
1440
|
+
class Foo
|
|
1441
|
+
public def foo: () -> void
|
|
1442
|
+
|
|
1443
|
+
private def bar: () -> void
|
|
1444
|
+
end
|
|
1445
|
+
"
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
assert_no_local_diagnostics!(&context);
|
|
1449
|
+
|
|
1450
|
+
assert_definition_at!(&context, "2:3-2:29", Method, |def| {
|
|
1451
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1452
|
+
let sigs = def.signatures().as_slice();
|
|
1453
|
+
assert_eq!(sigs.len(), 1);
|
|
1454
|
+
assert_eq!(sigs[0].len(), 0);
|
|
1455
|
+
assert!(matches!(def.signatures(), Signatures::Simple(_)));
|
|
1456
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
assert_definition_at!(&context, "4:3-4:30", Method, |def| {
|
|
1460
|
+
assert_def_str_eq!(&context, def, "bar()");
|
|
1461
|
+
let sigs = def.signatures().as_slice();
|
|
1462
|
+
assert_eq!(sigs.len(), 1);
|
|
1463
|
+
assert_eq!(sigs[0].len(), 0);
|
|
1464
|
+
assert!(matches!(def.signatures(), Signatures::Simple(_)));
|
|
1465
|
+
assert_eq!(def.visibility(), &Visibility::Private);
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
#[test]
|
|
1470
|
+
fn index_method_definition_public_private_syntax() {
|
|
1471
|
+
let context = index_source({
|
|
1472
|
+
"
|
|
1473
|
+
class Foo
|
|
1474
|
+
def foo: () -> void
|
|
1475
|
+
def self.foo: () -> void
|
|
1476
|
+
|
|
1477
|
+
public
|
|
1478
|
+
|
|
1479
|
+
def bar: () -> void
|
|
1480
|
+
def self.bar: () -> void
|
|
1481
|
+
|
|
1482
|
+
private
|
|
1483
|
+
|
|
1484
|
+
def baz: () -> void
|
|
1485
|
+
def self.baz: () -> void
|
|
1486
|
+
end
|
|
1487
|
+
"
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
assert_no_local_diagnostics!(&context);
|
|
1491
|
+
|
|
1492
|
+
// Instance method: default visibility is public
|
|
1493
|
+
assert_definition_at!(&context, "2:3-2:22", Method, |def| {
|
|
1494
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1495
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
// Singleton method: always public regardless of current_visibility
|
|
1499
|
+
assert_definition_at!(&context, "3:3-3:27", Method, |def| {
|
|
1500
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1501
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
// Instance method: public due to `public` modifier on line 5
|
|
1505
|
+
assert_definition_at!(&context, "7:3-7:22", Method, |def| {
|
|
1506
|
+
assert_def_str_eq!(&context, def, "bar()");
|
|
1507
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
// Singleton method: always public regardless of current_visibility
|
|
1511
|
+
assert_definition_at!(&context, "8:3-8:27", Method, |def| {
|
|
1512
|
+
assert_def_str_eq!(&context, def, "bar()");
|
|
1513
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
// Instance method: private due to `private` modifier on line 10
|
|
1517
|
+
assert_definition_at!(&context, "12:3-12:22", Method, |def| {
|
|
1518
|
+
assert_def_str_eq!(&context, def, "baz()");
|
|
1519
|
+
assert_eq!(def.visibility(), &Visibility::Private);
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
// Singleton method: always public, ignores `private` modifier
|
|
1523
|
+
assert_definition_at!(&context, "13:3-13:27", Method, |def| {
|
|
1524
|
+
assert_def_str_eq!(&context, def, "baz()");
|
|
1525
|
+
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
#[test]
|
|
1530
|
+
fn index_method_definition_with_block() {
|
|
1531
|
+
let context = index_source({
|
|
1532
|
+
"
|
|
1533
|
+
class Foo
|
|
1534
|
+
def foo: () { (String) -> void } -> void
|
|
1535
|
+
end
|
|
1536
|
+
"
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
assert_no_local_diagnostics!(&context);
|
|
1540
|
+
|
|
1541
|
+
// Method with block: should have 1 parameter (Block)
|
|
1542
|
+
assert_definition_at!(&context, "2:3-2:43", Method, |def| {
|
|
1543
|
+
assert_def_str_eq!(&context, def, "foo()");
|
|
1544
|
+
assert_eq!(def.signatures().as_slice()[0].len(), 1);
|
|
1545
|
+
|
|
1546
|
+
assert_parameter!(&def.signatures().as_slice()[0][0], Block, |param| {
|
|
1547
|
+
assert_string_eq!(context, param.str(), "block");
|
|
1548
|
+
});
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
}
|