method-ray 0.1.4 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a219edf198245163ff36fbcc9e211dbfea2d5e5f41a467ee24636286967f4b9
4
- data.tar.gz: 7a82426e9c5b0ba53a2d1ca8bab90da2a4b4eb58316af7cfb8a576dc6dc28b64
3
+ metadata.gz: ee779223feab7110e29256fdc4860ba480d13d676d18e85284fd0765eef2cc4b
4
+ data.tar.gz: 249087d8f6b7772aa7428a4828ffc35eacb1b0aa8b27d5cd909b803176d6a683
5
5
  SHA512:
6
- metadata.gz: cac68a8608631ce81f145b17743ff7ec519e24f1ac2b92c90732d3f9fedd4badb26715fd5120b8a648fd574313383ac2dbd02d71cdfe98004ffafe5714e39cf3
7
- data.tar.gz: ded1386c832dfc9784c360d3685ce3431437d7cae1c155e02dc6606e3e155789f9f2ad42d5e4285aed6c8cb1dbf8231ac8f81392bf25a0536e3fd18e882537f3
6
+ metadata.gz: 8d6534a955eaf4937c5ebc4c525e07f5f7cd8599861f128cbb10161e00a6a932f1b47760e1283e7e2f48940ea0a89bb5c038ac83cdd8ce8a629f8f91b05e534a
7
+ data.tar.gz: cbe0d247ffcbd0ce4965f9e20a2293336aca3d7a6eeb96878665cb7692e11ea4366d3c1f2a87ea32711dbcd00a404d4a41da3e84d34829d40568ec0bb37d74b4
data/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.6] - 2026-02-23
9
+
10
+ ### Fixed
11
+
12
+ - Embed `method_loader.rb` at compile time with `include_str!` to eliminate runtime dependency on source directory ([#35](https://github.com/dak2/method-ray/pull/35))
13
+
14
+ ## [0.1.5] - 2026-02-23
15
+
16
+ ### Added
17
+
18
+ - String interpolation type inference (`InterpolatedStringNode`, `InterpolatedSymbolNode`, `InterpolatedRegularExpressionNode`) ([#26](https://github.com/dak2/method-ray/pull/26))
19
+ - Parentheses node type inference for parenthesized expressions ([#27](https://github.com/dak2/method-ray/pull/27))
20
+ - Qualified name method registration to resolve namespace conflicts ([#28](https://github.com/dak2/method-ray/pull/28))
21
+ - Return statement type inference with merge vertex pattern ([#29](https://github.com/dak2/method-ray/pull/29))
22
+ - Ternary operator type inference tests ([#30](https://github.com/dak2/method-ray/pull/30))
23
+ - Logical operator (`&&`/`||`) type inference with union type approximation ([#31](https://github.com/dak2/method-ray/pull/31))
24
+ - Class method (`def self.foo`) type registration and checking ([#32](https://github.com/dak2/method-ray/pull/32))
25
+
26
+ ### Changed
27
+
28
+ - Removed stateless `Analyzer` class and simplified Ruby FFI surface to module functions ([#33](https://github.com/dak2/method-ray/pull/33))
29
+
30
+ ### Deprecated
31
+
32
+ - `clear_cache` command ([#25](https://github.com/dak2/method-ray/pull/25))
33
+
8
34
  ## [0.1.4] - 2026-02-16
9
35
 
10
36
  ### Added
@@ -69,6 +95,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
69
95
  - Initial release
70
96
  - `methodray check` - Static type checking for Ruby files
71
97
 
98
+ [0.1.6]: https://github.com/dak2/method-ray/releases/tag/v0.1.6
99
+ [0.1.5]: https://github.com/dak2/method-ray/releases/tag/v0.1.5
72
100
  [0.1.4]: https://github.com/dak2/method-ray/releases/tag/v0.1.4
73
101
  [0.1.3]: https://github.com/dak2/method-ray/releases/tag/v0.1.3
74
102
  [0.1.2]: https://github.com/dak2/method-ray/releases/tag/v0.1.2
data/ext/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "methodray"
3
- version = "0.1.4"
3
+ version = "0.1.6"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
data/ext/src/lib.rs CHANGED
@@ -2,7 +2,7 @@
2
2
  //!
3
3
  //! This module provides the Ruby gem interface using magnus.
4
4
 
5
- use magnus::{function, method, prelude::*, Error, Ruby};
5
+ use magnus::{function, prelude::*, Error, Ruby};
6
6
  use methodray_core::{
7
7
  analyzer::AstInstaller,
8
8
  env::{GlobalEnv, LocalEnv},
@@ -10,86 +10,65 @@ use methodray_core::{
10
10
  rbs,
11
11
  };
12
12
 
13
- #[magnus::wrap(class = "MethodRay::Analyzer")]
14
- pub struct Analyzer {
15
- #[allow(dead_code)]
16
- path: String,
17
- }
18
-
19
- impl Analyzer {
20
- fn new(path: String) -> Self {
21
- Self { path }
22
- }
23
-
24
- fn version(&self) -> String {
25
- "0.1.0".to_string()
26
- }
27
-
28
- /// Execute type inference
29
- fn infer_types(&self, source: String) -> Result<String, Error> {
30
- // Parse
31
- let session = ParseSession::new();
32
- let parse_result = session.parse_source(&source, "source.rb").map_err(|e| {
33
- let ruby = unsafe { Ruby::get_unchecked() };
34
- Error::new(ruby.exception_runtime_error(), e.to_string())
35
- })?;
13
+ /// Type inference (public module function)
14
+ fn infer_types(source: String) -> Result<String, Error> {
15
+ // Parse
16
+ let session = ParseSession::new();
17
+ let parse_result = session.parse_source(&source, "source.rb").map_err(|e| {
18
+ let ruby = unsafe { Ruby::get_unchecked() };
19
+ Error::new(ruby.exception_runtime_error(), e.to_string())
20
+ })?;
36
21
 
37
- // Build graph
38
- let mut genv = GlobalEnv::new();
22
+ // Build graph
23
+ let mut genv = GlobalEnv::new();
39
24
 
40
- // Register built-in methods from RBS
41
- let ruby = unsafe { Ruby::get_unchecked() };
42
- rbs::register_rbs_methods(&mut genv, &ruby)?;
25
+ // Register built-in methods from RBS
26
+ let ruby = unsafe { Ruby::get_unchecked() };
27
+ rbs::register_rbs_methods(&mut genv, &ruby)?;
43
28
 
44
- let mut lenv = LocalEnv::new();
45
- let mut installer = AstInstaller::new(&mut genv, &mut lenv, &source);
29
+ let mut lenv = LocalEnv::new();
30
+ let mut installer = AstInstaller::new(&mut genv, &mut lenv, &source);
46
31
 
47
- // Process AST
48
- let root = parse_result.node();
49
- if let Some(program_node) = root.as_program_node() {
50
- let statements = program_node.statements();
51
- for stmt in &statements.body() {
52
- installer.install_node(&stmt);
53
- }
32
+ // Process AST
33
+ let root = parse_result.node();
34
+ if let Some(program_node) = root.as_program_node() {
35
+ let statements = program_node.statements();
36
+ for stmt in &statements.body() {
37
+ installer.install_node(&stmt);
54
38
  }
39
+ }
55
40
 
56
- installer.finish();
41
+ installer.finish();
57
42
 
58
- // Return results as string
59
- let mut results = Vec::new();
60
- for (var_name, vtx_id) in lenv.all_vars() {
61
- if let Some(vtx) = genv.get_vertex(*vtx_id) {
62
- results.push(format!("{}: {}", var_name, vtx.show()));
63
- }
43
+ // Return results as string
44
+ let mut results = Vec::new();
45
+ for (var_name, vtx_id) in lenv.all_vars() {
46
+ if let Some(vtx) = genv.get_vertex(*vtx_id) {
47
+ results.push(format!("{}: {}", var_name, vtx.show()));
64
48
  }
65
-
66
- Ok(results.join("\n"))
67
49
  }
50
+
51
+ Ok(results.join("\n"))
68
52
  }
69
53
 
70
- /// Setup function that only generates RBS cache
71
- /// This is used during gem build to pre-generate the cache
72
- fn setup() -> Result<String, Error> {
54
+ /// RBS cache generation (executed when requiring methodray/setup)
55
+ fn setup() -> Result<(), Error> {
73
56
  let ruby = unsafe { Ruby::get_unchecked() };
74
57
  let mut genv = GlobalEnv::new();
75
58
 
76
59
  // This will load RBS and save to cache if not already cached
77
- let count = rbs::register_rbs_methods(&mut genv, &ruby)?;
60
+ rbs::register_rbs_methods(&mut genv, &ruby)?;
78
61
 
79
- Ok(format!("RBS cache generated with {} methods", count))
62
+ Ok(())
80
63
  }
81
64
 
82
65
  #[magnus::init]
83
66
  fn init(ruby: &Ruby) -> Result<(), Error> {
84
67
  let module = ruby.define_module("MethodRay")?;
85
- let class = module.define_class("Analyzer", ruby.class_object())?;
86
-
87
- class.define_singleton_method("new", function!(Analyzer::new, 1))?;
88
- class.define_method("version", method!(Analyzer::version, 0))?;
89
- class.define_method("infer_types", method!(Analyzer::infer_types, 1))?;
68
+ module.define_singleton_method("infer_types", function!(infer_types, 1))?;
90
69
 
91
- // Module-level setup function for cache generation
92
- module.define_singleton_method("setup", function!(setup, 0))?;
70
+ // require methodray/setup to generate RBS cache on Ruby side
71
+ setup()?;
93
72
 
94
73
  Ok(())
95
74
  }
data/lib/methodray/cli.rb CHANGED
@@ -17,6 +17,7 @@ module MethodRay
17
17
  when 'watch'
18
18
  Commands.watch(args)
19
19
  when 'clear-cache'
20
+ warn "WARNING: 'clear-cache' is deprecated and will be removed in a future version."
20
21
  Commands.clear_cache(args)
21
22
  else
22
23
  puts "Unknown command: #{command}"
@@ -14,7 +14,6 @@ module MethodRay
14
14
  methodray version # Show version
15
15
  methodray check [FILE] [OPTIONS] # Type check a Ruby file
16
16
  methodray watch FILE # Watch file for changes and auto-check
17
- methodray clear-cache # Clear RBS method cache
18
17
 
19
18
  Examples:
20
19
  methodray check app/models/user.rb
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MethodRay
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.6'
5
5
  end
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "methodray-core"
3
- version = "0.1.4"
3
+ version = "0.1.6"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -18,10 +18,10 @@ pub(crate) fn process_attr_declaration(
18
18
  kind: AttrKind,
19
19
  attr_names: Vec<String>,
20
20
  ) {
21
- let Some(class_name) = genv.scope_manager.current_class_name() else {
21
+ let Some(qualified_name) = genv.scope_manager.current_qualified_name() else {
22
22
  return;
23
23
  };
24
- let recv_ty = Type::instance(&class_name);
24
+ let recv_ty = Type::instance(&qualified_name);
25
25
 
26
26
  for attr_name in attr_names {
27
27
  let ivar_name = format!("@{}", attr_name);
@@ -463,4 +463,76 @@ end
463
463
  let ret_vtx = info.return_vertex.unwrap();
464
464
  assert_eq!(get_type_show(&genv, ret_vtx), "String");
465
465
  }
466
+
467
+ // Test 11: ternary operator - different types → union
468
+ #[test]
469
+ fn test_ternary_union_type() {
470
+ let source = r#"
471
+ class Foo
472
+ def bar
473
+ true ? "hello" : 42
474
+ end
475
+ end
476
+ "#;
477
+ let genv = analyze(source);
478
+ let info = genv
479
+ .resolve_method(&Type::instance("Foo"), "bar")
480
+ .expect("Foo#bar should be registered");
481
+ let ret_vtx = info.return_vertex.unwrap();
482
+ assert_eq!(get_type_show(&genv, ret_vtx), "(Integer | String)");
483
+ }
484
+
485
+ // Test 12: ternary operator - same type → single type
486
+ #[test]
487
+ fn test_ternary_same_type() {
488
+ let source = r#"
489
+ class Foo
490
+ def bar
491
+ true ? "hello" : "world"
492
+ end
493
+ end
494
+ "#;
495
+ let genv = analyze(source);
496
+ let info = genv
497
+ .resolve_method(&Type::instance("Foo"), "bar")
498
+ .expect("Foo#bar should be registered");
499
+ let ret_vtx = info.return_vertex.unwrap();
500
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
501
+ }
502
+
503
+ // Test 13: ternary operator - nil branch → union with nil
504
+ #[test]
505
+ fn test_ternary_nil_branch() {
506
+ let source = r#"
507
+ class Foo
508
+ def bar
509
+ true ? "hello" : nil
510
+ end
511
+ end
512
+ "#;
513
+ let genv = analyze(source);
514
+ let info = genv
515
+ .resolve_method(&Type::instance("Foo"), "bar")
516
+ .expect("Foo#bar should be registered");
517
+ let ret_vtx = info.return_vertex.unwrap();
518
+ assert_eq!(get_type_show(&genv, ret_vtx), "(String | nil)");
519
+ }
520
+
521
+ // Test 14: nested ternary operator
522
+ #[test]
523
+ fn test_ternary_nested() {
524
+ let source = r#"
525
+ class Foo
526
+ def bar
527
+ true ? (false ? "inner" : 42) : :sym
528
+ end
529
+ end
530
+ "#;
531
+ let genv = analyze(source);
532
+ let info = genv
533
+ .resolve_method(&Type::instance("Foo"), "bar")
534
+ .expect("Foo#bar should be registered");
535
+ let ret_vtx = info.return_vertex.unwrap();
536
+ assert_eq!(get_type_show(&genv, ret_vtx), "(Integer | String | Symbol)");
537
+ }
466
538
  }