Ruby LSP Tapioca addon generates incomplete RBI files
In our codebase, when the Tapioca addon for Ruby LSP regenerates RBI after a routes change, three of the RBI files are missing some definitions.
Specifically:
sorbet/rbi/dsl/application_controller.rbiis missing helper methods:
class ApplicationController
include GeneratedUrlHelpersModule
include GeneratedPathHelpersModule
-
- sig { returns(HelperProxy) }
- def helpers; end
-
- module HelperMethods
- include ::Turbo::DriveHelper
- # <snip>
- include ::Pundit::Helper
-
- sig { returns(T.nilable(::Member)) }
- def current_member; end
-
- sig { returns(T.untyped) }
- def error; end
-
- sig { params(record: T.untyped).returns(T.untyped) }
- def policy(record); end
-
- sig { params(scope: T.untyped).returns(T.untyped) }
- def pundit_policy_scope(scope); end
-
- sig { returns(::ApplicationPolicy::UserContext) }
- def pundit_user; end
-
- sig { returns(T.untyped) }
- def success; end
-
- sig { returns(T.untyped) }
- def true_user; end
- end
-
- class HelperProxy < ::ActionView::Base
- include HelperMethods
- end
end
sorbet/rbi/dsl/devise/mailer.rbiis missing mailer methods (this class comes from the Devise gem):
class Devise::Mailer
include GeneratedUrlHelpersModule
-
- class << self
- sig { params(record: T.untyped, token: T.untyped, opts: T.untyped).returns(::ActionMailer::MessageDelivery) }
- def confirmation_instructions(record, token, opts = T.unsafe(nil)); end
-
- sig { params(record: T.untyped, opts: T.untyped).returns(::ActionMailer::MessageDelivery) }
- def email_changed(record, opts = T.unsafe(nil)); end
-
- sig { params(record: T.untyped, opts: T.untyped).returns(::ActionMailer::MessageDelivery) }
- def password_change(record, opts = T.unsafe(nil)); end
-
- sig { params(record: T.untyped, token: T.untyped, opts: T.untyped).returns(::ActionMailer::MessageDelivery) }
- def reset_password_instructions(record, token, opts = T.unsafe(nil)); end
-
- sig { params(record: T.untyped, token: T.untyped, opts: T.untyped).returns(::ActionMailer::MessageDelivery) }
- def unlock_instructions(record, token, opts = T.unsafe(nil)); end
- end
end
sorbet/rbi/dsl/rambulance/exceptions_app.rbiis also missing helper methods (this class comes from the Rambulance gem):
class Rambulance::ExceptionsApp
include GeneratedUrlHelpersModule
include GeneratedPathHelpersModule
-
- sig { returns(HelperProxy) }
- def helpers; end
-
- module HelperMethods
- include ::Turbo::DriveHelper
- # <snip>
- include ::DeviseHelper
-
- sig { returns(T.untyped) }
- def exception; end
-
- sig { returns(T.untyped) }
- def status_in_words; end
- end
-
- class HelperProxy < ::ActionView::Base
- include HelperMethods
- end
end
This behavior is annoying enough that we resorted to disabling the Tapioca addon for now, and asking developers to manually run bin/tapioca dsl after updating routes.
On the other hand, the issue is very consistent and easy to reproduce, so let me know if you need more details or need me to do anything to help debug this issue.
@olivier-thatch Thanks for reporting this! I'll try to repro.
We are experiencing the same issue: Here is some more information for our case
- Rails 7.0.8.7
- Ruby 3.3.7
Debugging the Tapioca LSP Addon i see that the ApplicationController is loaded when requesting the "route_dsl" server addon in the rails instance.
The combination of
lib/ruby_lsp/tapioca/server_addon.rb:38.when "route_dsl"that runst Tapioca with only the UrlHelpers compiler- Plus the fact that the ApplicationController is loaded when calling
::Tapioca::Dsl::Compilers::UrlHelpers.gather_constants
Makes the tapioca addon to run something similar to bin/tapioca dsl ApplicationController --only Tapioca::Dsl::Compilers::UrlHelpers ActiveSupportConcern.
And that ends removing some other content generated by the other compilers.
The same happens not only for ApplicationController but for other classes in our project too.
I think that the root cause is that there are constants that are generated by multiple compilers, and recompiling those .rbi with the --only flag makes those rbi get the rbi generated by other compilers pruned.
How to replicate
- Load a constant that is affected by Tapioca::Dsl::Compilers::UrlHelpers and another compiler. In this case i replicate with
ApplicationController.
# config/routes.rb
Rails.application.routes.draw do
a = ApplicationController
get "up" => "rails/health#show", as: :rails_health_check
end
- Comment the
getto trigger a dsl compiling of routes. - The
application_controller.rbihelpers rbi is going to get removed (bug).
I propose that the addon keeps updating the constants gathered by the compiler, but run the tapioca dsl without the --only flag, effectevily keeping the content written by other compilers in those .rbi files.
I will open a PR
I think this was done because we thought UrlHelpers compiler was distinct enough but I can see that ApplicationController is returned as part of UrlHelpers.gather_constants. We can either not call gather_constants and regenerate generated_path_helpers_module/generated_url_helpers_module or we can remove the --only flag as you have.
Unfortunately I don't think we can do the first option since you could have a constant that no longer should include the helper modules and needs updating. So I think we have to go with removing --only 😕
cc @vinistock
Yeah, I think we'll need to remove the --only for now. The idea was to get better performance and avoid having other compilers run, but then we would need it to not remove RBIs generated by other compilers.
@vinistock already done that in this PR if you could review it 👍🏻 .