-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Ruby: Add partial support for working with RBI (Ruby Interface) files #8845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This is a really good point. I'm not sure what the best approach is. We could completely separate Another option is to extract them as normal Ruby code, but try to exclude them from call resolution and other stuff that we don't want them included in. This might be as simple as adding |
Another thing that comes to mind: do we want to be working at the |
It may indeed cause problem, although most likely we'd resolve a call to both targets. The prototype likely has no body so its effect on dataflow etc should be minimal. In normal Ruby files you can also define the same method multiple times. At runtime the last definition "wins". So the problem with multiple definitions is not new to RBI; it's just more likely to happen with RBI files. |
Co-authored-by: Arthur Baars <aibaars@github.com>
Yeah, this is a really good point. I went with dataflow out of convenience more than anything else, mostly to use API graphs for get method calls/constant accesses from the
It's perhaps not a very relevant use case though. Type aliases seem uncommon, at least in the I've replaced the dataflow version with an AST based version in the latest commit at time of writing. The implementation is of very similar complexity overall. As a side note, I'm still using the CFG to determine things like:
It may be possible to cover these cases purely using the AST, e.g. by looking at successive statements in a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fine to me. We might want to have a generalized API that covers RBI and RBS when we add support for RBS files.
*/ | ||
abstract class RbiType extends Expr { } | ||
|
||
class ConstantReadAccessAsRbiType extends RbiType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class has no qldoc and its name isn't very descriptive. Is it supposed to represent a named class or module type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to add qldoc for this. It's intended to represent cases like read accesses to the Integer
and MyList
classes and the MyList2
constant in:
MyList2 = T.type_alias(MyList)
sig { params(l: MyList2).returns(Integer) }
def len(l); end
Generally I think that these types would always represent some Ruby class, possibly via some intermediate type aliases, but the class definitions might be not be available in the database (Integer
and String
are common examples). I ended up making this QL class very broad to avoid missing cases like these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separately, I've noticed that I can simplify this as just class ConstantReadAccessAsRbiType extends RbiType, ConstantReadAccess { }
after moving this library to the AST layer.
Makes sense to me - the design of this API should probably be based around working with other models-as-data tooling. |
RBI files are used by Sorbet to glean type information about Ruby code. These are valid Ruby files that can contain additional information which Sorbet can use to perform both static and runtime type checking. I've focused mostly on the static elements of this in the current PR.
It's possible to define the source code of a program directly in RBI files, but it generally seems more common to have the source of a program in regular
.rb
files and to use separate corresponding RBI files to define type signatures. This approach is useful in defining type signatures for gems, collected into a repository at https://github.com/sorbet/sorbet-typed/tree/master/lib - similar to the https://github.com/DefinitelyTyped/DefinitelyTyped project for TypeScript. External type definitions are the focus of this initial library implementation. One possible use case is to use existing type definitions fromsorbet-typed
to generate a list of type signatures for an API, which could serve as a first step in modelling that API.As RBI files are "just" Ruby, we just need to tell the extractor to look for
.rbi
files as well in order to extract them. I've not enabled this by default in this PR, partly because I'm concerned that methods with multiple definitions (one real, and one "prototype" for typing purposes) could cause problems with resolving call targets.This library is marked as experimental for the moment. It includes support for a chunk of RBI, but it's definitely not comprehensive. Some major missing features of the current implementation are:
T::Enumerable
,T::Set
,T::Range
T.reveal_type(x)
to get the type of a variablex
. These seemed less relevant for just extracting type signatures.T::Struct
andT::Enum
classes which lets code that uses these concepts be written in a more type-safe waySee https://sorbet.org/docs/rbi for reference