method-ray 0.1.9 → 0.1.10
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/CHANGELOG.md +20 -0
- data/README.md +9 -11
- data/core/Cargo.toml +1 -1
- data/core/src/analyzer/assignments.rs +0 -280
- data/core/src/analyzer/blocks.rs +0 -190
- data/core/src/analyzer/calls.rs +3 -32
- data/core/src/analyzer/conditionals.rs +0 -348
- data/core/src/analyzer/definitions.rs +11 -526
- data/core/src/analyzer/dispatch.rs +54 -1000
- data/core/src/analyzer/exceptions.rs +0 -454
- data/core/src/analyzer/literals.rs +0 -54
- data/core/src/analyzer/loops.rs +0 -207
- data/core/src/analyzer/mod.rs +0 -15
- data/core/src/analyzer/operators.rs +0 -205
- data/core/src/analyzer/parameters.rs +4 -227
- data/core/src/analyzer/parentheses.rs +0 -88
- data/core/src/analyzer/returns.rs +0 -152
- data/core/src/analyzer/super_calls.rs +1 -212
- data/core/src/analyzer/variables.rs +5 -25
- data/core/src/checker.rs +0 -13
- data/core/src/diagnostics/diagnostic.rs +0 -41
- data/core/src/diagnostics/formatter.rs +0 -38
- data/core/src/env/box_manager.rs +0 -30
- data/core/src/env/global_env.rs +52 -79
- data/core/src/env/local_env.rs +0 -50
- data/core/src/env/method_registry.rs +52 -233
- data/core/src/env/scope.rs +0 -375
- data/core/src/env/vertex_manager.rs +0 -73
- data/core/src/graph/box.rs +20 -439
- data/core/src/graph/change_set.rs +0 -65
- data/core/src/graph/vertex.rs +0 -69
- data/core/src/parser.rs +0 -77
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- metadata +5 -4
|
@@ -27,6 +27,17 @@ pub(crate) fn process_class_node(
|
|
|
27
27
|
) -> Option<VertexId> {
|
|
28
28
|
let class_name = extract_class_name(class_node);
|
|
29
29
|
let superclass = class_node.superclass().and_then(|sup| extract_constant_path(&sup));
|
|
30
|
+
|
|
31
|
+
// Warn if superclass is a dynamic expression (not a constant path)
|
|
32
|
+
// TODO: Replace eprintln! with structured diagnostic (record_type_error or warning)
|
|
33
|
+
// so this is visible in LSP mode and includes source location.
|
|
34
|
+
if class_node.superclass().is_some() && superclass.is_none() {
|
|
35
|
+
eprintln!(
|
|
36
|
+
"[methodray] warning: dynamic superclass expression in class {}; inheritance will be ignored",
|
|
37
|
+
class_name
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
install_class(genv, class_name, superclass.as_deref());
|
|
31
42
|
|
|
32
43
|
if let Some(body) = class_node.body() {
|
|
@@ -192,529 +203,3 @@ pub(crate) fn extract_constant_path(node: &Node) -> Option<String> {
|
|
|
192
203
|
|
|
193
204
|
None
|
|
194
205
|
}
|
|
195
|
-
|
|
196
|
-
#[cfg(test)]
|
|
197
|
-
mod tests {
|
|
198
|
-
use super::*;
|
|
199
|
-
use crate::graph::ChangeSet;
|
|
200
|
-
use crate::parser::ParseSession;
|
|
201
|
-
use crate::types::Type;
|
|
202
|
-
|
|
203
|
-
#[test]
|
|
204
|
-
fn test_enter_exit_class_scope() {
|
|
205
|
-
let mut genv = GlobalEnv::new();
|
|
206
|
-
|
|
207
|
-
install_class(&mut genv, "User".to_string(), None);
|
|
208
|
-
assert_eq!(
|
|
209
|
-
genv.scope_manager.current_class_name(),
|
|
210
|
-
Some("User".to_string())
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
exit_scope(&mut genv);
|
|
214
|
-
assert_eq!(genv.scope_manager.current_class_name(), None);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
#[test]
|
|
218
|
-
fn test_enter_exit_module_scope() {
|
|
219
|
-
let mut genv = GlobalEnv::new();
|
|
220
|
-
|
|
221
|
-
install_module(&mut genv, "Utils".to_string());
|
|
222
|
-
assert_eq!(
|
|
223
|
-
genv.scope_manager.current_module_name(),
|
|
224
|
-
Some("Utils".to_string())
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
exit_scope(&mut genv);
|
|
228
|
-
assert_eq!(genv.scope_manager.current_module_name(), None);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
#[test]
|
|
232
|
-
fn test_nested_method_scope() {
|
|
233
|
-
let mut genv = GlobalEnv::new();
|
|
234
|
-
|
|
235
|
-
install_class(&mut genv, "User".to_string(), None);
|
|
236
|
-
install_method(&mut genv, "greet".to_string());
|
|
237
|
-
|
|
238
|
-
// Still in User class context
|
|
239
|
-
assert_eq!(
|
|
240
|
-
genv.scope_manager.current_class_name(),
|
|
241
|
-
Some("User".to_string())
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
exit_scope(&mut genv); // exit method
|
|
245
|
-
exit_scope(&mut genv); // exit class
|
|
246
|
-
|
|
247
|
-
assert_eq!(genv.scope_manager.current_class_name(), None);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
#[test]
|
|
251
|
-
fn test_method_in_module() {
|
|
252
|
-
let mut genv = GlobalEnv::new();
|
|
253
|
-
|
|
254
|
-
install_module(&mut genv, "Helpers".to_string());
|
|
255
|
-
install_method(&mut genv, "format".to_string());
|
|
256
|
-
|
|
257
|
-
// Should find module context from within method
|
|
258
|
-
assert_eq!(
|
|
259
|
-
genv.scope_manager.current_module_name(),
|
|
260
|
-
Some("Helpers".to_string())
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
exit_scope(&mut genv); // exit method
|
|
264
|
-
exit_scope(&mut genv); // exit module
|
|
265
|
-
|
|
266
|
-
assert_eq!(genv.scope_manager.current_module_name(), None);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
#[test]
|
|
270
|
-
fn test_extract_simple_class_name() {
|
|
271
|
-
let source = "class User; end";
|
|
272
|
-
let session = ParseSession::new();
|
|
273
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
274
|
-
let root = parse_result.node();
|
|
275
|
-
let program = root.as_program_node().unwrap();
|
|
276
|
-
let stmt = program.statements().body().first().unwrap();
|
|
277
|
-
let class_node = stmt.as_class_node().unwrap();
|
|
278
|
-
|
|
279
|
-
let name = extract_class_name(&class_node);
|
|
280
|
-
assert_eq!(name, "User");
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
#[test]
|
|
284
|
-
fn test_extract_qualified_class_name() {
|
|
285
|
-
let source = "class Api::User; end";
|
|
286
|
-
let session = ParseSession::new();
|
|
287
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
288
|
-
let root = parse_result.node();
|
|
289
|
-
let program = root.as_program_node().unwrap();
|
|
290
|
-
let stmt = program.statements().body().first().unwrap();
|
|
291
|
-
let class_node = stmt.as_class_node().unwrap();
|
|
292
|
-
|
|
293
|
-
let name = extract_class_name(&class_node);
|
|
294
|
-
assert_eq!(name, "Api::User");
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
#[test]
|
|
298
|
-
fn test_extract_deeply_qualified_class_name() {
|
|
299
|
-
let source = "class Api::V1::Admin::User; end";
|
|
300
|
-
let session = ParseSession::new();
|
|
301
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
302
|
-
let root = parse_result.node();
|
|
303
|
-
let program = root.as_program_node().unwrap();
|
|
304
|
-
let stmt = program.statements().body().first().unwrap();
|
|
305
|
-
let class_node = stmt.as_class_node().unwrap();
|
|
306
|
-
|
|
307
|
-
let name = extract_class_name(&class_node);
|
|
308
|
-
assert_eq!(name, "Api::V1::Admin::User");
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
#[test]
|
|
312
|
-
fn test_extract_simple_module_name() {
|
|
313
|
-
let source = "module Utils; end";
|
|
314
|
-
let session = ParseSession::new();
|
|
315
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
316
|
-
let root = parse_result.node();
|
|
317
|
-
let program = root.as_program_node().unwrap();
|
|
318
|
-
let stmt = program.statements().body().first().unwrap();
|
|
319
|
-
let module_node = stmt.as_module_node().unwrap();
|
|
320
|
-
|
|
321
|
-
let name = extract_module_name(&module_node);
|
|
322
|
-
assert_eq!(name, "Utils");
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
#[test]
|
|
326
|
-
fn test_extract_qualified_module_name() {
|
|
327
|
-
let source = "module Api::V1; end";
|
|
328
|
-
let session = ParseSession::new();
|
|
329
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
330
|
-
let root = parse_result.node();
|
|
331
|
-
let program = root.as_program_node().unwrap();
|
|
332
|
-
let stmt = program.statements().body().first().unwrap();
|
|
333
|
-
let module_node = stmt.as_module_node().unwrap();
|
|
334
|
-
|
|
335
|
-
let name = extract_module_name(&module_node);
|
|
336
|
-
assert_eq!(name, "Api::V1");
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
#[test]
|
|
340
|
-
fn test_process_def_node_registers_user_method() {
|
|
341
|
-
let source = "class User; def name; \"Alice\"; end; end";
|
|
342
|
-
let session = ParseSession::new();
|
|
343
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
344
|
-
let root = parse_result.node();
|
|
345
|
-
let program = root.as_program_node().unwrap();
|
|
346
|
-
|
|
347
|
-
let mut genv = GlobalEnv::new();
|
|
348
|
-
let mut lenv = LocalEnv::new();
|
|
349
|
-
let mut changes = ChangeSet::new();
|
|
350
|
-
|
|
351
|
-
let stmt = program.statements().body().first().unwrap();
|
|
352
|
-
let class_node = stmt.as_class_node().unwrap();
|
|
353
|
-
process_class_node(&mut genv, &mut lenv, &mut changes, source, &class_node);
|
|
354
|
-
|
|
355
|
-
// User#name should be registered as a user-defined method
|
|
356
|
-
let info = genv
|
|
357
|
-
.resolve_method(&Type::instance("User"), "name")
|
|
358
|
-
.expect("User#name should be registered");
|
|
359
|
-
assert!(info.return_vertex.is_some());
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
#[test]
|
|
363
|
-
fn test_qualified_name_method_registration() {
|
|
364
|
-
let source = r#"
|
|
365
|
-
module Api
|
|
366
|
-
module V1
|
|
367
|
-
class User
|
|
368
|
-
def name
|
|
369
|
-
"Alice"
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
end
|
|
374
|
-
"#;
|
|
375
|
-
let session = ParseSession::new();
|
|
376
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
377
|
-
let root = parse_result.node();
|
|
378
|
-
let program = root.as_program_node().unwrap();
|
|
379
|
-
|
|
380
|
-
let mut genv = GlobalEnv::new();
|
|
381
|
-
let mut lenv = LocalEnv::new();
|
|
382
|
-
let mut changes = ChangeSet::new();
|
|
383
|
-
|
|
384
|
-
for stmt in &program.statements().body() {
|
|
385
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Method should be registered with qualified name "Api::V1::User"
|
|
389
|
-
let info = genv
|
|
390
|
-
.resolve_method(&Type::instance("Api::V1::User"), "name")
|
|
391
|
-
.expect("Api::V1::User#name should be registered");
|
|
392
|
-
assert!(info.return_vertex.is_some());
|
|
393
|
-
|
|
394
|
-
// Should NOT be registered with simple name "User"
|
|
395
|
-
assert!(
|
|
396
|
-
genv.resolve_method(&Type::instance("User"), "name").is_none(),
|
|
397
|
-
"User#name should not exist — method should be registered under qualified name"
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
#[test]
|
|
402
|
-
fn test_same_class_name_different_namespace() {
|
|
403
|
-
let source = r#"
|
|
404
|
-
module Api
|
|
405
|
-
class User
|
|
406
|
-
def name
|
|
407
|
-
"api_user"
|
|
408
|
-
end
|
|
409
|
-
end
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
module Admin
|
|
413
|
-
class User
|
|
414
|
-
def name
|
|
415
|
-
"admin_user"
|
|
416
|
-
end
|
|
417
|
-
end
|
|
418
|
-
end
|
|
419
|
-
"#;
|
|
420
|
-
let session = ParseSession::new();
|
|
421
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
422
|
-
let root = parse_result.node();
|
|
423
|
-
let program = root.as_program_node().unwrap();
|
|
424
|
-
|
|
425
|
-
let mut genv = GlobalEnv::new();
|
|
426
|
-
let mut lenv = LocalEnv::new();
|
|
427
|
-
let mut changes = ChangeSet::new();
|
|
428
|
-
|
|
429
|
-
for stmt in &program.statements().body() {
|
|
430
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Both should be registered separately
|
|
434
|
-
let api_info = genv
|
|
435
|
-
.resolve_method(&Type::instance("Api::User"), "name")
|
|
436
|
-
.expect("Api::User#name should be registered");
|
|
437
|
-
assert!(api_info.return_vertex.is_some());
|
|
438
|
-
|
|
439
|
-
let admin_info = genv
|
|
440
|
-
.resolve_method(&Type::instance("Admin::User"), "name")
|
|
441
|
-
.expect("Admin::User#name should be registered");
|
|
442
|
-
assert!(admin_info.return_vertex.is_some());
|
|
443
|
-
|
|
444
|
-
// Simple "User" should not resolve
|
|
445
|
-
assert!(
|
|
446
|
-
genv.resolve_method(&Type::instance("User"), "name").is_none(),
|
|
447
|
-
"User#name should not exist — both are under qualified names"
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
#[test]
|
|
452
|
-
fn test_class_method_registration() {
|
|
453
|
-
let source = r#"
|
|
454
|
-
class User
|
|
455
|
-
def self.create
|
|
456
|
-
"created"
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
"#;
|
|
460
|
-
let session = ParseSession::new();
|
|
461
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
462
|
-
let root = parse_result.node();
|
|
463
|
-
let program = root.as_program_node().unwrap();
|
|
464
|
-
|
|
465
|
-
let mut genv = GlobalEnv::new();
|
|
466
|
-
let mut lenv = LocalEnv::new();
|
|
467
|
-
let mut changes = ChangeSet::new();
|
|
468
|
-
|
|
469
|
-
for stmt in &program.statements().body() {
|
|
470
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// def self.create should be registered as singleton method
|
|
474
|
-
let info = genv
|
|
475
|
-
.resolve_method(&Type::singleton("User"), "create")
|
|
476
|
-
.expect("User.create should be registered as singleton method");
|
|
477
|
-
assert!(info.return_vertex.is_some());
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
#[test]
|
|
481
|
-
fn test_class_method_with_params() {
|
|
482
|
-
let source = r#"
|
|
483
|
-
class User
|
|
484
|
-
def self.find(id)
|
|
485
|
-
"user"
|
|
486
|
-
end
|
|
487
|
-
end
|
|
488
|
-
"#;
|
|
489
|
-
let session = ParseSession::new();
|
|
490
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
491
|
-
let root = parse_result.node();
|
|
492
|
-
let program = root.as_program_node().unwrap();
|
|
493
|
-
|
|
494
|
-
let mut genv = GlobalEnv::new();
|
|
495
|
-
let mut lenv = LocalEnv::new();
|
|
496
|
-
let mut changes = ChangeSet::new();
|
|
497
|
-
|
|
498
|
-
for stmt in &program.statements().body() {
|
|
499
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
let info = genv
|
|
503
|
-
.resolve_method(&Type::singleton("User"), "find")
|
|
504
|
-
.expect("User.find should be registered");
|
|
505
|
-
assert!(info.return_vertex.is_some());
|
|
506
|
-
assert_eq!(info.param_vertices.as_ref().unwrap().len(), 1);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
#[test]
|
|
510
|
-
fn test_class_method_in_qualified_namespace() {
|
|
511
|
-
let source = r#"
|
|
512
|
-
module Api
|
|
513
|
-
class User
|
|
514
|
-
def self.create
|
|
515
|
-
"created"
|
|
516
|
-
end
|
|
517
|
-
end
|
|
518
|
-
end
|
|
519
|
-
"#;
|
|
520
|
-
let session = ParseSession::new();
|
|
521
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
522
|
-
let root = parse_result.node();
|
|
523
|
-
let program = root.as_program_node().unwrap();
|
|
524
|
-
|
|
525
|
-
let mut genv = GlobalEnv::new();
|
|
526
|
-
let mut lenv = LocalEnv::new();
|
|
527
|
-
let mut changes = ChangeSet::new();
|
|
528
|
-
|
|
529
|
-
for stmt in &program.statements().body() {
|
|
530
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
let info = genv
|
|
534
|
-
.resolve_method(&Type::singleton("Api::User"), "create")
|
|
535
|
-
.expect("Api::User.create should be registered");
|
|
536
|
-
assert!(info.return_vertex.is_some());
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
#[test]
|
|
540
|
-
fn test_class_method_not_registered_as_instance() {
|
|
541
|
-
let source = r#"
|
|
542
|
-
class User
|
|
543
|
-
def self.create
|
|
544
|
-
"created"
|
|
545
|
-
end
|
|
546
|
-
end
|
|
547
|
-
"#;
|
|
548
|
-
let session = ParseSession::new();
|
|
549
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
550
|
-
let root = parse_result.node();
|
|
551
|
-
let program = root.as_program_node().unwrap();
|
|
552
|
-
|
|
553
|
-
let mut genv = GlobalEnv::new();
|
|
554
|
-
let mut lenv = LocalEnv::new();
|
|
555
|
-
let mut changes = ChangeSet::new();
|
|
556
|
-
|
|
557
|
-
for stmt in &program.statements().body() {
|
|
558
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// def self.create should NOT be registered as instance method
|
|
562
|
-
assert!(
|
|
563
|
-
genv.resolve_method(&Type::instance("User"), "create").is_none(),
|
|
564
|
-
"User#create should not exist — it's a class method"
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
#[test]
|
|
569
|
-
fn test_non_self_receiver_not_treated_as_class_method() {
|
|
570
|
-
let source = r#"
|
|
571
|
-
class User
|
|
572
|
-
def other.foo
|
|
573
|
-
"test"
|
|
574
|
-
end
|
|
575
|
-
end
|
|
576
|
-
"#;
|
|
577
|
-
let session = ParseSession::new();
|
|
578
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
579
|
-
let root = parse_result.node();
|
|
580
|
-
let program = root.as_program_node().unwrap();
|
|
581
|
-
|
|
582
|
-
let mut genv = GlobalEnv::new();
|
|
583
|
-
let mut lenv = LocalEnv::new();
|
|
584
|
-
let mut changes = ChangeSet::new();
|
|
585
|
-
|
|
586
|
-
for stmt in &program.statements().body() {
|
|
587
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// def other.foo should NOT be registered as singleton method
|
|
591
|
-
assert!(
|
|
592
|
-
genv.resolve_method(&Type::singleton("User"), "foo").is_none(),
|
|
593
|
-
"User.foo should not exist — receiver is not self"
|
|
594
|
-
);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
#[test]
|
|
598
|
-
fn test_class_method_return_type_inference() {
|
|
599
|
-
let source = r#"
|
|
600
|
-
class User
|
|
601
|
-
def self.create
|
|
602
|
-
"created"
|
|
603
|
-
end
|
|
604
|
-
end
|
|
605
|
-
"#;
|
|
606
|
-
let session = ParseSession::new();
|
|
607
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
608
|
-
let root = parse_result.node();
|
|
609
|
-
let program = root.as_program_node().unwrap();
|
|
610
|
-
|
|
611
|
-
let mut genv = GlobalEnv::new();
|
|
612
|
-
let mut lenv = LocalEnv::new();
|
|
613
|
-
let mut changes = ChangeSet::new();
|
|
614
|
-
|
|
615
|
-
for stmt in &program.statements().body() {
|
|
616
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
let info = genv
|
|
620
|
-
.resolve_method(&Type::singleton("User"), "create")
|
|
621
|
-
.expect("User.create should be registered");
|
|
622
|
-
let ret_vtx = info.return_vertex.expect("should have return vertex");
|
|
623
|
-
|
|
624
|
-
// Run solver to propagate types
|
|
625
|
-
genv.apply_changes(changes);
|
|
626
|
-
genv.run_all();
|
|
627
|
-
|
|
628
|
-
let vertex = genv.get_vertex(ret_vtx).or_else(|| {
|
|
629
|
-
// return vertex might be a source
|
|
630
|
-
None
|
|
631
|
-
});
|
|
632
|
-
if let Some(v) = vertex {
|
|
633
|
-
assert_eq!(v.show(), "String");
|
|
634
|
-
} else {
|
|
635
|
-
// Check if it's a source
|
|
636
|
-
let src = genv.get_source(ret_vtx).expect("should have source or vertex");
|
|
637
|
-
assert_eq!(src.ty, Type::string());
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
#[test]
|
|
642
|
-
fn test_class_method_in_reopened_class() {
|
|
643
|
-
let source = r#"
|
|
644
|
-
class User
|
|
645
|
-
def self.create
|
|
646
|
-
"created"
|
|
647
|
-
end
|
|
648
|
-
end
|
|
649
|
-
|
|
650
|
-
class User
|
|
651
|
-
def self.destroy
|
|
652
|
-
"destroyed"
|
|
653
|
-
end
|
|
654
|
-
end
|
|
655
|
-
"#;
|
|
656
|
-
let session = ParseSession::new();
|
|
657
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
658
|
-
let root = parse_result.node();
|
|
659
|
-
let program = root.as_program_node().unwrap();
|
|
660
|
-
|
|
661
|
-
let mut genv = GlobalEnv::new();
|
|
662
|
-
let mut lenv = LocalEnv::new();
|
|
663
|
-
let mut changes = ChangeSet::new();
|
|
664
|
-
|
|
665
|
-
for stmt in &program.statements().body() {
|
|
666
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
// Both class methods should be registered
|
|
670
|
-
assert!(
|
|
671
|
-
genv.resolve_method(&Type::singleton("User"), "create").is_some(),
|
|
672
|
-
"User.create should be registered"
|
|
673
|
-
);
|
|
674
|
-
assert!(
|
|
675
|
-
genv.resolve_method(&Type::singleton("User"), "destroy").is_some(),
|
|
676
|
-
"User.destroy should be registered"
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
#[test]
|
|
681
|
-
fn test_class_method_param_type_propagation() {
|
|
682
|
-
let source = r#"
|
|
683
|
-
class User
|
|
684
|
-
def self.find(id)
|
|
685
|
-
id
|
|
686
|
-
end
|
|
687
|
-
end
|
|
688
|
-
|
|
689
|
-
User.find(42)
|
|
690
|
-
"#;
|
|
691
|
-
let session = ParseSession::new();
|
|
692
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
693
|
-
let root = parse_result.node();
|
|
694
|
-
let program = root.as_program_node().unwrap();
|
|
695
|
-
|
|
696
|
-
let mut genv = GlobalEnv::new();
|
|
697
|
-
let mut lenv = LocalEnv::new();
|
|
698
|
-
let mut changes = ChangeSet::new();
|
|
699
|
-
|
|
700
|
-
for stmt in &program.statements().body() {
|
|
701
|
-
crate::analyzer::install::install_node(&mut genv, &mut lenv, &mut changes, source, &stmt);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
let info = genv
|
|
705
|
-
.resolve_method(&Type::singleton("User"), "find")
|
|
706
|
-
.expect("User.find should be registered");
|
|
707
|
-
let param_vtxs = info.param_vertices.as_ref().expect("should have param vertices");
|
|
708
|
-
assert_eq!(param_vtxs.len(), 1);
|
|
709
|
-
|
|
710
|
-
let param_vtx = param_vtxs[0];
|
|
711
|
-
|
|
712
|
-
// Run solver to propagate argument types
|
|
713
|
-
genv.apply_changes(changes);
|
|
714
|
-
genv.run_all();
|
|
715
|
-
|
|
716
|
-
// Parameter should have Integer type propagated from call site
|
|
717
|
-
let vertex = genv.get_vertex(param_vtx).expect("param vertex should exist");
|
|
718
|
-
assert_eq!(vertex.show(), "Integer");
|
|
719
|
-
}
|
|
720
|
-
}
|