-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
migrator: typed: strict #21269
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
base: main
Are you sure you want to change the base?
migrator: typed: strict #21269
Conversation
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.
Pull request overview
This PR upgrades the migrator.rb file from typed: true to typed: strict mode in Sorbet, adding comprehensive type signatures throughout the codebase as part of the ongoing effort to improve type safety (#17297).
Key changes:
- Added type signatures to all class methods, instance methods, and error class constructors
- Added type annotations for all attr_reader declarations
- Used T.let with explicit types for all instance variable initializations to satisfy strict mode requirements
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Library/Homebrew/migrator.rb
Outdated
|
|
||
| # Error for when a migration is necessary. | ||
| class MigrationNeededError < RuntimeError | ||
| sig { params(oldname: BasicObject, newname: T.untyped).void } |
Copilot
AI
Dec 17, 2025
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.
The parameter oldname is typed as BasicObject which is overly restrictive. In the method body, string interpolation is used with oldname (line 20-21), which requires oldname to respond to to_s. However, BasicObject doesn't have to_s defined. This should be typed as String or T.untyped instead.
Library/Homebrew/migrator.rb
Outdated
|
|
||
| # Error for when the old name's path does not exist. | ||
| class MigratorNoOldpathError < RuntimeError | ||
| sig { params(oldname: BasicObject).void } |
Copilot
AI
Dec 17, 2025
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.
The parameter oldname is typed as BasicObject which is overly restrictive. In the method body, string interpolation is used with oldname (line 30), which requires oldname to respond to to_s. However, BasicObject doesn't have to_s defined. This should be typed as String or T.untyped instead.
Library/Homebrew/migrator.rb
Outdated
| @old_tap = T.let(first_tab&.tap, T.nilable(Tap)) | ||
|
|
||
| raise MigratorDifferentTapsError.new(formula, oldname, old_tap) if !force && !from_same_tap_user? | ||
| raise MigratorDifferentTapsError.new(formula, oldname, T.must(old_tap)) if !force && !from_same_tap_user? |
Copilot
AI
Dec 17, 2025
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 T.must call is potentially unsafe. If old_tap is nil at this point (which is possible based on the type signature), this will raise an exception. The conditional check !force && !from_same_tap_user? doesn't guarantee that old_tap is non-nil. Consider checking old_tap explicitly before using T.must, or handling the nil case differently.
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.
Yes, this T.must doesn't seem correct.
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.
@hyuraku please update this one.
Library/Homebrew/migrator.rb
Outdated
| end | ||
| end | ||
|
|
||
| sig { returns(T.nilable(T.any(T::Array[T.any(File, String, Pathname)], Integer))) } |
Copilot
AI
Dec 17, 2025
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 is an overly complex and broad return type signature. The method returns the result of FileUtils.mv which typically returns an array of moved files or an integer (0). This signature attempts to express all possible return types but is unnecessarily complicated. Consider using T.untyped or a simpler type like T.nilable(T::Array[String]) if the return value isn't actually used.
Library/Homebrew/migrator.rb
Outdated
| @newname_lock = T.let(FormulaLock.new(newname), T.nilable(FormulaLock)) | ||
| @oldname_lock = T.let(FormulaLock.new(oldname), T.nilable(FormulaLock)) | ||
| T.must(@newname_lock).lock | ||
| T.must(@oldname_lock).lock |
Copilot
AI
Dec 17, 2025
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.
The lock instance variables are being initialized and immediately used with T.must which is unnecessary and overly complicated. Since you're initializing them in the same method, you can assign them directly without wrapping in T.let(..., T.nilable(...)) and then calling T.must. Instead, type them as non-nilable and remove the T.must calls.
- for MigratorNoOldpathError to String
MikeMcQuaid
left a comment
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.
Thanks @hyuraku looking good so far! There's still a bunch of T.must (including those I didn't comment on) where I don't think there's a sufficient guarantee that it is non-nil. You can see a few examples I've provided to provide that guarantee.
Library/Homebrew/formula.rb
Outdated
| sig { params(fetch_head: T::Boolean).returns(T::Array[Keg]) } | ||
| def outdated_kegs(fetch_head: false) | ||
| raise Migrator::MigrationNeededError.new(oldnames_to_migrate.first, name) if migration_needed? | ||
| raise Migrator::MigrationNeededError.new(T.must(oldnames_to_migrate.first), name) if migration_needed? |
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.
| raise Migrator::MigrationNeededError.new(T.must(oldnames_to_migrate.first), name) if migration_needed? | |
| raise Migrator::MigrationNeededError.new(oldnames_to_migrate.fetch(0)), name) if migration_needed? |
Library/Homebrew/migrator.rb
Outdated
| @old_tap = T.let(first_tab&.tap, T.nilable(Tap)) | ||
|
|
||
| raise MigratorDifferentTapsError.new(formula, oldname, old_tap) if !force && !from_same_tap_user? | ||
| raise MigratorDifferentTapsError.new(formula, oldname, T.must(old_tap)) if !force && !from_same_tap_user? |
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.
Yes, this T.must doesn't seem correct.
Library/Homebrew/migrator.rb
Outdated
| formula_tap_user = formula.tap&.user | ||
| old_tap_user = nil | ||
|
|
||
| new_tap = if old_tap |
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.
| new_tap = if old_tap | |
| new_tap = if (old_tap = self.old_tap) |
and then can remove T.musts below
| sig { void } | ||
| def repin | ||
| return unless pinned? | ||
| return unless old_pin_link_record |
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.
| return unless old_pin_link_record | |
| old_pin_link_record = self.old_pin_link_record | |
| return unless old_pin_link_record |
and then can remove T.must below
MikeMcQuaid
left a comment
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.
@hyuraku A few more T.must to cleanup.
Library/Homebrew/migrator.rb
Outdated
| @old_tap = T.let(first_tab&.tap, T.nilable(Tap)) | ||
|
|
||
| raise MigratorDifferentTapsError.new(formula, oldname, old_tap) if !force && !from_same_tap_user? | ||
| raise MigratorDifferentTapsError.new(formula, oldname, T.must(old_tap)) if !force && !from_same_tap_user? |
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.
@hyuraku please update this one.
brew lgtm(style, typechecking and tests) with your changes locally?part of #17297