Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Generate RBS cache
run: |
echo "Generating RBS cache..."
ruby -I lib -rmethodray -e 'MethodRay.setup'
ruby -I lib -rmethodray -e '1'
# Copy cache to lib directory for bundling
# macOS uses ~/Library/Caches, Linux uses ~/.cache
if [[ "${{ runner.os }}" == "macOS" ]]; then
Expand Down
97 changes: 38 additions & 59 deletions ext/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,73 @@
//!
//! This module provides the Ruby gem interface using magnus.

use magnus::{function, method, prelude::*, Error, Ruby};
use magnus::{function, prelude::*, Error, Ruby};
use methodray_core::{
analyzer::AstInstaller,
env::{GlobalEnv, LocalEnv},
parser::ParseSession,
rbs,
};

#[magnus::wrap(class = "MethodRay::Analyzer")]
pub struct Analyzer {
#[allow(dead_code)]
path: String,
}

impl Analyzer {
fn new(path: String) -> Self {
Self { path }
}

fn version(&self) -> String {
"0.1.0".to_string()
}

/// Execute type inference
fn infer_types(&self, source: String) -> Result<String, Error> {
// Parse
let session = ParseSession::new();
let parse_result = session.parse_source(&source, "source.rb").map_err(|e| {
let ruby = unsafe { Ruby::get_unchecked() };
Error::new(ruby.exception_runtime_error(), e.to_string())
})?;
/// Type inference (public module function)
fn infer_types(source: String) -> Result<String, Error> {
// Parse
let session = ParseSession::new();
let parse_result = session.parse_source(&source, "source.rb").map_err(|e| {
let ruby = unsafe { Ruby::get_unchecked() };
Error::new(ruby.exception_runtime_error(), e.to_string())
})?;

// Build graph
let mut genv = GlobalEnv::new();
// Build graph
let mut genv = GlobalEnv::new();

// Register built-in methods from RBS
let ruby = unsafe { Ruby::get_unchecked() };
rbs::register_rbs_methods(&mut genv, &ruby)?;
// Register built-in methods from RBS
let ruby = unsafe { Ruby::get_unchecked() };
rbs::register_rbs_methods(&mut genv, &ruby)?;

let mut lenv = LocalEnv::new();
let mut installer = AstInstaller::new(&mut genv, &mut lenv, &source);
let mut lenv = LocalEnv::new();
let mut installer = AstInstaller::new(&mut genv, &mut lenv, &source);

// Process AST
let root = parse_result.node();
if let Some(program_node) = root.as_program_node() {
let statements = program_node.statements();
for stmt in &statements.body() {
installer.install_node(&stmt);
}
// Process AST
let root = parse_result.node();
if let Some(program_node) = root.as_program_node() {
let statements = program_node.statements();
for stmt in &statements.body() {
installer.install_node(&stmt);
}
}

installer.finish();
installer.finish();

// Return results as string
let mut results = Vec::new();
for (var_name, vtx_id) in lenv.all_vars() {
if let Some(vtx) = genv.get_vertex(*vtx_id) {
results.push(format!("{}: {}", var_name, vtx.show()));
}
// Return results as string
let mut results = Vec::new();
for (var_name, vtx_id) in lenv.all_vars() {
if let Some(vtx) = genv.get_vertex(*vtx_id) {
results.push(format!("{}: {}", var_name, vtx.show()));
}

Ok(results.join("\n"))
}

Ok(results.join("\n"))
}

/// Setup function that only generates RBS cache
/// This is used during gem build to pre-generate the cache
fn setup() -> Result<String, Error> {
/// RBS cache generation (executed when requiring methodray/setup)
fn setup() -> Result<(), Error> {
let ruby = unsafe { Ruby::get_unchecked() };
let mut genv = GlobalEnv::new();

// This will load RBS and save to cache if not already cached
let count = rbs::register_rbs_methods(&mut genv, &ruby)?;
rbs::register_rbs_methods(&mut genv, &ruby)?;

Ok(format!("RBS cache generated with {} methods", count))
Ok(())
}

#[magnus::init]
fn init(ruby: &Ruby) -> Result<(), Error> {
let module = ruby.define_module("MethodRay")?;
let class = module.define_class("Analyzer", ruby.class_object())?;

class.define_singleton_method("new", function!(Analyzer::new, 1))?;
class.define_method("version", method!(Analyzer::version, 0))?;
class.define_method("infer_types", method!(Analyzer::infer_types, 1))?;
module.define_singleton_method("infer_types", function!(infer_types, 1))?;

// Module-level setup function for cache generation
module.define_singleton_method("setup", function!(setup, 0))?;
// require methodray/setup to generate RBS cache on Ruby side
setup()?;

Ok(())
}
6 changes: 2 additions & 4 deletions rust/src/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ impl FileChecker {
// Just verify cache exists
use crate::cache::RbsCache;
RbsCache::load().context(
"Failed to load RBS cache. Please run from Ruby first to generate cache:\n\
ruby -rmethodray -e 'MethodRay::Analyzer.new(\".\").infer_types(\"x=1\")'",
"Failed to load RBS cache.",
)?;

Ok(Self {})
Expand Down Expand Up @@ -70,8 +69,7 @@ fn load_rbs_from_cache(genv: &mut GlobalEnv) -> Result<()> {
use crate::types::Type;

let cache = RbsCache::load().context(
"Failed to load RBS cache. Please run from Ruby first to generate cache:\n\
ruby -rmethodray -e 'MethodRay::Analyzer.new(\".\").infer_types(\"x=1\")'",
"Failed to load RBS cache.",
)?;

let methods = cache.methods();
Expand Down
6 changes: 1 addition & 5 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "methodray"

# Ensure RBS cache exists before any test runs (CheckTest CLI tests depend on it)
MethodRay.setup

require "minitest/autorun"
require "tempfile"
require "open3"
Expand All @@ -12,8 +9,7 @@ module CLITestHelper
private

def infer(source)
analyzer = MethodRay::Analyzer.new("test")
result = analyzer.infer_types(source)
result = MethodRay.infer_types(source)
types = {}
result.each_line do |line|
line = line.strip
Expand Down