Conversation
Adds `# typed: true` to all 753 spec files that previously had no Sorbet sigil, in preparation for enabling Sorbet's experimental RSpec mode.
Adds `--enable-experimental-rspec` to the Sorbet config so that Sorbet type-checks RSpec example groups with awareness of the DSL structure (describe/context/it/let/before/etc.). Also adds explicit includes on `RSpec::Core::ExampleGroup` in upstream.rbi so Sorbet resolves `RSpec::Matchers`, `RSpec::SharedContext`, and `RSpec::Mocks::ExampleMethods` methods within example group bodies.
These gems were previously excluded from tapioca gem RBI generation. Removing the exclusions and generating their RBIs gives Sorbet the type information needed to resolve rspec/rspec-mocks constants and methods under the new experimental RSpec mode.
Adds a tapioca DSL compiler that scans test files for custom RSpec matcher definitions and generates RBI stubs for them on `RSpec::Matchers`. Handles all four definition styles: - RSpec::Matchers.define :name do |args| - (RSpec::Matchers.)?define_negated_matcher :name, :base - (RSpec::Matchers.)?alias_matcher :new, :old - matcher :name do |args| (inside describe/context/shared_context) Because example groups include `RSpec::Matchers`, adding stubs there makes custom matchers visible to Sorbet's experimental RSpec type-checker. The generated RBI reduces "Method does not exist" errors by ~560.
`RSpec::Matchers::MatcherDelegator` forwards unknown calls to its wrapped `base_matcher` via `method_missing`. Sorbet cannot follow that delegation, so aliased/negated wrappers of `output` (e.g. `not_to_output`) appear to lack `to_stdout`, `to_stderr`, and their `_from_any_process` variants. Declaring those four methods on `MatcherDelegator` in upstream.rbi — with `T.self_type` as the return type to preserve chainability — eliminates the "Method to_stderr does not exist on AliasedNegatedMatcher" errors (-129).
|
"Looks" good when 🟢. I'd suggest you keep these all |
…ioca Instead of manually shim-ing individual methods (e.g. to_stdout/to_stderr) onto RSpec::Matchers::MatcherDelegator, a tapioca DSL compiler now enumerates every public method defined on a concrete RSpec::Matchers::BuiltIn subclass (excluding those already on BaseMatcher or MatcherDelegator) and declares them on MatcherDelegator. This is more precise than a blanket T.untyped escape-hatch: only methods that genuinely exist on some built-in matcher are permitted on delegators (to_stdout/to_stderr from Output, by/from/to from Change, with/argument from RespondTo, etc.). Calls to methods that exist on no built-in matcher still raise a 7003 error. Removes the manual upstream.rbi shim added in the previous commit.
|
@MikeMcQuaid This is just a scratch PR for me to track the progress in resolving major categories of rspec/sorbet compatibility (and possibly to link back to when upstreaming work to e.g. sorbet or tapioca). I think it would actually be counterproductive to start flipping the |
|
@dduugg cool works for me! |
coursgranja4-commits
left a comment
There was a problem hiding this comment.
and the diff what about
Adds sorbet/rbi/shims/rspec.rbi to inform Sorbet that RSpec::Core::ExampleGroup includes RSpec::Matchers, RSpec::Mocks::ExampleMethods, RSpec::SharedContext, and RuboCop::RSpec::ExpectOffense (both as instance and class methods, the latter to handle block-scope inference in .each + it patterns). Adds sorbet/rbi/shims/rubocop.rbi to define RuboCop::RSpec::ExpectOffense with its methods (expect_offense, expect_correction, expect_no_offenses, etc.), which are absent from the tapioca-generated rubocop gem RBI. 4309 type errors remain.
… a manual shim The rubocop gem ships rubocop/rspec/expect_offense but does not require it from its main entry point. sorbet/tapioca/require.rb already loads it as an additional require when tapioca processes rubocop-rspec, so tapioca correctly attributes RuboCop::RSpec::ExpectOffense to the rubocop gem. The trim allowlist in typecheck.rb already includes RuboCop::RSpec::ExpectOffense. After a full gem regeneration (brew typecheck --update-all) the module and its AnnotatedSource class appear in rubocop@1.85.0.rbi, making the manual sorbet/rbi/shims/rubocop.rbi shim redundant.
RSpec::Mocks::ExampleMethods includes ExpectHost which defines
`expect(target)` with a required argument. Because it is included
after RSpec::Matchers in the shim, it shadows
`RSpec::Matchers#expect(value = T.unsafe(nil), &block)` in Sorbet's
method lookup, causing a "Not enough arguments" error whenever the
block-only form is used:
expect { some_code }.not_to raise_error
Explicitly define `expect` on RSpec::Core::ExampleGroup itself so it
takes precedence over both included modules, matching the real RSpec
signature from rspec-expectations:
https://github.com/rspec/rspec/blob/rspec-expectations-v3.13.5/rspec-expectations/lib/rspec/expectations/syntax.rb#L72-L74
brew tc error count: 3467 (unchanged)
RSpec::Mocks::ExampleMethods includes ExpectHost which defines
`expect(target)` with a required argument. Because it was included
after RSpec::Matchers, it sat higher in the MRO and shadowed
`RSpec::Matchers#expect(value = T.unsafe(nil), &block)`, causing:
expect { brew "command", "info" } # Not enough arguments, expected 1 got 0
Fix by including RSpec::Matchers last so it sits above
RSpec::Mocks::ExampleMethods in the ancestor chain, making
the rspec-expectations signature take precedence:
https://github.com/rspec/rspec/blob/rspec-expectations-v3.13.5/rspec-expectations/lib/rspec/expectations/syntax.rb#L72-L74
brew tc error count: 3466 (was 3467)
|
@dduugg Let me know how I can help! |
Add `--enable-experimental-rspec` to the Sorbet config so that Sorbet type-checks RSpec spec files. This is an isolated piece of the work in #21690, separated so that the follow-up work of dialling up the `typed:` level of individual specs can land independently. Enabling the flag surfaced a pre-existing type error in `formula-analytics/pycall-setup.rbi` where `InfluxDBClient3#initialize` was incorrectly declared as a singleton method (`def self.initialize`) rather than an instance method (`def initialize`). Fixed here since the error is only visible with RSpec mode enabled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `--enable-experimental-rspec` to the Sorbet config so that Sorbet type-checks RSpec spec files. This is an isolated piece of the work in #21690, separated so that the follow-up work of dialling up the `typed:` level of individual specs can land independently. Enabling the flag surfaced a pre-existing type error in `formula-analytics/pycall-setup.rbi` where `InfluxDBClient3#initialize` was incorrectly declared as a singleton method (`def self.initialize`) rather than an instance method (`def initialize`). Fixed here since the error is only visible with RSpec mode enabled.
|
Hi @dduugg! I saw that you'd split out adding the experimental flag, and that now this PR has a bunch of conflicts, so I did a follow up to make all tests I'm not sure what your eventual plans are for this - I'd love to help do some of this work (I love a list!), but I also noticed that your issue here was closed? |
Summary
--enable-experimental-rspecmode so example groups are type-checked with RSpec DSL awareness# typed: true(from untyped) as a prerequisiterspecandrspec-mocks(previously excluded)RSpec::Matchersstubs, reducing "Method does not exist" errors by ~560Commits
test: promote all spec files to # typed: true— blanket sigil promotion on all 753 spec filessorbet: enable experimental RSpec mode— adds--enable-experimental-rspecto sorbet config and explicitRSpec::Core::ExampleGroupincludes in upstream.rbisorbet: generate tapioca gem RBIs for rspec and rspec-mocks— removes the exclusions and regenerates those gem RBIssorbet: add tapioca DSL compiler for custom RSpec matchers— scans test files fordefine/alias_matcher/define_negated_matcher/matchercalls and generates typed stubsError count
brew typecheckerrors: 5,990 → 5,432 after this PR (−558). The remaining errors are unrelated to custom matchers (built-in dynamic predicate matchers, include/module-helper resolution, etc.).🤖 Generated with Claude Code