Skip to content

version: enable Sorbet type checking#10331

Merged
SeekingMeaning merged 1 commit intoHomebrew:masterfrom
SeekingMeaning:version-typecheck
Jan 19, 2021
Merged

version: enable Sorbet type checking#10331
SeekingMeaning merged 1 commit intoHomebrew:masterfrom
SeekingMeaning:version-typecheck

Conversation

@SeekingMeaning
Copy link
Copy Markdown
Contributor

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew typecheck with your changes locally?
  • Have you successfully run brew tests with your changes locally?
  • Have you successfully run brew man locally and committed any changes?

Notable changes:

  • Added a NullVersion singleton class that extends Version
  • Changed Version::Token to be an abstract class
  • Added version.rbi to work around Sorbet limitation with aliases

There are a lot of T.untyped but this is a start, at least

@BrewTestBot
Copy link
Copy Markdown
Contributor

Review period will end on 2021-01-15 at 22:32:42 UTC.

@BrewTestBot BrewTestBot added the waiting for feedback Merging is blocked until sufficient time has passed for review label Jan 14, 2021
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorbet question: what does this do and why is it needed?

Copy link
Copy Markdown
Member

@reitermarkus reitermarkus Jan 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It casts T.nilable(Type) to Type, i.e. ensuring something is non-nil.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if it is nil with or without Sorbet enabled at runtime?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without, the >= call will fail; with, a TypeError is raised.

Both should never happen since the macOS major version should always be non-nil.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Is it possible to somehow move logic like this into where major is set/returned instead?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's calling Version#major, which definitely can have a nil major version.

Copy link
Copy Markdown
Contributor Author

@SeekingMeaning SeekingMeaning Jan 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option is to add an RBI file that says OS::Mac::Version#major will always return a non-nil Token. We can do this since we have this check in #initialize:

raise MacOSVersionError, version unless /\A1\d+(?:\.\d+){0,2}\Z/.match?(version)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option is to add an RBI file that says OS::Mac::Version#major will always return a non-nil Token

Something like that seems good 👍🏻

@BrewTestBot
Copy link
Copy Markdown
Contributor

Review period ended.

@BrewTestBot BrewTestBot removed the waiting for feedback Merging is blocked until sufficient time has passed for review label Jan 16, 2021
@SeekingMeaning SeekingMeaning force-pushed the version-typecheck branch 2 times, most recently from 6768893 to a72515c Compare January 16, 2021 19:55
@SeekingMeaning
Copy link
Copy Markdown
Contributor Author

SeekingMeaning commented Jan 16, 2021

  • Replaced T.untyped with String for Token#create, Version#create, and Version#initialize
    • Replaced ENV["HOMEBREW_LINUX_MINIMUM_GLIBC_VERSION"] with ENV.fetch("...")
  • Replaced when nil and else branches in Token#from with return NULL_TOKEN if ... guard clause
  • Added #null? to Token class as overridable method
    • The NullToken class already had #null? but now we can add override to its sig
  • Changed return types of NullVersion methods:
    • Changed return type of NullVersion#<=> from T.nilable(Integer) to plain Integer since it always returns -1
    • Changed return type of NullVersion#major, #minor, #patch from T.nilable(Token) to plain Token since they always return NULL_TOKEN

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we restrict this perhaps to Token, String, Integer, nil or NullToken? Would ease some other code, too.

Copy link
Copy Markdown
Contributor Author

@SeekingMeaning SeekingMeaning Jan 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave this a go, but I came to the conclusion that adding a type to this would require something like Java's generics where a subclass can specify the type of a parent variable. I don't think Sorbet supports this, though

Edit: I'll give this another try and see where I get

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Added a type for the value parameter for #initialize
    • TokenT.nilable(T.any(String, Integer))
    • NullToken has no parameters for #initialize
    • StringTokenString
    • NumericTokenT.any(String, Integer) (converted to Integer by value.to_i)
  2. Added overrides for attr_reader :value with the following return types:
    • NullTokenNilClass (The class for plain nil is NilClass)
    • StringTokenString
    • NumericTokenInteger
  3. Replaced @value = value with @value = T.let(value, T.untyped), otherwise we get this error for NullToken (and a similar error for StringToken and NumericToken):
version.rb:107: Expected NilClass but found T.nilable(T.any(String, Integer)) for method result type https://srb.help/7005
     107 |    attr_reader :value
                           ^^^^^
  Expected NilClass
    version.rb:107: Method value has return type NilClass
     107 |    attr_reader :value
              ^^^^^^^^^^^^^^^^^^
  Got T.nilable(T.any(String, Integer)) originating from:
    version.rb:59:
    59 |      @value = value
              ^^^^^^

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option is to add an RBI file that says OS::Mac::Version#major will always return a non-nil Token

Something like that seems good 👍🏻

@SeekingMeaning SeekingMeaning merged commit 6e6c339 into Homebrew:master Jan 19, 2021
@BrewTestBot BrewTestBot added the outdated PR was locked due to age label Feb 19, 2021
@Homebrew Homebrew locked as resolved and limited conversation to collaborators Feb 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

outdated PR was locked due to age

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants