A lightweight type checker for Ruby that adds optional type safety using YARD annotations. Designed to run alongside your existing Ruby LSP (like Shopify's Ruby LSP or Solargraph), not replace it.
Static Ruby analyzes your YARD type annotations and reports:
- Nil safety issues - Calling methods on values that might be nil
- Type mismatches - Passing wrong types to annotated methods
- Argument errors - Wrong number of arguments to methods
It's a simple, focused tool for teams that want some type checking without adopting Sorbet or RBS.
git clone https://github.com/sampeach/static-ruby
cd static-ruby
go build -o rubyls ./cmd/rubylsAdd YARD annotations to your Ruby code:
# @param name [String]
# @param age [Integer]
# @return [User]
def create_user(name, age)
User.new(name, age)
end
# @param user [User, nil]
# @return [String]
def greet(user)
user.name # Warning: called on potentially nil receiver
end
create_user(123, "wrong") # Error: type mismatchRun the analyzer:
./test-analyze your_file.rb# @param name [String]
# @param count [Integer]
# @return [Array<String>]
def repeat(name, count)
[name] * count
end# @param user [User, nil]
def maybe_greet(user)
user&.greet # Safe navigation - no warning
user.greet # Warning: potentially nil
end# @type [Hash{String => Integer}]
@cache = {}- Basic:
String,Integer,Float,Symbol,Boolean - Nilable:
String, nilorString | nil - Collections:
Array<T>,Hash{K => V},Set<T> - Your classes:
User,MyModule::MyClass
Static Ruby runs as a separate LSP server focused only on type diagnostics. Configure your editor to run both your main Ruby LSP and this one.
Use a multi-LSP setup or run it as a standalone checker.
-- Add alongside your existing Ruby LSP
local configs = require('lspconfig.configs')
configs.static_ruby = {
default_config = {
cmd = { '/path/to/rubyls' },
filetypes = { 'ruby' },
root_dir = require('lspconfig.util').root_pattern('Gemfile', '.git'),
},
}
require('lspconfig').static_ruby.setup{}For CI or one-off checks:
# Build the test tool
go build -o test-analyze ./cmd/test-analyze
# Analyze a file
./test-analyze app/models/user.rbExample output:
Parsed app/models/user.rb successfully
Parse errors: 0
Comments: 12
Diagnostics: 2
[warning] Line 24: method 'name' called on potentially nil receiver (type: User | nil)
[error] Line 43: argument 1 has type 'String' but parameter 'id' expects 'Integer'
- Only checks code with YARD annotations (unannotated code is ignored)
- No cross-file type inference
- No metaprogramming support
- No RBS/Sorbet compatibility
This is intentionally simple. For full type systems, use Sorbet or Steep with RBS.
MIT