Add erase_generic_types option to RBS translators#611
Conversation
| #: (Method, ::RBS::MethodType) -> Sig | ||
| def translate(method, type) | ||
| translator = new(method) |
There was a problem hiding this comment.
This method is just a shim for backwards-compatibility, so there's no need to also add this option here.
Users who want the new API can just switch to .new.
| @result = Sig.new #: Sig | ||
| @type_translator = TypeTranslator.new #: TypeTranslator | ||
| @erase_generic_types = erase_generic_types |
There was a problem hiding this comment.
Style nit, keep the ivars initialized from the params first and in-order
| @result = Sig.new #: Sig | |
| @type_translator = TypeTranslator.new #: TypeTranslator | |
| @erase_generic_types = erase_generic_types | |
| @erase_generic_types = erase_generic_types | |
| @result = Sig.new #: Sig | |
| @type_translator = TypeTranslator.new #: TypeTranslator |
| Type.proc.params(arg0: Type.untyped).returns(Type.untyped) | ||
| when ::RBS::Types::Variable | ||
| Type.type_parameter(type.name) | ||
| @erase_generic_types ? Type.untyped : Type.type_parameter(type.name) |
There was a problem hiding this comment.
Rewrite into T.anything rather than T.untyped. It conveys a more specific specific intent.
| @erase_generic_types ? Type.untyped : Type.type_parameter(type.name) | |
| @erase_generic_types ? Type.anything : Type.type_parameter(type.name) |
There was a problem hiding this comment.
Sounds good:) I wondered if anything would act differently from untyped since Sorbet seems to specifically use untyped, but outside of a snippet that couldn't come from statically correct RBS anyways it seems to be all good
| assert_equal( | ||
| [ | ||
| SigParam.new("array", Type.simple("Array")), | ||
| SigParam.new("item", Type.untyped), |
There was a problem hiding this comment.
Will need to update the tests to match, e.g.
| SigParam.new("item", Type.untyped), | |
| SigParam.new("item", Type.anything), |
| def test_translate_variable | ||
| assert_equal(Type.type_parameter(:T), translate_variable(:T)) | ||
| assert_equal(Type.type_parameter(:E), translate_variable(:E)) | ||
| end |
There was a problem hiding this comment.
Oh interesting, I guess this wasn't covered directly before, but indirectly via test/rbi/rbs/method_type_translator_test.rb
| assert_equal(Type.class_of(Type.simple("Foo")), translate("singleton(Foo)", erase_generic_types: true)) | ||
| assert_equal( | ||
| Type.class_of(Type.simple("Foo")), | ||
| translate("singleton(Foo)[Bar]", erase_generic_types: true), | ||
| ) |
There was a problem hiding this comment.
Notably, the expected value is the same in both cases. You can emphasize that by extracting the variable in common:
| assert_equal(Type.class_of(Type.simple("Foo")), translate("singleton(Foo)", erase_generic_types: true)) | |
| assert_equal( | |
| Type.class_of(Type.simple("Foo")), | |
| translate("singleton(Foo)[Bar]", erase_generic_types: true), | |
| ) | |
| expected = Type.class_of(Type.simple("Foo")) | |
| assert_equal(expected, translate("singleton(Foo)", erase_generic_types: true)) | |
| assert_equal( | |
| expected, | |
| translate("singleton(Foo)[Bar]", erase_generic_types: true), | |
| ) |
There was a problem hiding this comment.
I consolidated the tests a bit
0556924 to
6680acb
Compare
| end | ||
|
|
||
| #: (String type_name) -> String | ||
| def erase_t_generic_type(type_name) |
There was a problem hiding this comment.
Similarly to how we add the prefix to translate generic types, we probably want to strip prefixes and use the actual runtime type when erasing. Otherwise something like #: (String component_name, T::Array[Symbol] includes) -> void can error at runtime
6680acb to
8d4e7bf
Compare
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
| Type.proc.params(arg0: Type.untyped).returns(Type.untyped) | ||
| when ::RBS::Types::Variable | ||
| Type.type_parameter(type.name) | ||
| @options.erase_generic_types? ? Type.anything : Type.type_parameter(type.name) |
There was a problem hiding this comment.
Inline with my other comment, I think these should always stay
There was a problem hiding this comment.
Ok, I agree that we can replace method type variables with T.anyting, but nitpick: Can we make this a normal if/else. Usage of the ternary operator is really rare in Ruby:
| @options.erase_generic_types? ? Type.anything : Type.type_parameter(type.name) | |
| if @options.erase_generic_types? | |
| Type.anything | |
| else | |
| Type.type_parameter(type.name) | |
| end |
Previously, `RBS::TypeTranslator` and `RBS::MethodTypeTranslator` always translated generic types like `Foo[Bar]`. However, `sorbet-runtime` is unable to enforce them, so they are superfluous in runtime contexts This commit adds a default `false` `erase_generic_types` kwarg to both translators. When set to `true` generic types are dropped and the simple type is returned (`Foo[Bar]` to `Foo`)
0b23987 to
4cd0e91
Compare
8d4e7bf to
a78fe18
Compare
paracycle
left a comment
There was a problem hiding this comment.
Thanks, I have a small stylistic comment, otherwise, this looks good to me!

Previously,
RBS::TypeTranslatorandRBS::MethodTypeTranslatoralways translated generic types. However, assorbet-runtimeis unable to enforce them, they are superfluous in runtime contextsThis PR adds a default
falseerase_generic_typeskwarg to both translator's constructors. When set totruegeneric types are dropped (Foo[Bar]toFoo) and type params are treated asT.anything