rubydex 0.2.0 → 0.2.1
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/ext/rubydex/declaration.c +38 -0
- data/ext/rubydex/graph.c +70 -17
- data/lib/rubydex/declaration.rb +31 -0
- data/lib/rubydex/version.rb +1 -1
- data/rbi/rubydex.rbi +41 -11
- data/rust/rubydex/src/diagnostic.rs +1 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +2 -5
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +14 -7
- data/rust/rubydex/src/model/declaration.rs +48 -0
- data/rust/rubydex/src/model/definitions.rs +18 -9
- data/rust/rubydex/src/model/graph.rs +237 -35
- data/rust/rubydex/src/query.rs +1475 -159
- data/rust/rubydex/src/resolution.rs +173 -61
- data/rust/rubydex/src/resolution_tests.rs +178 -0
- data/rust/rubydex-sys/src/declaration_api.rs +19 -0
- data/rust/rubydex-sys/src/graph_api.rs +89 -5
- metadata +2 -2
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -6,13 +6,14 @@ use std::thread;
|
|
|
6
6
|
use url::Url;
|
|
7
7
|
|
|
8
8
|
use crate::model::built_in::OBJECT_ID;
|
|
9
|
-
use crate::model::declaration::{Ancestor, Declaration};
|
|
9
|
+
use crate::model::declaration::{Ancestor, Declaration, Namespace};
|
|
10
10
|
use crate::model::definitions::{Definition, Parameter};
|
|
11
11
|
use crate::model::graph::Graph;
|
|
12
12
|
use crate::model::identity_maps::IdentityHashSet;
|
|
13
13
|
use crate::model::ids::{DeclarationId, NameId, StringId, UriId};
|
|
14
14
|
use crate::model::keywords::{self, Keyword};
|
|
15
15
|
use crate::model::name::NameRef;
|
|
16
|
+
use crate::model::visibility::Visibility;
|
|
16
17
|
|
|
17
18
|
/// Controls how declaration names are matched against the search query.
|
|
18
19
|
#[derive(Default)]
|
|
@@ -169,18 +170,37 @@ pub enum CompletionCandidate {
|
|
|
169
170
|
pub enum CompletionReceiver {
|
|
170
171
|
/// Completion requested for an expression with no previous token (e.g.: at the start of a line with nothing before)
|
|
171
172
|
/// Includes: all keywords, all global variables and reacheable instance variables, class variables, constants and methods
|
|
172
|
-
|
|
173
|
+
///
|
|
174
|
+
/// `nesting_name_id` represents the lexical scope. It is walked for constants and drives class-variable
|
|
175
|
+
/// lookup (cvars follow lexical scope in Ruby, not self).
|
|
176
|
+
/// `self_decl_id` overrides the self-type used for methods and instance variables when the runtime `self`
|
|
177
|
+
/// diverges from the innermost lexical scope — for example `def Foo.bar` (where self is `Foo` but the
|
|
178
|
+
/// lexical scope is the outer namespace) or `def self.bar`. Callers may pass a `ConstantAlias` id; it is
|
|
179
|
+
/// unwrapped to the target namespace. When `None`, self is derived from the innermost lexical scope.
|
|
180
|
+
Expression {
|
|
181
|
+
self_decl_id: Option<DeclarationId>,
|
|
182
|
+
nesting_name_id: NameId,
|
|
183
|
+
},
|
|
173
184
|
/// Completion requested after a namespace access operator (e.g.: `Foo::`)
|
|
174
|
-
/// Includes: all constants and singleton methods for the namespace and its ancestors
|
|
175
|
-
NamespaceAccess
|
|
185
|
+
/// Includes: all constants and singleton methods for the namespace and its ancestors.
|
|
186
|
+
NamespaceAccess {
|
|
187
|
+
self_decl_id: Option<DeclarationId>,
|
|
188
|
+
namespace_decl_id: DeclarationId,
|
|
189
|
+
},
|
|
176
190
|
/// Completion requested after a method call operator (e.g.: `foo.`, `@bar.`, `@@baz.`, `Qux.`).
|
|
177
191
|
/// In the case of singleton completion (e.g.: `Foo.`), the declaration ID should be for the singleton class (i.e.: `Foo::<Foo>`)
|
|
178
|
-
/// Includes: all methods that exist on the type of the receiver and its ancestors
|
|
179
|
-
MethodCall
|
|
192
|
+
/// Includes: all methods that exist on the type of the receiver and its ancestors.
|
|
193
|
+
MethodCall {
|
|
194
|
+
self_decl_id: Option<DeclarationId>,
|
|
195
|
+
receiver_decl_id: DeclarationId,
|
|
196
|
+
},
|
|
180
197
|
/// Completion requested inside a method call's argument list (e.g.: `foo.bar(|)`)
|
|
181
198
|
/// Includes: everything expressions do plus keyword parameter names of the method being called
|
|
199
|
+
///
|
|
200
|
+
/// Same `self_decl_id` / `nesting_name_id` split as `Expression`.
|
|
182
201
|
MethodArgument {
|
|
183
|
-
|
|
202
|
+
self_decl_id: Option<DeclarationId>,
|
|
203
|
+
nesting_name_id: NameId,
|
|
184
204
|
method_decl_id: DeclarationId,
|
|
185
205
|
},
|
|
186
206
|
}
|
|
@@ -204,26 +224,58 @@ impl<'a> CompletionContext<'a> {
|
|
|
204
224
|
}
|
|
205
225
|
}
|
|
206
226
|
|
|
207
|
-
///
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
/// Method visibility depends both on the type of self and the current lexical scope (due to protected methods)
|
|
228
|
+
fn method_visible_at_call(
|
|
229
|
+
graph: &Graph,
|
|
230
|
+
method_id: DeclarationId,
|
|
231
|
+
defined_in: DeclarationId,
|
|
232
|
+
caller_self: Option<DeclarationId>,
|
|
233
|
+
receiver: DeclarationId,
|
|
234
|
+
) -> bool {
|
|
235
|
+
let Some(visibility) = graph.visibility(&method_id) else {
|
|
236
|
+
return true;
|
|
216
237
|
};
|
|
217
|
-
// Collect only members matching certain kinds
|
|
218
|
-
($graph:expr, $declaration:expr, $context:expr, $candidates:expr, $kinds:pat) => {
|
|
219
|
-
for (member_str_id, member_declaration_id) in $declaration.members() {
|
|
220
|
-
let member = $graph.declarations().get(member_declaration_id).unwrap();
|
|
221
238
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
239
|
+
match visibility {
|
|
240
|
+
Visibility::Public => true,
|
|
241
|
+
Visibility::Private | Visibility::ModuleFunction => caller_self == Some(receiver),
|
|
242
|
+
Visibility::Protected => caller_self.is_some_and(|cs| {
|
|
243
|
+
let defined_in = graph.declarations().get(&defined_in).unwrap().as_namespace().unwrap();
|
|
244
|
+
let descendants = defined_in.descendants();
|
|
245
|
+
descendants.contains(&cs) && descendants.contains(&receiver)
|
|
246
|
+
}),
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// Walks one namespace's direct members. `kind_filter` selects the declaration kinds to surface; `visibility_filter`
|
|
251
|
+
/// decides whether each surviving candidate is reachable from the access site.
|
|
252
|
+
fn collect_members<'a>(
|
|
253
|
+
graph: &'a Graph,
|
|
254
|
+
namespace_id: DeclarationId,
|
|
255
|
+
kind_filter: fn(&Declaration) -> bool,
|
|
256
|
+
visibility_filter: impl Fn(DeclarationId) -> bool,
|
|
257
|
+
completion_ctx: &mut CompletionContext<'a>,
|
|
258
|
+
candidates: &mut Vec<CompletionCandidate>,
|
|
259
|
+
) {
|
|
260
|
+
let namespace = graph.declarations().get(&namespace_id).unwrap().as_namespace().unwrap();
|
|
261
|
+
|
|
262
|
+
for (member_str_id, member_decl_id) in namespace.members() {
|
|
263
|
+
let member = graph.declarations().get(member_decl_id).unwrap();
|
|
264
|
+
|
|
265
|
+
if !kind_filter(member) {
|
|
266
|
+
continue;
|
|
225
267
|
}
|
|
226
|
-
|
|
268
|
+
|
|
269
|
+
if !completion_ctx.dedup(member_str_id) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if !visibility_filter(*member_decl_id) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
candidates.push(CompletionCandidate::Declaration(*member_decl_id));
|
|
278
|
+
}
|
|
227
279
|
}
|
|
228
280
|
|
|
229
281
|
/// Determines all possible completion candidates based on the current context of the cursor. There are multiple cases
|
|
@@ -233,7 +285,6 @@ macro_rules! collect_candidates {
|
|
|
233
285
|
/// global variables that are reacheable from the current lexical scope and self type
|
|
234
286
|
/// - Expression in method arguments collects everything that expressions do and all keyword parameter names that are
|
|
235
287
|
/// applicable to the method being called
|
|
236
|
-
/// everything else
|
|
237
288
|
/// - Namespace access (e.g.: `Foo::`) collects all constants and singleton methods for the namespace that `Foo`
|
|
238
289
|
/// resolves to
|
|
239
290
|
/// - Method calls on anything (e.g.: `foo.`, `@bar.`, `@@baz.`, `Qux.`) collects all methods that exist on the type
|
|
@@ -245,19 +296,30 @@ macro_rules! collect_candidates {
|
|
|
245
296
|
///
|
|
246
297
|
/// # Errors
|
|
247
298
|
///
|
|
248
|
-
/// Will error if the given `
|
|
299
|
+
/// Will error if the given `self_decl_id` does not resolve to a namespace declaration (directly or via
|
|
300
|
+
/// a constant alias).
|
|
249
301
|
pub fn completion_candidates<'a>(
|
|
250
302
|
graph: &'a Graph,
|
|
251
303
|
context: CompletionContext<'a>,
|
|
252
304
|
) -> Result<Vec<CompletionCandidate>, Box<dyn Error>> {
|
|
253
305
|
match context.completion_receiver {
|
|
254
|
-
CompletionReceiver::Expression
|
|
255
|
-
|
|
256
|
-
|
|
306
|
+
CompletionReceiver::Expression {
|
|
307
|
+
self_decl_id,
|
|
308
|
+
nesting_name_id,
|
|
309
|
+
} => expression_completion(graph, self_decl_id, nesting_name_id, context),
|
|
310
|
+
CompletionReceiver::NamespaceAccess {
|
|
311
|
+
self_decl_id,
|
|
312
|
+
namespace_decl_id,
|
|
313
|
+
} => namespace_access_completion(graph, self_decl_id, namespace_decl_id, context),
|
|
314
|
+
CompletionReceiver::MethodCall {
|
|
315
|
+
self_decl_id,
|
|
316
|
+
receiver_decl_id,
|
|
317
|
+
} => method_call_completion(graph, self_decl_id, receiver_decl_id, context),
|
|
257
318
|
CompletionReceiver::MethodArgument {
|
|
258
|
-
|
|
319
|
+
self_decl_id,
|
|
320
|
+
nesting_name_id,
|
|
259
321
|
method_decl_id,
|
|
260
|
-
} => method_argument_completion(graph,
|
|
322
|
+
} => method_argument_completion(graph, self_decl_id, nesting_name_id, method_decl_id, context),
|
|
261
323
|
}
|
|
262
324
|
}
|
|
263
325
|
|
|
@@ -286,12 +348,14 @@ fn resolve_to_namespace(graph: &Graph, decl_id: DeclarationId) -> Result<Option<
|
|
|
286
348
|
/// Collect completion for a namespace access (e.g.: `Foo::`)
|
|
287
349
|
fn namespace_access_completion<'a>(
|
|
288
350
|
graph: &'a Graph,
|
|
351
|
+
self_decl_id: Option<DeclarationId>,
|
|
289
352
|
namespace_decl_id: DeclarationId,
|
|
290
353
|
mut context: CompletionContext<'a>,
|
|
291
354
|
) -> Result<Vec<CompletionCandidate>, Box<dyn Error>> {
|
|
292
355
|
let Some(resolved_id) = resolve_to_namespace(graph, namespace_decl_id)? else {
|
|
293
356
|
return Ok(Vec::new());
|
|
294
357
|
};
|
|
358
|
+
let resolved_caller_self_id = self_decl_id.map(|id| resolve_self_namespace(graph, id)).transpose()?;
|
|
295
359
|
let namespace = graph.declarations().get(&resolved_id).unwrap().as_namespace().unwrap();
|
|
296
360
|
let mut candidates = Vec::new();
|
|
297
361
|
|
|
@@ -305,26 +369,35 @@ fn namespace_access_completion<'a>(
|
|
|
305
369
|
break;
|
|
306
370
|
}
|
|
307
371
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
collect_candidates!(
|
|
372
|
+
collect_members(
|
|
311
373
|
graph,
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
374
|
+
*ancestor_id,
|
|
375
|
+
|d| d.as_namespace().is_some() || d.as_constant().is_some() || d.as_constant_alias().is_some(),
|
|
376
|
+
|id| !matches!(graph.visibility(&id), Some(Visibility::Private)),
|
|
377
|
+
&mut context,
|
|
378
|
+
&mut candidates,
|
|
316
379
|
);
|
|
317
380
|
}
|
|
318
381
|
}
|
|
319
382
|
|
|
320
|
-
//
|
|
383
|
+
// The receiver of an explicit `Foo::` call is the singleton class, so visibility checks
|
|
384
|
+
// compare against it (not against `Foo` itself).
|
|
321
385
|
if let Some(singleton_id) = namespace.singleton_class() {
|
|
322
386
|
let singleton = graph.declarations().get(singleton_id).unwrap().as_namespace().unwrap();
|
|
387
|
+
let receiver = *singleton_id;
|
|
323
388
|
|
|
324
389
|
for ancestor in singleton.ancestors() {
|
|
325
390
|
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
326
|
-
let
|
|
327
|
-
|
|
391
|
+
let defined_in = *ancestor_id;
|
|
392
|
+
|
|
393
|
+
collect_members(
|
|
394
|
+
graph,
|
|
395
|
+
defined_in,
|
|
396
|
+
|d| d.as_method().is_some(),
|
|
397
|
+
|id| method_visible_at_call(graph, id, defined_in, resolved_caller_self_id, receiver),
|
|
398
|
+
&mut context,
|
|
399
|
+
&mut candidates,
|
|
400
|
+
);
|
|
328
401
|
}
|
|
329
402
|
}
|
|
330
403
|
}
|
|
@@ -335,117 +408,248 @@ fn namespace_access_completion<'a>(
|
|
|
335
408
|
/// Collect completion for a method call (e.g.: `foo.`, `@bar.`, `Baz.`)
|
|
336
409
|
fn method_call_completion<'a>(
|
|
337
410
|
graph: &'a Graph,
|
|
411
|
+
self_decl_id: Option<DeclarationId>,
|
|
338
412
|
receiver_decl_id: DeclarationId,
|
|
339
413
|
mut context: CompletionContext<'a>,
|
|
340
414
|
) -> Result<Vec<CompletionCandidate>, Box<dyn Error>> {
|
|
341
415
|
let Some(resolved_id) = resolve_to_namespace(graph, receiver_decl_id)? else {
|
|
342
416
|
return Ok(Vec::new());
|
|
343
417
|
};
|
|
418
|
+
let resolved_caller_self_id = self_decl_id.map(|id| resolve_self_namespace(graph, id)).transpose()?;
|
|
344
419
|
let namespace = graph.declarations().get(&resolved_id).unwrap().as_namespace().unwrap();
|
|
345
420
|
let mut candidates = Vec::new();
|
|
346
421
|
|
|
347
422
|
for ancestor in namespace.ancestors() {
|
|
348
423
|
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
349
|
-
let
|
|
350
|
-
|
|
424
|
+
let defined_in = *ancestor_id;
|
|
425
|
+
collect_members(
|
|
426
|
+
graph,
|
|
427
|
+
defined_in,
|
|
428
|
+
|d| d.as_method().is_some(),
|
|
429
|
+
|id| method_visible_at_call(graph, id, defined_in, resolved_caller_self_id, resolved_id),
|
|
430
|
+
&mut context,
|
|
431
|
+
&mut candidates,
|
|
432
|
+
);
|
|
351
433
|
}
|
|
352
434
|
}
|
|
353
435
|
|
|
354
436
|
Ok(candidates)
|
|
355
437
|
}
|
|
356
438
|
|
|
439
|
+
fn resolve_self_namespace(graph: &Graph, decl_id: DeclarationId) -> Result<DeclarationId, Box<dyn Error>> {
|
|
440
|
+
resolve_to_namespace(graph, decl_id)?
|
|
441
|
+
.ok_or_else(|| format!("self declaration {decl_id:?} not found in graph").into())
|
|
442
|
+
}
|
|
443
|
+
|
|
357
444
|
/// Collect completion for an expression
|
|
358
445
|
fn expression_completion<'a>(
|
|
359
446
|
graph: &'a Graph,
|
|
360
|
-
|
|
447
|
+
self_decl_id: Option<DeclarationId>,
|
|
448
|
+
nesting_name_id: NameId,
|
|
361
449
|
mut context: CompletionContext<'a>,
|
|
362
450
|
) -> Result<Vec<CompletionCandidate>, Box<dyn Error>> {
|
|
363
|
-
let Some(name_ref) = graph.names().get(&
|
|
364
|
-
return Err(format!("Name {
|
|
451
|
+
let Some(name_ref) = graph.names().get(&nesting_name_id) else {
|
|
452
|
+
return Err(format!("Name {nesting_name_id} not found in graph").into());
|
|
365
453
|
};
|
|
366
454
|
let NameRef::Resolved(name_ref) = name_ref else {
|
|
367
|
-
return Err(format!("Expected name {
|
|
455
|
+
return Err(format!("Expected name {nesting_name_id} to be resolved").into());
|
|
456
|
+
};
|
|
457
|
+
// When no explicit self is given, self is the innermost lexical scope (the nesting's own declaration).
|
|
458
|
+
// When explicit, follow constant aliases so callers can pass whatever the expression that set self
|
|
459
|
+
// resolves to without having to unwrap aliases themselves. Missing or non-namespace decls are graph
|
|
460
|
+
// inconsistencies and surfaced as errors.
|
|
461
|
+
let resolved_self_decl_id = match self_decl_id {
|
|
462
|
+
Some(id) => resolve_self_namespace(graph, id)?,
|
|
463
|
+
None => *name_ref.declaration_id(),
|
|
368
464
|
};
|
|
369
|
-
let
|
|
465
|
+
let self_decl = graph
|
|
466
|
+
.declarations()
|
|
467
|
+
.get(&resolved_self_decl_id)
|
|
468
|
+
.unwrap()
|
|
469
|
+
.as_namespace()
|
|
470
|
+
.ok_or("Expected associated declaration to be a namespace")?;
|
|
471
|
+
let innermost_lexical_decl = graph
|
|
370
472
|
.declarations()
|
|
371
473
|
.get(name_ref.declaration_id())
|
|
372
|
-
.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
474
|
+
.unwrap()
|
|
475
|
+
.as_namespace()
|
|
476
|
+
.unwrap();
|
|
477
|
+
|
|
376
478
|
let mut candidates = Vec::new();
|
|
377
479
|
|
|
378
|
-
//
|
|
379
|
-
|
|
480
|
+
// Collect constants. Immediate scope includes inheritance. Outer scopes only include `Object` inheritance when it's a module
|
|
481
|
+
collect_constants_from_lexical_scope(graph, innermost_lexical_decl, &mut context, &mut candidates);
|
|
482
|
+
collect_constants_from_outer_nesting(graph, name_ref, &mut context, &mut candidates);
|
|
380
483
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
484
|
+
// Collect class variables, which are based on the inheritance chain of the attached object of the immediate lexical scope
|
|
485
|
+
collect_class_variables_from_lexical_scope(graph, name_ref, &mut context, &mut candidates);
|
|
486
|
+
|
|
487
|
+
// Globals are accessible from anywhere, regardless of lexical scope or `self` type.
|
|
488
|
+
collect_members(
|
|
489
|
+
graph,
|
|
490
|
+
*OBJECT_ID,
|
|
491
|
+
|d| d.as_global_variable().is_some(),
|
|
492
|
+
|_| true,
|
|
493
|
+
&mut context,
|
|
494
|
+
&mut candidates,
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
// Collect methods and instance variables, which are based on the inheritance chain of the `self` type (which may
|
|
498
|
+
// not match the immediate lexical scope)
|
|
499
|
+
collect_methods_and_ivars_from_self(graph, self_decl, &mut context, &mut candidates);
|
|
500
|
+
|
|
501
|
+
// Keywords are always available in expression contexts
|
|
502
|
+
candidates.extend(keywords::KEYWORDS.iter().map(CompletionCandidate::Keyword));
|
|
503
|
+
Ok(candidates)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/// Collects constants reachable from the innermost lexical scope's ancestor chain. Module bodies also fall back to
|
|
507
|
+
/// `Object`'s ancestor chain to mirror Ruby's resolution rules.
|
|
508
|
+
fn collect_constants_from_lexical_scope<'a>(
|
|
509
|
+
graph: &'a Graph,
|
|
510
|
+
innermost_lexical_decl: &'a Namespace,
|
|
511
|
+
context: &mut CompletionContext<'a>,
|
|
512
|
+
candidates: &mut Vec<CompletionCandidate>,
|
|
513
|
+
) {
|
|
514
|
+
for ancestor in innermost_lexical_decl.ancestors() {
|
|
515
|
+
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
516
|
+
collect_members(
|
|
517
|
+
graph,
|
|
518
|
+
*ancestor_id,
|
|
519
|
+
|d| d.as_namespace().is_some() || d.as_constant().is_some() || d.as_constant_alias().is_some(),
|
|
520
|
+
|_| true,
|
|
521
|
+
context,
|
|
522
|
+
candidates,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if matches!(innermost_lexical_decl, Namespace::Module(_)) {
|
|
528
|
+
let object = graph.declarations().get(&OBJECT_ID).unwrap().as_namespace().unwrap();
|
|
529
|
+
|
|
530
|
+
for ancestor in object.ancestors() {
|
|
531
|
+
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
532
|
+
collect_members(
|
|
533
|
+
graph,
|
|
534
|
+
*ancestor_id,
|
|
535
|
+
|d| d.as_namespace().is_some() || d.as_constant().is_some() || d.as_constant_alias().is_some(),
|
|
536
|
+
|_| true,
|
|
537
|
+
context,
|
|
538
|
+
candidates,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/// Collects class variables visible from the current lexical scope. Class variables are resolved
|
|
546
|
+
/// lexically and singleton classes are skipped: inside `class Bar; class << Foo; @@cvar` the
|
|
547
|
+
/// cvar belongs to `Bar`, not `Foo`. We walk the lexical chain (innermost outward) until we find
|
|
548
|
+
/// a non-singleton namespace, then walk that namespace's ancestors.
|
|
549
|
+
fn collect_class_variables_from_lexical_scope<'a>(
|
|
550
|
+
graph: &'a Graph,
|
|
551
|
+
name_ref: &crate::model::name::ResolvedName,
|
|
552
|
+
context: &mut CompletionContext<'a>,
|
|
553
|
+
candidates: &mut Vec<CompletionCandidate>,
|
|
554
|
+
) {
|
|
555
|
+
let mut decl = graph
|
|
556
|
+
.declarations()
|
|
557
|
+
.get(name_ref.declaration_id())
|
|
558
|
+
.unwrap()
|
|
559
|
+
.as_namespace()
|
|
560
|
+
.unwrap();
|
|
561
|
+
let mut current_name_id = *name_ref.nesting();
|
|
385
562
|
|
|
386
|
-
|
|
563
|
+
while matches!(decl, Namespace::SingletonClass(_)) {
|
|
564
|
+
let Some(parent_name_id) = current_name_id else {
|
|
565
|
+
// No non-singleton lexical scope (invalid Ruby). Skip cvar collection.
|
|
566
|
+
return;
|
|
567
|
+
};
|
|
568
|
+
let NameRef::Resolved(parent_ref) = graph.names().get(&parent_name_id).unwrap() else {
|
|
569
|
+
return;
|
|
570
|
+
};
|
|
571
|
+
decl = graph
|
|
387
572
|
.declarations()
|
|
388
|
-
.get(
|
|
573
|
+
.get(parent_ref.declaration_id())
|
|
389
574
|
.unwrap()
|
|
390
575
|
.as_namespace()
|
|
391
576
|
.unwrap();
|
|
577
|
+
current_name_id = *parent_ref.nesting();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
for ancestor in decl.ancestors() {
|
|
581
|
+
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
582
|
+
collect_members(
|
|
583
|
+
graph,
|
|
584
|
+
*ancestor_id,
|
|
585
|
+
|d| d.as_class_variable().is_some(),
|
|
586
|
+
|_| true,
|
|
587
|
+
context,
|
|
588
|
+
candidates,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/// Walks the outer lexical nesting chain (excluding the innermost scope) to collect constants reachable through
|
|
595
|
+
/// enclosing classes/modules.
|
|
596
|
+
fn collect_constants_from_outer_nesting<'a>(
|
|
597
|
+
graph: &'a Graph,
|
|
598
|
+
name_ref: &crate::model::name::ResolvedName,
|
|
599
|
+
context: &mut CompletionContext<'a>,
|
|
600
|
+
candidates: &mut Vec<CompletionCandidate>,
|
|
601
|
+
) {
|
|
602
|
+
let mut current_name_id = *name_ref.nesting();
|
|
603
|
+
|
|
604
|
+
while let Some(id) = current_name_id {
|
|
605
|
+
let NameRef::Resolved(parent_ref) = graph.names().get(&id).unwrap() else {
|
|
606
|
+
break;
|
|
607
|
+
};
|
|
392
608
|
|
|
393
|
-
|
|
609
|
+
collect_members(
|
|
394
610
|
graph,
|
|
395
|
-
|
|
611
|
+
*parent_ref.declaration_id(),
|
|
612
|
+
|d| d.as_namespace().is_some() || d.as_constant().is_some() || d.as_constant_alias().is_some(),
|
|
613
|
+
|_| true,
|
|
396
614
|
context,
|
|
397
615
|
candidates,
|
|
398
|
-
Declaration::Namespace(_) | Declaration::Constant(_) | Declaration::ConstantAlias(_)
|
|
399
616
|
);
|
|
400
617
|
|
|
401
|
-
current_name_id = *
|
|
618
|
+
current_name_id = *parent_ref.nesting();
|
|
402
619
|
}
|
|
620
|
+
}
|
|
403
621
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
| Declaration::Constant(_)
|
|
413
|
-
| Declaration::ConstantAlias(_)
|
|
414
|
-
| Declaration::GlobalVariable(_)
|
|
415
|
-
);
|
|
416
|
-
|
|
417
|
-
// Walk ancestors collecting all applicable completion members
|
|
622
|
+
/// Collects methods and instance variables along `self`'s ancestor chain. The chain may differ
|
|
623
|
+
/// from the lexical chain when `self` was rebound (e.g., `def Foo.baz` written inside `class Bar`).
|
|
624
|
+
fn collect_methods_and_ivars_from_self<'a>(
|
|
625
|
+
graph: &'a Graph,
|
|
626
|
+
self_decl: &'a Namespace,
|
|
627
|
+
context: &mut CompletionContext<'a>,
|
|
628
|
+
candidates: &mut Vec<CompletionCandidate>,
|
|
629
|
+
) {
|
|
418
630
|
for ancestor in self_decl.ancestors() {
|
|
419
631
|
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
420
|
-
|
|
421
|
-
collect_candidates!(&ancestor_decl, context, candidates);
|
|
422
|
-
|
|
423
|
-
// Collect class variables from the attached object, which are available at any singleton class level
|
|
424
|
-
// within self
|
|
425
|
-
let attached_object = graph.attached_object(ancestor_decl);
|
|
426
|
-
collect_candidates!(
|
|
632
|
+
collect_members(
|
|
427
633
|
graph,
|
|
428
|
-
|
|
634
|
+
*ancestor_id,
|
|
635
|
+
|d| d.as_method().is_some() || d.as_instance_variable().is_some(),
|
|
636
|
+
|_| true,
|
|
429
637
|
context,
|
|
430
638
|
candidates,
|
|
431
|
-
Declaration::ClassVariable(_)
|
|
432
639
|
);
|
|
433
640
|
}
|
|
434
641
|
}
|
|
435
|
-
|
|
436
|
-
// Keywords are always available in expression contexts
|
|
437
|
-
candidates.extend(keywords::KEYWORDS.iter().map(CompletionCandidate::Keyword));
|
|
438
|
-
Ok(candidates)
|
|
439
642
|
}
|
|
440
643
|
|
|
441
644
|
/// Collect completion for a method argument (e.g.: `foo.bar(|)`)
|
|
442
645
|
fn method_argument_completion<'a>(
|
|
443
646
|
graph: &'a Graph,
|
|
444
|
-
|
|
647
|
+
self_decl_id: Option<DeclarationId>,
|
|
648
|
+
nesting_name_id: NameId,
|
|
445
649
|
method_decl_id: DeclarationId,
|
|
446
650
|
context: CompletionContext<'a>,
|
|
447
651
|
) -> Result<Vec<CompletionCandidate>, Box<dyn Error>> {
|
|
448
|
-
let mut candidates = expression_completion(graph,
|
|
652
|
+
let mut candidates = expression_completion(graph, self_decl_id, nesting_name_id, context)?;
|
|
449
653
|
let Some(method_decl) = graph.declarations().get(&method_decl_id) else {
|
|
450
654
|
return Ok(candidates);
|
|
451
655
|
};
|
|
@@ -523,30 +727,37 @@ mod tests {
|
|
|
523
727
|
|
|
524
728
|
macro_rules! assert_completion_eq {
|
|
525
729
|
($context:expr, $receiver:expr, $expected:expr) => {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
);
|
|
730
|
+
let mut actual: Vec<String> = completion_candidates($context.graph(), CompletionContext::new($receiver))
|
|
731
|
+
.unwrap()
|
|
732
|
+
.iter()
|
|
733
|
+
.map(|candidate| candidate_label(&$context, candidate))
|
|
734
|
+
.collect();
|
|
735
|
+
actual.sort();
|
|
736
|
+
|
|
737
|
+
let mut expected: Vec<String> = $expected.into_iter().map(String::from).collect();
|
|
738
|
+
expected.sort();
|
|
739
|
+
|
|
740
|
+
assert_eq!(expected, actual);
|
|
534
741
|
};
|
|
535
742
|
}
|
|
536
743
|
|
|
537
744
|
/// Asserts declaration and keyword argument completion candidates, excluding language keywords.
|
|
538
745
|
/// Language keywords are always present in expression contexts and tested separately.
|
|
746
|
+
/// Both sides are sorted before comparison so tests are not coupled to candidate emission order.
|
|
539
747
|
macro_rules! assert_declaration_completion_eq {
|
|
540
748
|
($context:expr, $receiver:expr, $expected:expr) => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
);
|
|
749
|
+
let mut actual: Vec<String> = completion_candidates($context.graph(), CompletionContext::new($receiver))
|
|
750
|
+
.unwrap()
|
|
751
|
+
.iter()
|
|
752
|
+
.filter(|c| !matches!(c, CompletionCandidate::Keyword(_)))
|
|
753
|
+
.map(|candidate| candidate_label(&$context, candidate))
|
|
754
|
+
.collect();
|
|
755
|
+
actual.sort();
|
|
756
|
+
|
|
757
|
+
let mut expected: Vec<String> = $expected.into_iter().map(String::from).collect();
|
|
758
|
+
expected.sort();
|
|
759
|
+
|
|
760
|
+
assert_eq!(expected, actual);
|
|
550
761
|
};
|
|
551
762
|
}
|
|
552
763
|
|
|
@@ -761,8 +972,12 @@ mod tests {
|
|
|
761
972
|
let name_id = Name::new(StringId::from("Child"), ParentScope::None, None).id();
|
|
762
973
|
assert_declaration_completion_eq!(
|
|
763
974
|
context,
|
|
764
|
-
CompletionReceiver::Expression
|
|
975
|
+
CompletionReceiver::Expression {
|
|
976
|
+
self_decl_id: None,
|
|
977
|
+
nesting_name_id: name_id,
|
|
978
|
+
},
|
|
765
979
|
[
|
|
980
|
+
"Foo::CONST",
|
|
766
981
|
"Class",
|
|
767
982
|
"BasicObject",
|
|
768
983
|
"Child",
|
|
@@ -772,7 +987,6 @@ mod tests {
|
|
|
772
987
|
"Foo",
|
|
773
988
|
"Object",
|
|
774
989
|
"Child#baz()",
|
|
775
|
-
"Foo::CONST",
|
|
776
990
|
"Foo#bar()",
|
|
777
991
|
"Parent#initialize()",
|
|
778
992
|
"Parent#@var"
|
|
@@ -807,7 +1021,10 @@ mod tests {
|
|
|
807
1021
|
let name_id = Name::new(StringId::from("Child"), ParentScope::None, None).id();
|
|
808
1022
|
assert_declaration_completion_eq!(
|
|
809
1023
|
context,
|
|
810
|
-
CompletionReceiver::Expression
|
|
1024
|
+
CompletionReceiver::Expression {
|
|
1025
|
+
self_decl_id: None,
|
|
1026
|
+
nesting_name_id: name_id,
|
|
1027
|
+
},
|
|
811
1028
|
[
|
|
812
1029
|
"Class",
|
|
813
1030
|
"BasicObject",
|
|
@@ -853,7 +1070,10 @@ mod tests {
|
|
|
853
1070
|
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
854
1071
|
assert_declaration_completion_eq!(
|
|
855
1072
|
context,
|
|
856
|
-
CompletionReceiver::Expression
|
|
1073
|
+
CompletionReceiver::Expression {
|
|
1074
|
+
self_decl_id: None,
|
|
1075
|
+
nesting_name_id: name_id,
|
|
1076
|
+
},
|
|
857
1077
|
[
|
|
858
1078
|
"Foo",
|
|
859
1079
|
"Class",
|
|
@@ -900,7 +1120,10 @@ mod tests {
|
|
|
900
1120
|
let name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_id), Some(foo_id)).id();
|
|
901
1121
|
assert_declaration_completion_eq!(
|
|
902
1122
|
context,
|
|
903
|
-
CompletionReceiver::Expression
|
|
1123
|
+
CompletionReceiver::Expression {
|
|
1124
|
+
self_decl_id: None,
|
|
1125
|
+
nesting_name_id: name_id,
|
|
1126
|
+
},
|
|
904
1127
|
[
|
|
905
1128
|
"Module",
|
|
906
1129
|
"Class",
|
|
@@ -917,7 +1140,10 @@ mod tests {
|
|
|
917
1140
|
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
918
1141
|
assert_declaration_completion_eq!(
|
|
919
1142
|
context,
|
|
920
|
-
CompletionReceiver::Expression
|
|
1143
|
+
CompletionReceiver::Expression {
|
|
1144
|
+
self_decl_id: None,
|
|
1145
|
+
nesting_name_id: name_id,
|
|
1146
|
+
},
|
|
921
1147
|
[
|
|
922
1148
|
"Module",
|
|
923
1149
|
"Class",
|
|
@@ -966,9 +1192,11 @@ mod tests {
|
|
|
966
1192
|
.id();
|
|
967
1193
|
assert_declaration_completion_eq!(
|
|
968
1194
|
context,
|
|
969
|
-
CompletionReceiver::Expression
|
|
1195
|
+
CompletionReceiver::Expression {
|
|
1196
|
+
self_decl_id: None,
|
|
1197
|
+
nesting_name_id: name_id,
|
|
1198
|
+
},
|
|
970
1199
|
[
|
|
971
|
-
"Foo::CONST_A",
|
|
972
1200
|
"Module",
|
|
973
1201
|
"Class",
|
|
974
1202
|
"Object",
|
|
@@ -976,6 +1204,7 @@ mod tests {
|
|
|
976
1204
|
"Kernel",
|
|
977
1205
|
"Foo",
|
|
978
1206
|
"Bar",
|
|
1207
|
+
"Foo::CONST_A",
|
|
979
1208
|
"Bar#bar_m()",
|
|
980
1209
|
"Bar#bar_m2()"
|
|
981
1210
|
]
|
|
@@ -984,7 +1213,10 @@ mod tests {
|
|
|
984
1213
|
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
985
1214
|
assert_declaration_completion_eq!(
|
|
986
1215
|
context,
|
|
987
|
-
CompletionReceiver::Expression
|
|
1216
|
+
CompletionReceiver::Expression {
|
|
1217
|
+
self_decl_id: None,
|
|
1218
|
+
nesting_name_id: name_id,
|
|
1219
|
+
},
|
|
988
1220
|
[
|
|
989
1221
|
"Module",
|
|
990
1222
|
"Class",
|
|
@@ -1025,16 +1257,19 @@ mod tests {
|
|
|
1025
1257
|
// when the user types the unqualified name CONST
|
|
1026
1258
|
assert_declaration_completion_eq!(
|
|
1027
1259
|
context,
|
|
1028
|
-
CompletionReceiver::Expression
|
|
1260
|
+
CompletionReceiver::Expression {
|
|
1261
|
+
self_decl_id: None,
|
|
1262
|
+
nesting_name_id: name_id,
|
|
1263
|
+
},
|
|
1029
1264
|
[
|
|
1030
|
-
"Foo::CONST",
|
|
1031
|
-
"Foo::Bar",
|
|
1032
1265
|
"Class",
|
|
1033
1266
|
"Object",
|
|
1034
1267
|
"BasicObject",
|
|
1035
1268
|
"Kernel",
|
|
1036
1269
|
"Foo",
|
|
1037
1270
|
"Module",
|
|
1271
|
+
"Foo::CONST",
|
|
1272
|
+
"Foo::Bar",
|
|
1038
1273
|
"Foo::Bar#baz()"
|
|
1039
1274
|
]
|
|
1040
1275
|
);
|
|
@@ -1069,19 +1304,11 @@ mod tests {
|
|
|
1069
1304
|
.id();
|
|
1070
1305
|
assert_declaration_completion_eq!(
|
|
1071
1306
|
context,
|
|
1072
|
-
CompletionReceiver::Expression
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
"BasicObject",
|
|
1078
|
-
"Object",
|
|
1079
|
-
"Kernel",
|
|
1080
|
-
"Module",
|
|
1081
|
-
"Foo",
|
|
1082
|
-
"Class",
|
|
1083
|
-
"Foo::Bar#bar_m()"
|
|
1084
|
-
]
|
|
1307
|
+
CompletionReceiver::Expression {
|
|
1308
|
+
self_decl_id: None,
|
|
1309
|
+
nesting_name_id: name_id,
|
|
1310
|
+
},
|
|
1311
|
+
["Foo::Bar", "$var2", "$var", "Foo::Bar#bar_m()"]
|
|
1085
1312
|
);
|
|
1086
1313
|
}
|
|
1087
1314
|
|
|
@@ -1108,7 +1335,10 @@ mod tests {
|
|
|
1108
1335
|
|
|
1109
1336
|
assert_completion_eq!(
|
|
1110
1337
|
context,
|
|
1111
|
-
CompletionReceiver::NamespaceAccess
|
|
1338
|
+
CompletionReceiver::NamespaceAccess {
|
|
1339
|
+
self_decl_id: None,
|
|
1340
|
+
namespace_decl_id: DeclarationId::from("Foo")
|
|
1341
|
+
},
|
|
1112
1342
|
["Foo::CONST", "Foo::Bar", "Foo::<Foo>#class_method()"]
|
|
1113
1343
|
);
|
|
1114
1344
|
}
|
|
@@ -1141,7 +1371,10 @@ mod tests {
|
|
|
1141
1371
|
|
|
1142
1372
|
assert_completion_eq!(
|
|
1143
1373
|
context,
|
|
1144
|
-
CompletionReceiver::NamespaceAccess
|
|
1374
|
+
CompletionReceiver::NamespaceAccess {
|
|
1375
|
+
self_decl_id: None,
|
|
1376
|
+
namespace_decl_id: DeclarationId::from("Child")
|
|
1377
|
+
},
|
|
1145
1378
|
[
|
|
1146
1379
|
"Child::CHILD_CONST",
|
|
1147
1380
|
"Parent::PARENT_CONST",
|
|
@@ -1179,7 +1412,10 @@ mod tests {
|
|
|
1179
1412
|
|
|
1180
1413
|
assert_completion_eq!(
|
|
1181
1414
|
context,
|
|
1182
|
-
CompletionReceiver::NamespaceAccess
|
|
1415
|
+
CompletionReceiver::NamespaceAccess {
|
|
1416
|
+
self_decl_id: None,
|
|
1417
|
+
namespace_decl_id: DeclarationId::from("Child")
|
|
1418
|
+
},
|
|
1183
1419
|
["Child::CONST", "Child::<Child>#shared_method()"]
|
|
1184
1420
|
);
|
|
1185
1421
|
}
|
|
@@ -1202,7 +1438,10 @@ mod tests {
|
|
|
1202
1438
|
|
|
1203
1439
|
assert_completion_eq!(
|
|
1204
1440
|
context,
|
|
1205
|
-
CompletionReceiver::NamespaceAccess
|
|
1441
|
+
CompletionReceiver::NamespaceAccess {
|
|
1442
|
+
self_decl_id: None,
|
|
1443
|
+
namespace_decl_id: DeclarationId::from("Foo")
|
|
1444
|
+
},
|
|
1206
1445
|
["Foo::CONST"]
|
|
1207
1446
|
);
|
|
1208
1447
|
}
|
|
@@ -1224,7 +1463,10 @@ mod tests {
|
|
|
1224
1463
|
|
|
1225
1464
|
assert_completion_eq!(
|
|
1226
1465
|
context,
|
|
1227
|
-
CompletionReceiver::NamespaceAccess
|
|
1466
|
+
CompletionReceiver::NamespaceAccess {
|
|
1467
|
+
self_decl_id: None,
|
|
1468
|
+
namespace_decl_id: DeclarationId::from("Foo")
|
|
1469
|
+
},
|
|
1228
1470
|
["Foo::CONST", "Foo::Bar"]
|
|
1229
1471
|
);
|
|
1230
1472
|
}
|
|
@@ -1254,7 +1496,10 @@ mod tests {
|
|
|
1254
1496
|
|
|
1255
1497
|
assert_completion_eq!(
|
|
1256
1498
|
context,
|
|
1257
|
-
CompletionReceiver::NamespaceAccess
|
|
1499
|
+
CompletionReceiver::NamespaceAccess {
|
|
1500
|
+
self_decl_id: None,
|
|
1501
|
+
namespace_decl_id: DeclarationId::from("Foo::MyOriginal")
|
|
1502
|
+
},
|
|
1258
1503
|
[
|
|
1259
1504
|
"Original::CONST",
|
|
1260
1505
|
"Original::Nested",
|
|
@@ -1286,7 +1531,10 @@ mod tests {
|
|
|
1286
1531
|
|
|
1287
1532
|
assert_completion_eq!(
|
|
1288
1533
|
context,
|
|
1289
|
-
CompletionReceiver::NamespaceAccess
|
|
1534
|
+
CompletionReceiver::NamespaceAccess {
|
|
1535
|
+
self_decl_id: None,
|
|
1536
|
+
namespace_decl_id: DeclarationId::from("Alias2")
|
|
1537
|
+
},
|
|
1290
1538
|
["Original::CONST", "Original::<Original>#class_method()"]
|
|
1291
1539
|
);
|
|
1292
1540
|
}
|
|
@@ -1313,7 +1561,10 @@ mod tests {
|
|
|
1313
1561
|
|
|
1314
1562
|
assert_completion_eq!(
|
|
1315
1563
|
context,
|
|
1316
|
-
CompletionReceiver::NamespaceAccess
|
|
1564
|
+
CompletionReceiver::NamespaceAccess {
|
|
1565
|
+
self_decl_id: None,
|
|
1566
|
+
namespace_decl_id: DeclarationId::from("Foo")
|
|
1567
|
+
},
|
|
1317
1568
|
["Foo::CONST", "Foo::<Foo>#class_method()"]
|
|
1318
1569
|
);
|
|
1319
1570
|
}
|
|
@@ -1347,7 +1598,10 @@ mod tests {
|
|
|
1347
1598
|
|
|
1348
1599
|
assert_completion_eq!(
|
|
1349
1600
|
context,
|
|
1350
|
-
CompletionReceiver::NamespaceAccess
|
|
1601
|
+
CompletionReceiver::NamespaceAccess {
|
|
1602
|
+
self_decl_id: None,
|
|
1603
|
+
namespace_decl_id: DeclarationId::from("Foo")
|
|
1604
|
+
},
|
|
1351
1605
|
["Foo::FOO_CONST", "Bar::CONST", "Foo::<Foo>#foo_class_method()"]
|
|
1352
1606
|
);
|
|
1353
1607
|
}
|
|
@@ -1375,7 +1629,10 @@ mod tests {
|
|
|
1375
1629
|
|
|
1376
1630
|
assert_completion_eq!(
|
|
1377
1631
|
context,
|
|
1378
|
-
CompletionReceiver::MethodCall
|
|
1632
|
+
CompletionReceiver::MethodCall {
|
|
1633
|
+
self_decl_id: None,
|
|
1634
|
+
receiver_decl_id: DeclarationId::from("Foo")
|
|
1635
|
+
},
|
|
1379
1636
|
["Foo#baz()", "Foo#bar()"]
|
|
1380
1637
|
);
|
|
1381
1638
|
}
|
|
@@ -1405,7 +1662,10 @@ mod tests {
|
|
|
1405
1662
|
|
|
1406
1663
|
assert_completion_eq!(
|
|
1407
1664
|
context,
|
|
1408
|
-
CompletionReceiver::MethodCall
|
|
1665
|
+
CompletionReceiver::MethodCall {
|
|
1666
|
+
self_decl_id: None,
|
|
1667
|
+
receiver_decl_id: DeclarationId::from("Foo::MyOriginal")
|
|
1668
|
+
},
|
|
1409
1669
|
["Original#baz()", "Original#bar()"]
|
|
1410
1670
|
);
|
|
1411
1671
|
}
|
|
@@ -1430,7 +1690,10 @@ mod tests {
|
|
|
1430
1690
|
|
|
1431
1691
|
assert_completion_eq!(
|
|
1432
1692
|
context,
|
|
1433
|
-
CompletionReceiver::MethodCall
|
|
1693
|
+
CompletionReceiver::MethodCall {
|
|
1694
|
+
self_decl_id: None,
|
|
1695
|
+
receiver_decl_id: DeclarationId::from("Child")
|
|
1696
|
+
},
|
|
1434
1697
|
["Child#child_method()", "Parent#parent_method()"]
|
|
1435
1698
|
);
|
|
1436
1699
|
}
|
|
@@ -1457,7 +1720,10 @@ mod tests {
|
|
|
1457
1720
|
|
|
1458
1721
|
assert_completion_eq!(
|
|
1459
1722
|
context,
|
|
1460
|
-
CompletionReceiver::MethodCall
|
|
1723
|
+
CompletionReceiver::MethodCall {
|
|
1724
|
+
self_decl_id: None,
|
|
1725
|
+
receiver_decl_id: DeclarationId::from("Foo")
|
|
1726
|
+
},
|
|
1461
1727
|
["Foo#foo_method()", "Mixin#mixin_method()"]
|
|
1462
1728
|
);
|
|
1463
1729
|
}
|
|
@@ -1484,7 +1750,10 @@ mod tests {
|
|
|
1484
1750
|
|
|
1485
1751
|
assert_completion_eq!(
|
|
1486
1752
|
context,
|
|
1487
|
-
CompletionReceiver::MethodCall
|
|
1753
|
+
CompletionReceiver::MethodCall {
|
|
1754
|
+
self_decl_id: None,
|
|
1755
|
+
receiver_decl_id: DeclarationId::from("Child")
|
|
1756
|
+
},
|
|
1488
1757
|
["Child#shared_method()", "Child#child_only()", "Parent#parent_only()"]
|
|
1489
1758
|
);
|
|
1490
1759
|
}
|
|
@@ -1512,7 +1781,10 @@ mod tests {
|
|
|
1512
1781
|
|
|
1513
1782
|
assert_completion_eq!(
|
|
1514
1783
|
context,
|
|
1515
|
-
CompletionReceiver::MethodCall
|
|
1784
|
+
CompletionReceiver::MethodCall {
|
|
1785
|
+
self_decl_id: None,
|
|
1786
|
+
receiver_decl_id: DeclarationId::from("Foo")
|
|
1787
|
+
},
|
|
1516
1788
|
["Foo#initialize()", "Foo#bar()"]
|
|
1517
1789
|
);
|
|
1518
1790
|
}
|
|
@@ -1537,7 +1809,10 @@ mod tests {
|
|
|
1537
1809
|
|
|
1538
1810
|
assert_completion_eq!(
|
|
1539
1811
|
context,
|
|
1540
|
-
CompletionReceiver::MethodCall
|
|
1812
|
+
CompletionReceiver::MethodCall {
|
|
1813
|
+
self_decl_id: None,
|
|
1814
|
+
receiver_decl_id: DeclarationId::from("Foo::<Foo>")
|
|
1815
|
+
},
|
|
1541
1816
|
["Foo::<Foo>#baz()", "Foo::<Foo>#bar()"]
|
|
1542
1817
|
);
|
|
1543
1818
|
}
|
|
@@ -1560,7 +1835,8 @@ mod tests {
|
|
|
1560
1835
|
assert_declaration_completion_eq!(
|
|
1561
1836
|
context,
|
|
1562
1837
|
CompletionReceiver::MethodArgument {
|
|
1563
|
-
|
|
1838
|
+
self_decl_id: None,
|
|
1839
|
+
nesting_name_id: name_id,
|
|
1564
1840
|
method_decl_id: DeclarationId::from("Foo#greet()"),
|
|
1565
1841
|
},
|
|
1566
1842
|
[
|
|
@@ -1595,7 +1871,8 @@ mod tests {
|
|
|
1595
1871
|
assert_declaration_completion_eq!(
|
|
1596
1872
|
context,
|
|
1597
1873
|
CompletionReceiver::MethodArgument {
|
|
1598
|
-
|
|
1874
|
+
self_decl_id: None,
|
|
1875
|
+
nesting_name_id: name_id,
|
|
1599
1876
|
method_decl_id: DeclarationId::from("Foo#bar()"),
|
|
1600
1877
|
},
|
|
1601
1878
|
["Class", "Object", "BasicObject", "Kernel", "Foo", "Module", "Foo#bar()"]
|
|
@@ -1620,7 +1897,8 @@ mod tests {
|
|
|
1620
1897
|
assert_declaration_completion_eq!(
|
|
1621
1898
|
context,
|
|
1622
1899
|
CompletionReceiver::MethodArgument {
|
|
1623
|
-
|
|
1900
|
+
self_decl_id: None,
|
|
1901
|
+
nesting_name_id: name_id,
|
|
1624
1902
|
method_decl_id: DeclarationId::from("Foo#search()"),
|
|
1625
1903
|
},
|
|
1626
1904
|
// Only RequiredKeyword and OptionalKeyword, not RestKeyword (**opts)
|
|
@@ -1663,7 +1941,8 @@ mod tests {
|
|
|
1663
1941
|
assert_declaration_completion_eq!(
|
|
1664
1942
|
context,
|
|
1665
1943
|
CompletionReceiver::MethodArgument {
|
|
1666
|
-
|
|
1944
|
+
self_decl_id: None,
|
|
1945
|
+
nesting_name_id: name_id,
|
|
1667
1946
|
method_decl_id: DeclarationId::from("Foo#bar()"),
|
|
1668
1947
|
},
|
|
1669
1948
|
[
|
|
@@ -1689,7 +1968,10 @@ mod tests {
|
|
|
1689
1968
|
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
1690
1969
|
assert_completion_eq!(
|
|
1691
1970
|
context,
|
|
1692
|
-
CompletionReceiver::Expression
|
|
1971
|
+
CompletionReceiver::Expression {
|
|
1972
|
+
self_decl_id: None,
|
|
1973
|
+
nesting_name_id: name_id,
|
|
1974
|
+
},
|
|
1693
1975
|
[
|
|
1694
1976
|
"Class",
|
|
1695
1977
|
"Object",
|
|
@@ -1752,7 +2034,8 @@ mod tests {
|
|
|
1752
2034
|
assert_completion_eq!(
|
|
1753
2035
|
context,
|
|
1754
2036
|
CompletionReceiver::MethodArgument {
|
|
1755
|
-
|
|
2037
|
+
self_decl_id: None,
|
|
2038
|
+
nesting_name_id: name_id,
|
|
1756
2039
|
method_decl_id: DeclarationId::from("Foo#bar()"),
|
|
1757
2040
|
},
|
|
1758
2041
|
[
|
|
@@ -1817,7 +2100,10 @@ mod tests {
|
|
|
1817
2100
|
|
|
1818
2101
|
let candidates = completion_candidates(
|
|
1819
2102
|
context.graph(),
|
|
1820
|
-
CompletionContext::new(CompletionReceiver::NamespaceAccess
|
|
2103
|
+
CompletionContext::new(CompletionReceiver::NamespaceAccess {
|
|
2104
|
+
self_decl_id: None,
|
|
2105
|
+
namespace_decl_id: DeclarationId::from("Foo"),
|
|
2106
|
+
}),
|
|
1821
2107
|
)
|
|
1822
2108
|
.unwrap();
|
|
1823
2109
|
|
|
@@ -1832,10 +2118,1040 @@ mod tests {
|
|
|
1832
2118
|
|
|
1833
2119
|
let candidates = completion_candidates(
|
|
1834
2120
|
context.graph(),
|
|
1835
|
-
CompletionContext::new(CompletionReceiver::MethodCall
|
|
2121
|
+
CompletionContext::new(CompletionReceiver::MethodCall {
|
|
2122
|
+
self_decl_id: None,
|
|
2123
|
+
receiver_decl_id: DeclarationId::from("Foo"),
|
|
2124
|
+
}),
|
|
1836
2125
|
)
|
|
1837
2126
|
.unwrap();
|
|
1838
2127
|
|
|
1839
2128
|
assert!(!candidates.iter().any(|c| matches!(c, CompletionCandidate::Keyword(_))));
|
|
1840
2129
|
}
|
|
2130
|
+
|
|
2131
|
+
#[test]
|
|
2132
|
+
fn expression_completion_class_variables_follow_lexical_scope() {
|
|
2133
|
+
// `@@cvar` in Ruby is resolved via the innermost lexical class/module's ancestor chain,
|
|
2134
|
+
// NOT `self`'s ancestor chain. Inside `def Foo.bar` written inside `Outer`, the lexical
|
|
2135
|
+
// scope is `[Outer]`, so `@@outer_cvar` is reachable and `@@foo_cvar` (which lives on
|
|
2136
|
+
// `Foo`, not on any lexical ancestor) must not be offered.
|
|
2137
|
+
let mut context = GraphTest::new();
|
|
2138
|
+
|
|
2139
|
+
context.index_uri(
|
|
2140
|
+
"file:///foo.rb",
|
|
2141
|
+
"
|
|
2142
|
+
module Outer
|
|
2143
|
+
@@outer_cvar = 1
|
|
2144
|
+
|
|
2145
|
+
class Foo
|
|
2146
|
+
@@foo_cvar = 2
|
|
2147
|
+
def self.singleton_m; end
|
|
2148
|
+
end
|
|
2149
|
+
end
|
|
2150
|
+
",
|
|
2151
|
+
);
|
|
2152
|
+
context.resolve();
|
|
2153
|
+
|
|
2154
|
+
let outer_name_id = Name::new(StringId::from("Outer"), ParentScope::None, None).id();
|
|
2155
|
+
assert_declaration_completion_eq!(
|
|
2156
|
+
context,
|
|
2157
|
+
CompletionReceiver::Expression {
|
|
2158
|
+
self_decl_id: Some(DeclarationId::from("Outer::Foo::<Foo>")),
|
|
2159
|
+
nesting_name_id: outer_name_id,
|
|
2160
|
+
},
|
|
2161
|
+
[
|
|
2162
|
+
"Module",
|
|
2163
|
+
"Class",
|
|
2164
|
+
"Object",
|
|
2165
|
+
"BasicObject",
|
|
2166
|
+
"Kernel",
|
|
2167
|
+
"Outer",
|
|
2168
|
+
"Outer::Foo",
|
|
2169
|
+
"Outer#@@outer_cvar",
|
|
2170
|
+
"Outer::Foo::<Foo>#singleton_m()"
|
|
2171
|
+
]
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
#[test]
|
|
2176
|
+
fn expression_completion_follows_self_decl_alias() {
|
|
2177
|
+
let mut context = GraphTest::new();
|
|
2178
|
+
|
|
2179
|
+
context.index_uri(
|
|
2180
|
+
"file:///foo.rb",
|
|
2181
|
+
"
|
|
2182
|
+
module Outer
|
|
2183
|
+
class Original
|
|
2184
|
+
def original_m; end
|
|
2185
|
+
end
|
|
2186
|
+
|
|
2187
|
+
MyAlias = Original
|
|
2188
|
+
end
|
|
2189
|
+
",
|
|
2190
|
+
);
|
|
2191
|
+
context.resolve();
|
|
2192
|
+
|
|
2193
|
+
let name_id = Name::new(StringId::from("Outer"), ParentScope::None, None).id();
|
|
2194
|
+
// `self_decl_id` points to the alias `Outer::MyAlias`, which is a `ConstantAlias` rather than a `Namespace`.
|
|
2195
|
+
// The completion should still collect members from the aliased namespace (`Outer::Original`) instead of
|
|
2196
|
+
// returning an error, so callers do not have to unwrap aliases themselves.
|
|
2197
|
+
assert_declaration_completion_eq!(
|
|
2198
|
+
context,
|
|
2199
|
+
CompletionReceiver::Expression {
|
|
2200
|
+
self_decl_id: Some(DeclarationId::from("Outer::MyAlias")),
|
|
2201
|
+
nesting_name_id: name_id,
|
|
2202
|
+
},
|
|
2203
|
+
[
|
|
2204
|
+
"Outer::MyAlias",
|
|
2205
|
+
"Outer::Original",
|
|
2206
|
+
"Class",
|
|
2207
|
+
"Object",
|
|
2208
|
+
"BasicObject",
|
|
2209
|
+
"Outer",
|
|
2210
|
+
"Kernel",
|
|
2211
|
+
"Module",
|
|
2212
|
+
"Outer::Original#original_m()"
|
|
2213
|
+
]
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
#[test]
|
|
2218
|
+
fn expression_completion_in_method_definition_with_receiver_uses_lexical_scope_for_class_variables() {
|
|
2219
|
+
let mut context = GraphTest::new();
|
|
2220
|
+
|
|
2221
|
+
context.index_uri(
|
|
2222
|
+
"file:///foo.rb",
|
|
2223
|
+
"
|
|
2224
|
+
class Foo
|
|
2225
|
+
@@class_var = 1
|
|
2226
|
+
end
|
|
2227
|
+
|
|
2228
|
+
class Bar
|
|
2229
|
+
@@other_class_var = 2
|
|
2230
|
+
|
|
2231
|
+
def Foo.baz
|
|
2232
|
+
# completion here
|
|
2233
|
+
end
|
|
2234
|
+
end
|
|
2235
|
+
",
|
|
2236
|
+
);
|
|
2237
|
+
context.resolve();
|
|
2238
|
+
|
|
2239
|
+
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2240
|
+
assert_declaration_completion_eq!(
|
|
2241
|
+
context,
|
|
2242
|
+
CompletionReceiver::Expression {
|
|
2243
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>")),
|
|
2244
|
+
nesting_name_id: name_id,
|
|
2245
|
+
},
|
|
2246
|
+
[
|
|
2247
|
+
"Module",
|
|
2248
|
+
"Class",
|
|
2249
|
+
"Object",
|
|
2250
|
+
"BasicObject",
|
|
2251
|
+
"Kernel",
|
|
2252
|
+
"Foo",
|
|
2253
|
+
"Bar",
|
|
2254
|
+
"Foo::<Foo>#baz()",
|
|
2255
|
+
"Bar#@@other_class_var"
|
|
2256
|
+
]
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
#[test]
|
|
2261
|
+
fn expression_completion_in_method_definition_with_receiver_uses_lexical_scope_for_constants() {
|
|
2262
|
+
let mut context = GraphTest::new();
|
|
2263
|
+
|
|
2264
|
+
context.index_uri(
|
|
2265
|
+
"file:///foo.rb",
|
|
2266
|
+
"
|
|
2267
|
+
class Foo
|
|
2268
|
+
CONST = 1
|
|
2269
|
+
end
|
|
2270
|
+
|
|
2271
|
+
class Bar
|
|
2272
|
+
OTHER_CONST = 2
|
|
2273
|
+
|
|
2274
|
+
def Foo.baz
|
|
2275
|
+
# completion here
|
|
2276
|
+
end
|
|
2277
|
+
end
|
|
2278
|
+
",
|
|
2279
|
+
);
|
|
2280
|
+
context.resolve();
|
|
2281
|
+
|
|
2282
|
+
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2283
|
+
assert_declaration_completion_eq!(
|
|
2284
|
+
context,
|
|
2285
|
+
CompletionReceiver::Expression {
|
|
2286
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>")),
|
|
2287
|
+
nesting_name_id: name_id,
|
|
2288
|
+
},
|
|
2289
|
+
[
|
|
2290
|
+
"Bar::OTHER_CONST",
|
|
2291
|
+
"Module",
|
|
2292
|
+
"Class",
|
|
2293
|
+
"Object",
|
|
2294
|
+
"BasicObject",
|
|
2295
|
+
"Kernel",
|
|
2296
|
+
"Foo",
|
|
2297
|
+
"Bar",
|
|
2298
|
+
"Foo::<Foo>#baz()"
|
|
2299
|
+
]
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
#[test]
|
|
2304
|
+
fn expression_completion_does_not_leak_constants_reachable_only_through_self_ancestors() {
|
|
2305
|
+
let mut context = GraphTest::new();
|
|
2306
|
+
|
|
2307
|
+
context.index_uri(
|
|
2308
|
+
"file:///foo.rb",
|
|
2309
|
+
"
|
|
2310
|
+
module Mixin
|
|
2311
|
+
MIXIN_CONST = 1
|
|
2312
|
+
end
|
|
2313
|
+
|
|
2314
|
+
class Foo
|
|
2315
|
+
extend Mixin
|
|
2316
|
+
end
|
|
2317
|
+
|
|
2318
|
+
class Bar
|
|
2319
|
+
def Foo.baz
|
|
2320
|
+
# completion here
|
|
2321
|
+
end
|
|
2322
|
+
end
|
|
2323
|
+
",
|
|
2324
|
+
);
|
|
2325
|
+
context.resolve();
|
|
2326
|
+
|
|
2327
|
+
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2328
|
+
assert_declaration_completion_eq!(
|
|
2329
|
+
context,
|
|
2330
|
+
CompletionReceiver::Expression {
|
|
2331
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>")),
|
|
2332
|
+
nesting_name_id: name_id,
|
|
2333
|
+
},
|
|
2334
|
+
[
|
|
2335
|
+
"Class",
|
|
2336
|
+
"BasicObject",
|
|
2337
|
+
"Mixin",
|
|
2338
|
+
"Object",
|
|
2339
|
+
"Kernel",
|
|
2340
|
+
"Module",
|
|
2341
|
+
"Foo",
|
|
2342
|
+
"Bar",
|
|
2343
|
+
"Foo::<Foo>#baz()"
|
|
2344
|
+
]
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
#[test]
|
|
2349
|
+
fn expression_completion_at_top_level_includes_object_constants() {
|
|
2350
|
+
let mut context = GraphTest::new();
|
|
2351
|
+
|
|
2352
|
+
context.index_uri(
|
|
2353
|
+
"file:///foo.rb",
|
|
2354
|
+
"
|
|
2355
|
+
class Foo
|
|
2356
|
+
CONST = 1
|
|
2357
|
+
end
|
|
2358
|
+
|
|
2359
|
+
TOP_CONST = 2
|
|
2360
|
+
",
|
|
2361
|
+
);
|
|
2362
|
+
context.resolve();
|
|
2363
|
+
|
|
2364
|
+
let name_id = Name::new(StringId::from("Object"), ParentScope::None, None).id();
|
|
2365
|
+
assert_declaration_completion_eq!(
|
|
2366
|
+
context,
|
|
2367
|
+
CompletionReceiver::Expression {
|
|
2368
|
+
self_decl_id: None,
|
|
2369
|
+
nesting_name_id: name_id,
|
|
2370
|
+
},
|
|
2371
|
+
["Module", "Class", "Object", "BasicObject", "Kernel", "Foo", "TOP_CONST"]
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
#[test]
|
|
2376
|
+
fn expression_completion_in_module_body_falls_back_to_object_constants() {
|
|
2377
|
+
let mut context = GraphTest::new();
|
|
2378
|
+
|
|
2379
|
+
context.index_uri(
|
|
2380
|
+
"file:///foo.rb",
|
|
2381
|
+
"
|
|
2382
|
+
TOP_CONST = 1
|
|
2383
|
+
|
|
2384
|
+
module Mod
|
|
2385
|
+
MOD_CONST = 2
|
|
2386
|
+
end
|
|
2387
|
+
",
|
|
2388
|
+
);
|
|
2389
|
+
context.resolve();
|
|
2390
|
+
|
|
2391
|
+
let name_id = Name::new(StringId::from("Mod"), ParentScope::None, None).id();
|
|
2392
|
+
assert_declaration_completion_eq!(
|
|
2393
|
+
context,
|
|
2394
|
+
CompletionReceiver::Expression {
|
|
2395
|
+
self_decl_id: None,
|
|
2396
|
+
nesting_name_id: name_id,
|
|
2397
|
+
},
|
|
2398
|
+
[
|
|
2399
|
+
"Mod::MOD_CONST",
|
|
2400
|
+
"Module",
|
|
2401
|
+
"Class",
|
|
2402
|
+
"Object",
|
|
2403
|
+
"BasicObject",
|
|
2404
|
+
"Kernel",
|
|
2405
|
+
"TOP_CONST",
|
|
2406
|
+
"Mod"
|
|
2407
|
+
]
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
#[test]
|
|
2412
|
+
fn expression_completion_in_module_body_falls_back_to_object_ancestors() {
|
|
2413
|
+
let mut context = GraphTest::new();
|
|
2414
|
+
|
|
2415
|
+
context.index_uri(
|
|
2416
|
+
"file:///foo.rb",
|
|
2417
|
+
"
|
|
2418
|
+
module Kernel
|
|
2419
|
+
CONST = 1
|
|
2420
|
+
end
|
|
2421
|
+
|
|
2422
|
+
module Mod
|
|
2423
|
+
# completion here
|
|
2424
|
+
end
|
|
2425
|
+
",
|
|
2426
|
+
);
|
|
2427
|
+
context.resolve();
|
|
2428
|
+
|
|
2429
|
+
let name_id = Name::new(StringId::from("Mod"), ParentScope::None, None).id();
|
|
2430
|
+
assert_declaration_completion_eq!(
|
|
2431
|
+
context,
|
|
2432
|
+
CompletionReceiver::Expression {
|
|
2433
|
+
self_decl_id: None,
|
|
2434
|
+
nesting_name_id: name_id,
|
|
2435
|
+
},
|
|
2436
|
+
[
|
|
2437
|
+
"Class",
|
|
2438
|
+
"Object",
|
|
2439
|
+
"Kernel",
|
|
2440
|
+
"BasicObject",
|
|
2441
|
+
"Mod",
|
|
2442
|
+
"Module",
|
|
2443
|
+
"Kernel::CONST",
|
|
2444
|
+
]
|
|
2445
|
+
);
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
#[test]
|
|
2449
|
+
fn expression_completion_at_top_level_offers_methods_from_object_chain() {
|
|
2450
|
+
let mut context = GraphTest::new();
|
|
2451
|
+
|
|
2452
|
+
context.index_uri(
|
|
2453
|
+
"file:///foo.rb",
|
|
2454
|
+
"
|
|
2455
|
+
def my_top_method; end
|
|
2456
|
+
|
|
2457
|
+
module Kernel
|
|
2458
|
+
def kernel_helper; end
|
|
2459
|
+
end
|
|
2460
|
+
",
|
|
2461
|
+
);
|
|
2462
|
+
context.resolve();
|
|
2463
|
+
|
|
2464
|
+
let name_id = Name::new(StringId::from("Object"), ParentScope::None, None).id();
|
|
2465
|
+
assert_declaration_completion_eq!(
|
|
2466
|
+
context,
|
|
2467
|
+
CompletionReceiver::Expression {
|
|
2468
|
+
self_decl_id: None,
|
|
2469
|
+
nesting_name_id: name_id,
|
|
2470
|
+
},
|
|
2471
|
+
[
|
|
2472
|
+
"Object",
|
|
2473
|
+
"Kernel",
|
|
2474
|
+
"BasicObject",
|
|
2475
|
+
"Module",
|
|
2476
|
+
"Class",
|
|
2477
|
+
"Object#my_top_method()",
|
|
2478
|
+
"Kernel#kernel_helper()"
|
|
2479
|
+
]
|
|
2480
|
+
);
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
#[test]
|
|
2484
|
+
fn expression_completion_in_basic_object_subclass_excludes_object_constants() {
|
|
2485
|
+
let mut context = GraphTest::new();
|
|
2486
|
+
|
|
2487
|
+
context.index_uri(
|
|
2488
|
+
"file:///foo.rb",
|
|
2489
|
+
"
|
|
2490
|
+
TOP_CONST = 1
|
|
2491
|
+
|
|
2492
|
+
class Bar < BasicObject
|
|
2493
|
+
BAR_CONST = 2
|
|
2494
|
+
end
|
|
2495
|
+
",
|
|
2496
|
+
);
|
|
2497
|
+
context.resolve();
|
|
2498
|
+
|
|
2499
|
+
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2500
|
+
assert_declaration_completion_eq!(
|
|
2501
|
+
context,
|
|
2502
|
+
CompletionReceiver::Expression {
|
|
2503
|
+
self_decl_id: None,
|
|
2504
|
+
nesting_name_id: name_id,
|
|
2505
|
+
},
|
|
2506
|
+
["Bar::BAR_CONST"]
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
#[test]
|
|
2511
|
+
fn expression_completion_class_variables_in_singleton_class_block_use_outer_lexical_scope() {
|
|
2512
|
+
let mut context = GraphTest::new();
|
|
2513
|
+
|
|
2514
|
+
context.index_uri(
|
|
2515
|
+
"file:///foo.rb",
|
|
2516
|
+
"
|
|
2517
|
+
class Foo
|
|
2518
|
+
@@foo_cvar = 1
|
|
2519
|
+
end
|
|
2520
|
+
|
|
2521
|
+
class Bar
|
|
2522
|
+
@@bar_cvar = 2
|
|
2523
|
+
|
|
2524
|
+
class << Foo
|
|
2525
|
+
# completion here
|
|
2526
|
+
end
|
|
2527
|
+
end
|
|
2528
|
+
",
|
|
2529
|
+
);
|
|
2530
|
+
context.resolve();
|
|
2531
|
+
|
|
2532
|
+
let bar_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2533
|
+
let foo_ref_id = Name::new(StringId::from("Foo"), ParentScope::None, Some(bar_id)).id();
|
|
2534
|
+
let nesting_name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_ref_id), Some(bar_id)).id();
|
|
2535
|
+
|
|
2536
|
+
assert_declaration_completion_eq!(
|
|
2537
|
+
context,
|
|
2538
|
+
CompletionReceiver::Expression {
|
|
2539
|
+
self_decl_id: None,
|
|
2540
|
+
nesting_name_id,
|
|
2541
|
+
},
|
|
2542
|
+
["Bar#@@bar_cvar"]
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
#[test]
|
|
2547
|
+
fn expression_completion_in_def_self_method_inside_module_body() {
|
|
2548
|
+
let mut context = GraphTest::new();
|
|
2549
|
+
|
|
2550
|
+
context.index_uri(
|
|
2551
|
+
"file:///foo.rb",
|
|
2552
|
+
"
|
|
2553
|
+
module Mod
|
|
2554
|
+
MOD_CONST = 1
|
|
2555
|
+
|
|
2556
|
+
def self.helper
|
|
2557
|
+
# completion here
|
|
2558
|
+
end
|
|
2559
|
+
end
|
|
2560
|
+
",
|
|
2561
|
+
);
|
|
2562
|
+
context.resolve();
|
|
2563
|
+
|
|
2564
|
+
let name_id = Name::new(StringId::from("Mod"), ParentScope::None, None).id();
|
|
2565
|
+
assert_declaration_completion_eq!(
|
|
2566
|
+
context,
|
|
2567
|
+
CompletionReceiver::Expression {
|
|
2568
|
+
self_decl_id: Some(DeclarationId::from("Mod::<Mod>")),
|
|
2569
|
+
nesting_name_id: name_id,
|
|
2570
|
+
},
|
|
2571
|
+
[
|
|
2572
|
+
"Mod::MOD_CONST",
|
|
2573
|
+
"Module",
|
|
2574
|
+
"Class",
|
|
2575
|
+
"Object",
|
|
2576
|
+
"BasicObject",
|
|
2577
|
+
"Kernel",
|
|
2578
|
+
"Mod",
|
|
2579
|
+
"Mod::<Mod>#helper()"
|
|
2580
|
+
]
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
#[test]
|
|
2585
|
+
fn expression_completion_in_singleton_class_block_at_top_level() {
|
|
2586
|
+
let mut context = GraphTest::new();
|
|
2587
|
+
|
|
2588
|
+
context.index_uri(
|
|
2589
|
+
"file:///foo.rb",
|
|
2590
|
+
"
|
|
2591
|
+
class Foo
|
|
2592
|
+
@@foo_cvar = 1
|
|
2593
|
+
end
|
|
2594
|
+
|
|
2595
|
+
class << Foo
|
|
2596
|
+
# completion here
|
|
2597
|
+
end
|
|
2598
|
+
",
|
|
2599
|
+
);
|
|
2600
|
+
context.resolve();
|
|
2601
|
+
|
|
2602
|
+
let foo_ref_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2603
|
+
let nesting_name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_ref_id), None).id();
|
|
2604
|
+
|
|
2605
|
+
assert_declaration_completion_eq!(
|
|
2606
|
+
context,
|
|
2607
|
+
CompletionReceiver::Expression {
|
|
2608
|
+
self_decl_id: None,
|
|
2609
|
+
nesting_name_id,
|
|
2610
|
+
},
|
|
2611
|
+
["Module", "Class", "Object", "BasicObject", "Kernel", "Foo"]
|
|
2612
|
+
);
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
#[test]
|
|
2616
|
+
fn expression_completion_errors_when_self_decl_id_does_not_exist() {
|
|
2617
|
+
let mut context = GraphTest::new();
|
|
2618
|
+
|
|
2619
|
+
context.index_uri(
|
|
2620
|
+
"file:///foo.rb",
|
|
2621
|
+
"
|
|
2622
|
+
class Foo
|
|
2623
|
+
end
|
|
2624
|
+
",
|
|
2625
|
+
);
|
|
2626
|
+
context.resolve();
|
|
2627
|
+
|
|
2628
|
+
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2629
|
+
let result = completion_candidates(
|
|
2630
|
+
context.graph(),
|
|
2631
|
+
CompletionContext::new(CompletionReceiver::Expression {
|
|
2632
|
+
self_decl_id: Some(DeclarationId::from("Nonexistent")),
|
|
2633
|
+
nesting_name_id: name_id,
|
|
2634
|
+
}),
|
|
2635
|
+
);
|
|
2636
|
+
|
|
2637
|
+
assert!(result.is_err(), "missing self_decl_id should surface as an error");
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
#[test]
|
|
2641
|
+
fn expression_completion_errors_when_self_decl_id_is_not_a_namespace() {
|
|
2642
|
+
let mut context = GraphTest::new();
|
|
2643
|
+
|
|
2644
|
+
context.index_uri(
|
|
2645
|
+
"file:///foo.rb",
|
|
2646
|
+
"
|
|
2647
|
+
class Foo
|
|
2648
|
+
CONST = 1
|
|
2649
|
+
end
|
|
2650
|
+
",
|
|
2651
|
+
);
|
|
2652
|
+
context.resolve();
|
|
2653
|
+
|
|
2654
|
+
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2655
|
+
let result = completion_candidates(
|
|
2656
|
+
context.graph(),
|
|
2657
|
+
CompletionContext::new(CompletionReceiver::Expression {
|
|
2658
|
+
// CONST resolves to a `Constant`, not a `Namespace` and not an alias.
|
|
2659
|
+
self_decl_id: Some(DeclarationId::from("Foo::CONST")),
|
|
2660
|
+
nesting_name_id: name_id,
|
|
2661
|
+
}),
|
|
2662
|
+
);
|
|
2663
|
+
|
|
2664
|
+
assert!(result.is_err(), "non-namespace self_decl_id should surface as an error");
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
#[test]
|
|
2668
|
+
fn expression_completion_follows_chained_self_decl_alias() {
|
|
2669
|
+
let mut context = GraphTest::new();
|
|
2670
|
+
|
|
2671
|
+
context.index_uri(
|
|
2672
|
+
"file:///foo.rb",
|
|
2673
|
+
"
|
|
2674
|
+
module Outer
|
|
2675
|
+
class Original
|
|
2676
|
+
def original_m; end
|
|
2677
|
+
end
|
|
2678
|
+
|
|
2679
|
+
FirstAlias = Original
|
|
2680
|
+
SecondAlias = FirstAlias
|
|
2681
|
+
end
|
|
2682
|
+
",
|
|
2683
|
+
);
|
|
2684
|
+
context.resolve();
|
|
2685
|
+
|
|
2686
|
+
let name_id = Name::new(StringId::from("Outer"), ParentScope::None, None).id();
|
|
2687
|
+
assert_declaration_completion_eq!(
|
|
2688
|
+
context,
|
|
2689
|
+
CompletionReceiver::Expression {
|
|
2690
|
+
self_decl_id: Some(DeclarationId::from("Outer::SecondAlias")),
|
|
2691
|
+
nesting_name_id: name_id,
|
|
2692
|
+
},
|
|
2693
|
+
[
|
|
2694
|
+
"Outer::FirstAlias",
|
|
2695
|
+
"Outer::SecondAlias",
|
|
2696
|
+
"Outer::Original",
|
|
2697
|
+
"Module",
|
|
2698
|
+
"Class",
|
|
2699
|
+
"Object",
|
|
2700
|
+
"BasicObject",
|
|
2701
|
+
"Outer",
|
|
2702
|
+
"Kernel",
|
|
2703
|
+
"Outer::Original#original_m()"
|
|
2704
|
+
]
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
#[test]
|
|
2709
|
+
fn expression_completion_includes_private_singleton_method_when_self_matches_owner() {
|
|
2710
|
+
let mut context = GraphTest::new();
|
|
2711
|
+
|
|
2712
|
+
context.index_uri(
|
|
2713
|
+
"file:///foo.rb",
|
|
2714
|
+
"
|
|
2715
|
+
class Foo
|
|
2716
|
+
def self.bar; end
|
|
2717
|
+
private_class_method :bar
|
|
2718
|
+
end
|
|
2719
|
+
|
|
2720
|
+
class Bar
|
|
2721
|
+
def Foo.baz
|
|
2722
|
+
# completion here
|
|
2723
|
+
end
|
|
2724
|
+
end
|
|
2725
|
+
",
|
|
2726
|
+
);
|
|
2727
|
+
context.resolve();
|
|
2728
|
+
|
|
2729
|
+
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2730
|
+
assert_declaration_completion_eq!(
|
|
2731
|
+
context,
|
|
2732
|
+
CompletionReceiver::Expression {
|
|
2733
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>")),
|
|
2734
|
+
nesting_name_id: name_id,
|
|
2735
|
+
},
|
|
2736
|
+
[
|
|
2737
|
+
"Module",
|
|
2738
|
+
"Class",
|
|
2739
|
+
"Object",
|
|
2740
|
+
"BasicObject",
|
|
2741
|
+
"Kernel",
|
|
2742
|
+
"Foo",
|
|
2743
|
+
"Bar",
|
|
2744
|
+
"Foo::<Foo>#bar()",
|
|
2745
|
+
"Foo::<Foo>#baz()"
|
|
2746
|
+
]
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
#[test]
|
|
2751
|
+
fn method_call_completion_excludes_private_method_for_external_call() {
|
|
2752
|
+
let mut context = GraphTest::new();
|
|
2753
|
+
|
|
2754
|
+
context.index_uri(
|
|
2755
|
+
"file:///foo.rb",
|
|
2756
|
+
"
|
|
2757
|
+
class Foo
|
|
2758
|
+
private
|
|
2759
|
+
|
|
2760
|
+
def bar; end
|
|
2761
|
+
end
|
|
2762
|
+
",
|
|
2763
|
+
);
|
|
2764
|
+
context.resolve();
|
|
2765
|
+
|
|
2766
|
+
assert_completion_eq!(
|
|
2767
|
+
context,
|
|
2768
|
+
CompletionReceiver::MethodCall {
|
|
2769
|
+
self_decl_id: None,
|
|
2770
|
+
receiver_decl_id: DeclarationId::from("Foo")
|
|
2771
|
+
},
|
|
2772
|
+
[] as [&str; 0]
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
#[test]
|
|
2777
|
+
fn expression_completion_includes_private_instance_method_inside_self_ancestor_chain() {
|
|
2778
|
+
let mut context = GraphTest::new();
|
|
2779
|
+
|
|
2780
|
+
context.index_uri(
|
|
2781
|
+
"file:///foo.rb",
|
|
2782
|
+
"
|
|
2783
|
+
class Foo
|
|
2784
|
+
def baz
|
|
2785
|
+
# completion here
|
|
2786
|
+
end
|
|
2787
|
+
|
|
2788
|
+
private
|
|
2789
|
+
|
|
2790
|
+
def bar; end
|
|
2791
|
+
end
|
|
2792
|
+
|
|
2793
|
+
class Bar < Foo
|
|
2794
|
+
def qux
|
|
2795
|
+
# completion here
|
|
2796
|
+
end
|
|
2797
|
+
end
|
|
2798
|
+
",
|
|
2799
|
+
);
|
|
2800
|
+
context.resolve();
|
|
2801
|
+
|
|
2802
|
+
let foo_name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2803
|
+
assert_declaration_completion_eq!(
|
|
2804
|
+
context,
|
|
2805
|
+
CompletionReceiver::Expression {
|
|
2806
|
+
self_decl_id: None,
|
|
2807
|
+
nesting_name_id: foo_name_id,
|
|
2808
|
+
},
|
|
2809
|
+
[
|
|
2810
|
+
"Module",
|
|
2811
|
+
"Class",
|
|
2812
|
+
"Object",
|
|
2813
|
+
"BasicObject",
|
|
2814
|
+
"Kernel",
|
|
2815
|
+
"Foo",
|
|
2816
|
+
"Bar",
|
|
2817
|
+
"Foo#baz()",
|
|
2818
|
+
"Foo#bar()"
|
|
2819
|
+
]
|
|
2820
|
+
);
|
|
2821
|
+
|
|
2822
|
+
let bar_name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2823
|
+
assert_declaration_completion_eq!(
|
|
2824
|
+
context,
|
|
2825
|
+
CompletionReceiver::Expression {
|
|
2826
|
+
self_decl_id: None,
|
|
2827
|
+
nesting_name_id: bar_name_id,
|
|
2828
|
+
},
|
|
2829
|
+
[
|
|
2830
|
+
"Module",
|
|
2831
|
+
"Class",
|
|
2832
|
+
"Object",
|
|
2833
|
+
"BasicObject",
|
|
2834
|
+
"Kernel",
|
|
2835
|
+
"Foo",
|
|
2836
|
+
"Bar",
|
|
2837
|
+
"Foo#baz()",
|
|
2838
|
+
"Foo#bar()",
|
|
2839
|
+
"Bar#qux()"
|
|
2840
|
+
]
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
#[test]
|
|
2845
|
+
fn method_call_completion_includes_protected_method_when_caller_shares_ancestor_with_receiver() {
|
|
2846
|
+
let mut context = GraphTest::new();
|
|
2847
|
+
|
|
2848
|
+
context.index_uri(
|
|
2849
|
+
"file:///foo.rb",
|
|
2850
|
+
"
|
|
2851
|
+
class Account
|
|
2852
|
+
protected
|
|
2853
|
+
|
|
2854
|
+
def balance; end
|
|
2855
|
+
end
|
|
2856
|
+
|
|
2857
|
+
class Savings < Account
|
|
2858
|
+
end
|
|
2859
|
+
|
|
2860
|
+
class Checking < Account
|
|
2861
|
+
end
|
|
2862
|
+
",
|
|
2863
|
+
);
|
|
2864
|
+
context.resolve();
|
|
2865
|
+
|
|
2866
|
+
// Caller's self is `Account` (or any descendant). Both caller and receiver descend from
|
|
2867
|
+
// the defining class, satisfying MRI's `caller.class <= defined_class && recv.class <= defined_class`.
|
|
2868
|
+
assert_completion_eq!(
|
|
2869
|
+
context,
|
|
2870
|
+
CompletionReceiver::MethodCall {
|
|
2871
|
+
self_decl_id: Some(DeclarationId::from("Account")),
|
|
2872
|
+
receiver_decl_id: DeclarationId::from("Savings"),
|
|
2873
|
+
},
|
|
2874
|
+
["Account#balance()"]
|
|
2875
|
+
);
|
|
2876
|
+
|
|
2877
|
+
assert_completion_eq!(
|
|
2878
|
+
context,
|
|
2879
|
+
CompletionReceiver::MethodCall {
|
|
2880
|
+
self_decl_id: Some(DeclarationId::from("Savings")),
|
|
2881
|
+
receiver_decl_id: DeclarationId::from("Checking"),
|
|
2882
|
+
},
|
|
2883
|
+
["Account#balance()"]
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
#[test]
|
|
2888
|
+
fn method_call_completion_excludes_protected_method_when_caller_does_not_share_ancestor() {
|
|
2889
|
+
let mut context = GraphTest::new();
|
|
2890
|
+
context.index_uri(
|
|
2891
|
+
"file:///foo.rb",
|
|
2892
|
+
"
|
|
2893
|
+
class Account
|
|
2894
|
+
protected
|
|
2895
|
+
|
|
2896
|
+
def balance; end
|
|
2897
|
+
end
|
|
2898
|
+
|
|
2899
|
+
class Unrelated
|
|
2900
|
+
end
|
|
2901
|
+
",
|
|
2902
|
+
);
|
|
2903
|
+
context.resolve();
|
|
2904
|
+
|
|
2905
|
+
// Receiver is `Account`; caller's self is `Unrelated`. Caller is not a descendant of the
|
|
2906
|
+
// defining class, so the protected check fails and `balance` is hidden.
|
|
2907
|
+
assert_completion_eq!(
|
|
2908
|
+
context,
|
|
2909
|
+
CompletionReceiver::MethodCall {
|
|
2910
|
+
self_decl_id: Some(DeclarationId::from("Unrelated")),
|
|
2911
|
+
receiver_decl_id: DeclarationId::from("Account"),
|
|
2912
|
+
},
|
|
2913
|
+
[] as [&str; 0]
|
|
2914
|
+
);
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
#[test]
|
|
2918
|
+
fn method_call_completion_includes_private_method_when_receiver_is_self() {
|
|
2919
|
+
let mut context = GraphTest::new();
|
|
2920
|
+
context.index_uri(
|
|
2921
|
+
"file:///foo.rb",
|
|
2922
|
+
"
|
|
2923
|
+
class Foo
|
|
2924
|
+
def public_method; end
|
|
2925
|
+
|
|
2926
|
+
private
|
|
2927
|
+
|
|
2928
|
+
def private_method; end
|
|
2929
|
+
end
|
|
2930
|
+
",
|
|
2931
|
+
);
|
|
2932
|
+
context.resolve();
|
|
2933
|
+
|
|
2934
|
+
// Caller's self is `Foo`, matching the receiver — `private_method` becomes visible
|
|
2935
|
+
// (Ruby 3.0+ allows `self.foo` for private methods, and our completion treats receiver-equals-self as the
|
|
2936
|
+
// implicit-receiver case).
|
|
2937
|
+
assert_completion_eq!(
|
|
2938
|
+
context,
|
|
2939
|
+
CompletionReceiver::MethodCall {
|
|
2940
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
2941
|
+
receiver_decl_id: DeclarationId::from("Foo"),
|
|
2942
|
+
},
|
|
2943
|
+
["Foo#public_method()", "Foo#private_method()"]
|
|
2944
|
+
);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
#[test]
|
|
2948
|
+
fn method_call_completion_includes_protected_method_through_included_module() {
|
|
2949
|
+
let mut context = GraphTest::new();
|
|
2950
|
+
context.index_uri(
|
|
2951
|
+
"file:///foo.rb",
|
|
2952
|
+
"
|
|
2953
|
+
module Sharable
|
|
2954
|
+
protected
|
|
2955
|
+
|
|
2956
|
+
def shared_secret; end
|
|
2957
|
+
end
|
|
2958
|
+
|
|
2959
|
+
class A
|
|
2960
|
+
include Sharable
|
|
2961
|
+
end
|
|
2962
|
+
|
|
2963
|
+
class B
|
|
2964
|
+
include Sharable
|
|
2965
|
+
end
|
|
2966
|
+
",
|
|
2967
|
+
);
|
|
2968
|
+
context.resolve();
|
|
2969
|
+
|
|
2970
|
+
assert_completion_eq!(
|
|
2971
|
+
context,
|
|
2972
|
+
CompletionReceiver::MethodCall {
|
|
2973
|
+
self_decl_id: Some(DeclarationId::from("A")),
|
|
2974
|
+
receiver_decl_id: DeclarationId::from("B"),
|
|
2975
|
+
},
|
|
2976
|
+
["Sharable#shared_secret()"]
|
|
2977
|
+
);
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
#[test]
|
|
2981
|
+
fn method_call_completion_excludes_protected_when_caller_class_is_not_descendant_of_defined_class() {
|
|
2982
|
+
let mut context = GraphTest::new();
|
|
2983
|
+
context.index_uri(
|
|
2984
|
+
"file:///foo.rb",
|
|
2985
|
+
"
|
|
2986
|
+
class Animal
|
|
2987
|
+
end
|
|
2988
|
+
|
|
2989
|
+
class Dog < Animal
|
|
2990
|
+
protected
|
|
2991
|
+
|
|
2992
|
+
def secret_trick; end
|
|
2993
|
+
end
|
|
2994
|
+
",
|
|
2995
|
+
);
|
|
2996
|
+
context.resolve();
|
|
2997
|
+
|
|
2998
|
+
assert_completion_eq!(
|
|
2999
|
+
context,
|
|
3000
|
+
CompletionReceiver::MethodCall {
|
|
3001
|
+
self_decl_id: Some(DeclarationId::from("Animal")),
|
|
3002
|
+
receiver_decl_id: DeclarationId::from("Dog"),
|
|
3003
|
+
},
|
|
3004
|
+
[] as [&str; 0]
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
#[test]
|
|
3009
|
+
fn method_call_completion_excludes_visibility_restricted_methods_at_top_level() {
|
|
3010
|
+
let mut context = GraphTest::new();
|
|
3011
|
+
context.index_uri(
|
|
3012
|
+
"file:///foo.rb",
|
|
3013
|
+
"
|
|
3014
|
+
class Foo
|
|
3015
|
+
def pub_inst; end
|
|
3016
|
+
|
|
3017
|
+
protected
|
|
3018
|
+
|
|
3019
|
+
def prot_inst; end
|
|
3020
|
+
|
|
3021
|
+
private
|
|
3022
|
+
|
|
3023
|
+
def priv_inst; end
|
|
3024
|
+
end
|
|
3025
|
+
",
|
|
3026
|
+
);
|
|
3027
|
+
context.resolve();
|
|
3028
|
+
|
|
3029
|
+
assert_completion_eq!(
|
|
3030
|
+
context,
|
|
3031
|
+
CompletionReceiver::MethodCall {
|
|
3032
|
+
self_decl_id: None,
|
|
3033
|
+
receiver_decl_id: DeclarationId::from("Foo"),
|
|
3034
|
+
},
|
|
3035
|
+
["Foo#pub_inst()"]
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
#[test]
|
|
3040
|
+
fn method_call_completion_hides_method_when_subclass_overrides_with_stricter_visibility() {
|
|
3041
|
+
let mut context = GraphTest::new();
|
|
3042
|
+
context.index_uri(
|
|
3043
|
+
"file:///foo.rb",
|
|
3044
|
+
"
|
|
3045
|
+
class Parent
|
|
3046
|
+
def foo; end
|
|
3047
|
+
end
|
|
3048
|
+
|
|
3049
|
+
class Child < Parent
|
|
3050
|
+
private
|
|
3051
|
+
|
|
3052
|
+
def foo; end
|
|
3053
|
+
end
|
|
3054
|
+
",
|
|
3055
|
+
);
|
|
3056
|
+
context.resolve();
|
|
3057
|
+
|
|
3058
|
+
assert_completion_eq!(
|
|
3059
|
+
context,
|
|
3060
|
+
CompletionReceiver::MethodCall {
|
|
3061
|
+
self_decl_id: None,
|
|
3062
|
+
receiver_decl_id: DeclarationId::from("Child"),
|
|
3063
|
+
},
|
|
3064
|
+
[] as [&str; 0]
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
#[test]
|
|
3069
|
+
fn namespace_access_completion_excludes_private_constant() {
|
|
3070
|
+
let mut context = GraphTest::new();
|
|
3071
|
+
context.index_uri(
|
|
3072
|
+
"file:///foo.rb",
|
|
3073
|
+
"
|
|
3074
|
+
class Foo
|
|
3075
|
+
PUB = 1
|
|
3076
|
+
PRIV = 2
|
|
3077
|
+
private_constant :PRIV
|
|
3078
|
+
end
|
|
3079
|
+
",
|
|
3080
|
+
);
|
|
3081
|
+
context.resolve();
|
|
3082
|
+
|
|
3083
|
+
assert_completion_eq!(
|
|
3084
|
+
context,
|
|
3085
|
+
CompletionReceiver::NamespaceAccess {
|
|
3086
|
+
self_decl_id: None,
|
|
3087
|
+
namespace_decl_id: DeclarationId::from("Foo"),
|
|
3088
|
+
},
|
|
3089
|
+
["Foo::PUB"]
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
#[test]
|
|
3094
|
+
fn namespace_access_completion_excludes_inherited_private_constant() {
|
|
3095
|
+
let mut context = GraphTest::new();
|
|
3096
|
+
context.index_uri(
|
|
3097
|
+
"file:///foo.rb",
|
|
3098
|
+
"
|
|
3099
|
+
class Parent
|
|
3100
|
+
SECRET = 1
|
|
3101
|
+
private_constant :SECRET
|
|
3102
|
+
end
|
|
3103
|
+
|
|
3104
|
+
class Child < Parent
|
|
3105
|
+
end
|
|
3106
|
+
",
|
|
3107
|
+
);
|
|
3108
|
+
context.resolve();
|
|
3109
|
+
|
|
3110
|
+
assert_completion_eq!(
|
|
3111
|
+
context,
|
|
3112
|
+
CompletionReceiver::NamespaceAccess {
|
|
3113
|
+
self_decl_id: None,
|
|
3114
|
+
namespace_decl_id: DeclarationId::from("Child"),
|
|
3115
|
+
},
|
|
3116
|
+
[] as [&str; 0]
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
#[test]
|
|
3121
|
+
fn expression_completion_includes_private_constant_within_lexical_scope() {
|
|
3122
|
+
let mut context = GraphTest::new();
|
|
3123
|
+
context.index_uri(
|
|
3124
|
+
"file:///foo.rb",
|
|
3125
|
+
"
|
|
3126
|
+
class Foo
|
|
3127
|
+
SECRET = 1
|
|
3128
|
+
private_constant :SECRET
|
|
3129
|
+
|
|
3130
|
+
def use_it
|
|
3131
|
+
# completion here
|
|
3132
|
+
end
|
|
3133
|
+
end
|
|
3134
|
+
",
|
|
3135
|
+
);
|
|
3136
|
+
context.resolve();
|
|
3137
|
+
|
|
3138
|
+
let foo_name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
3139
|
+
assert_declaration_completion_eq!(
|
|
3140
|
+
context,
|
|
3141
|
+
CompletionReceiver::Expression {
|
|
3142
|
+
self_decl_id: None,
|
|
3143
|
+
nesting_name_id: foo_name_id,
|
|
3144
|
+
},
|
|
3145
|
+
[
|
|
3146
|
+
"Module",
|
|
3147
|
+
"Class",
|
|
3148
|
+
"Object",
|
|
3149
|
+
"BasicObject",
|
|
3150
|
+
"Kernel",
|
|
3151
|
+
"Foo",
|
|
3152
|
+
"Foo::SECRET",
|
|
3153
|
+
"Foo#use_it()"
|
|
3154
|
+
]
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
1841
3157
|
}
|