Skip to content

Conversation

@hyuraku
Copy link
Contributor

@hyuraku hyuraku commented Dec 17, 2025

  • 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 lgtm (style, typechecking and tests) with your changes locally?

part of #17297

Copilot AI review requested due to automatic review settings December 17, 2025 12:54
Copy link
Contributor

Copilot AI left a 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.


# Error for when a migration is necessary.
class MigrationNeededError < RuntimeError
sig { params(oldname: BasicObject, newname: T.untyped).void }
Copy link

Copilot AI Dec 17, 2025

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.

Copilot uses AI. Check for mistakes.

# Error for when the old name's path does not exist.
class MigratorNoOldpathError < RuntimeError
sig { params(oldname: BasicObject).void }
Copy link

Copilot AI Dec 17, 2025

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.

Copilot uses AI. Check for mistakes.
@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?
Copy link

Copilot AI Dec 17, 2025

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.

Copilot uses AI. Check for mistakes.
Copy link
Member

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.

Copy link
Member

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.

end
end

sig { returns(T.nilable(T.any(T::Array[T.any(File, String, Pathname)], Integer))) }
Copy link

Copilot AI Dec 17, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines 517 to 520
@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
Copy link

Copilot AI Dec 17, 2025

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.

Copilot uses AI. Check for mistakes.
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a 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.

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?
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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?

@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?
Copy link
Member

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.

formula_tap_user = formula.tap&.user
old_tap_user = nil

new_tap = if old_tap
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a 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.

@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?
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants