Skip to content

Release Note 2.1

Soutaro Matsumoto edited this page Aug 27, 2026 · 2 revisions

The latest version of Steep 2.1 is 2.1.0.

Some of the highlights in Steep 2.1 are:

  • steep check and steep query connect to steep langserver (#2273)
  • steep query diagnostics command (#2274)
  • it block parameter (#2238)
  • RBS 4.2 (#2277)

You can install it with $ gem install steep or using Bundler.

gem 'steep', '~> 2.1.0', require: false

See the CHANGELOG for the details.

steep check and steep query connect to steep langserver

PRs: #2273

Steep 2.0 introduced steep server, a daemon that keeps the RBS environment in memory so that steep check and steep query can skip the setup. It had to be started separately, even when an editor was already running steep langserver on the same project.

steep langserver now binds the same per-project UNIX socket the daemon serves, so the commands connect to the language server your editor is already running.

# Your editor is running `steep langserver`

$ steep check
# Type checking files (server mode):

F.

lib/a.rb:7:20: [error] Cannot find compatible overloading of method `+` of type `::Integer`
...

$ steep query hover lib/a.rb:3:20
{"file":"lib/a.rb","line":3,"column":20,"result":{"contents":{"kind":"markdown","value":"```rbs\n::Array[::Integer]\n```\n"},...}}

Whichever of the two binds the socket first serves it, and the other one skips it. steep server remains the entry point where no editor is running -- CI, or coding agents working headless.

Pass --no-command-socket to steep langserver to turn the socket off.

steep query diagnostics

PRs: #2274

Note

steep query diagnostics is an experimental command. The user interface and output format may change without deprecation.

steep check is the imperative "run the type check now" command. The new steep query diagnostics asks the running server for the diagnostics it has already computed instead. It reloads the files that changed on disk, waits for the type checking to finish, and prints JSONL -- one JSON object per file.

$ steep query diagnostics lib/a.rb
{"path":"lib/a.rb","diagnostics":[{"message":"Cannot find compatible overloading of method `+` of type `::Integer`\n...","code":"Ruby::UnresolvedOverloading","severity":1,"range":{"start":{"line":6,"character":20},"end":{"line":6,"character":28}},...}]}

Giving no file prints every file the server knows about. "diagnostics": null means the server has not type checked that file yet, which is different from a file that has no error.

The command is designed for coding agents that edit files on disk and want fast feedback without spawning a type checker of their own.

it block parameter

PRs: #2238

Ruby 3.4's it block parameter is now supported.

[1, 2, 3].map { it + 1 }    # The type of `it` is `::Integer` here

Type checking, hover, signature help, and go-to-definition work with it as they do with _1. Steep parses your code with Prism's Ruby 3.4 parser, so it is recognized whichever supported Ruby version you run Steep on.

RBS 4.2

PRs: #2267, #2277, #2253

Steep 2.1 requires RBS 4.2.

RBS 4.1 changed the block types of Array#to_h and Hash#to_h, and the array-of-pairs argument of Hash.[], from tuple types to the new Hash::_Pair[K, V] interface. Steep derived a tuple hint only from a hint that is a tuple type itself, so a pair literal in a to_h block was inferred as an array and every such call reported Ruby::BlockBodyTypeMismatch.

Hash::_Pair is not a tuple type, but it declares the tuple it accepts through its #to_ary method. Steep now deconstructs an interface hint through #to_ary and uses the resulting tuple as the hint, so the literal is inferred as a tuple again.

[1, 2].to_h { |i| [i.to_s, i] }    #: Hash[String, Integer]

Type checking fixes

PRs: #2263, #2261, #2223

  • A type alias is accepted as the type of a lambda's block parameter. Annotating -> (&blk) { ... } with a proc type alias used to report Ruby::ProcTypeExpected, while the same proc type written inline was accepted. (#2263)
  • &:sym and &method(:name) are accepted where the block is optional. They used to report Ruby::BlockTypeMismatch. (#2261)
  • == against a literal is no longer reported as Ruby::UnreachableBranch when the receiver is Symbol, Integer, or String -- a regression introduced in 2.0.0. (#2223)

Clone this wiki locally