Fully qualify extend T::Generic & T::Helpers - #1001
Conversation
e02d892 to
90f5578
Compare
| unless already_extends?(node, /^(::)?T::Helpers$/) | ||
| extend_with("T::Helpers", into: node, at: insert_pos) | ||
| end | ||
| find_and_remove_existing_extend(node, /^(::)?T::Helpers$/) |
There was a problem hiding this comment.
I'm not sure if we should try to fix a pre-existing extend T::Helpers like this.
It's a nice feature in theory, but narrow. There are many other explicit uses of T that would be problematic (T.untyped, T::Boolean, T.any, ...), which we couldn't patch up as easily/reliably. I'm inclined err of inaction, and require people fix their own explicit usages of T.
I suppose the distinction is: who wrote the T?
If our rewriter wrote it, we should be good stewards and ::T it. If devs wrote it explicitly in their own source, they should choose ::T if they want it (or rename their generic arg to not be called T).
There was a problem hiding this comment.
Makes sense! I can drop those commits and open up something separately if we decide we want the behaviour later on. In that case I think it would make sense to remove already_extends? (rather than fixing) and just allow, potentially, a duplicate extends
RBS + pre-existing extends
# bin/console
source = <<~RUBY
# typed: true
# @final
#: [T]
class Box
extend T::Helpers
end
RUBY
puts Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(
source,
file: "example.rb",
)# typed: true
class Box
extend ::T::Helpers # spoom inserted (still needed, otherwise we `extend` after `final!` unless we insert below)
final!
extend ::T::Generic
T = type_member
extend T::Helpers # source
endThere was a problem hiding this comment.
In a similar vein, I think that's too narrow of a problem-solve to be worth it. It would make their extend T::Helpers, but other code like T.nilable still fail.
I think it's a simpler (more predictable) mental model for the developer if #: [T] always caused unqualified accesses to fail, consistently
There was a problem hiding this comment.
Agreed:) I removed already_extends? so it will continue to behave as it has been (inserting without checking for pre-existing) and a [T] + existing T::Helpers/Generic will error as expected
90f5578 to
a7791d4
Compare
| unless already_extends?(node, /^(::)?T::Helpers$/) | ||
| extend_with("T::Helpers", into: node, at: insert_pos) | ||
| end | ||
| extend_with("::T::Helpers", into: node, at: insert_pos) |
There was a problem hiding this comment.
Let's add a test doing this:
# @final
class Box
extend T::Helpers
endDo we add the extend anyway?
class Box
extend T::Helpers
extend ::T::Helpers
final!
endThere was a problem hiding this comment.
Yes:) Since already_extends? wasn't finding the node we've been inserting a duplicate even if there was a pre-existing. If we wanted already_extends? fixed instead of removed we'd need some logic to insert in the correct place (after the extends). Your example ends up as:
# typed: true
class Box
extend ::T::Helpers # spoom inserted
final!
extend T::Helpers # pre-existing
end
There was a problem hiding this comment.
Let's add this as a test to show the behavior is expected
a7791d4 to
41be386
Compare
Previously, a TypeError could be raised when we tried to insert an `extend T::Generic` or `extend T::Helpers` into a class that had a generic type member called `T` This commit fully qualifies inserted extends to `::T::Generic` and `::T::Helpers` to prevent this
41be386 to
0bee885
Compare
Tcan lead toTypeError#990To ensure
T::GenericandT::Helpersdon't conflict with user definedTconstants, we can fully qualify insertedT::Generic/Helpersto::T::