Skip to content

Make component removal best-effort and order-independent#4797

Open
Inconnu08 wants to merge 1 commit intorust-lang:mainfrom
Inconnu08:fix-component-remove-order
Open

Make component removal best-effort and order-independent#4797
Inconnu08 wants to merge 1 commit intorust-lang:mainfrom
Inconnu08:fix-component-remove-order

Conversation

@Inconnu08
Copy link
Copy Markdown
Contributor

Previously, rustup component remove parsed and removed components in a single pass,
which meant the command stopped on the first error after potentially removing earlier
valid components. As a result, behavior depended on argument order.

This change makes component removal best-effort by:

  • first collecting all parseable requested components
  • then attempting to remove each of them
  • deferring error reporting until the end

This makes the behavior consistent regardless of whether an invalid component appears
before or after valid ones as discussed here: #4794 .

Also adds a regression test covering both orderings.

Closes #4794

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch from 21add7c to 9bcc180 Compare April 4, 2026 04:20
Copy link
Copy Markdown
Member

@rami3l rami3l left a comment

Choose a reason for hiding this comment

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

Many thanks for working on this :)

View changes since this review

Copy link
Copy Markdown
Contributor

@djc djc left a comment

Choose a reason for hiding this comment

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

These commits are really not independent -- would prefer to have them all squashed.

View changes since this review

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch from 6c5bb6e to aefad2b Compare April 8, 2026 01:42
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 8, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch 2 times, most recently from 4f37dba to 7f5f683 Compare April 8, 2026 03:48
@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch from 7f5f683 to eeb7b06 Compare April 8, 2026 04:15
@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 8, 2026

Hmmm my apologies, but I think I might need some extra time for a final look at this before merging 🙏

@rami3l rami3l self-assigned this Apr 8, 2026
Copy link
Copy Markdown
Member

@rami3l rami3l left a comment

Choose a reason for hiding this comment

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

I'm feel okay about it globally but I have a few concerns:

  • I would like to confirm with @FranciscoTGouveia whether the original intention of #4794 is to aggregate certain errors (which is what I believe) rather than just showing the first error but with the bail-out delayed. IMHO the behavior as implemented in this patch will be very confusing to the user if multiple errors do happen. In this case I suggest that we either aggregate UnknownComponent* separately and early bail out for the rest, or actually aggregate all errors (which might be more difficult in terms of display).

  • The current distinction of UnknownComponent and UnknownComponents doesn't seem super necessary since it doesn't prevent the API user from creating the latter with only one Vec member.

  • The tuples in UnknownComponents might be better expressed via a separate struct, but that's a minor implementation detail.

View changes since this review

@Inconnu08
Copy link
Copy Markdown
Contributor Author

Thanks, that makes sense.

My intent with the current implementation was to:

  • preserve the existing UnknownComponent behavior for the single-component case
  • aggregate unknown component names only when multiple invalid components are present
  • keep the rest of the behavior as close as possible to the existing flow

One of the motivations for introducing UnknownComponents was also to avoid repeating the full error message multiple times when several invalid components are provided, which felt a bit noisy UX-wise. The grouped format seemed more concise and easier to read in that case.

That said, I agree the mixed behavior could be confusing if multiple different error types occur. I’m happy to adjust the approach once the intended UX for #4794 is confirmed with @FranciscoTGouveia whether that means aggregating only UnknownComponent* and bailing early for others, or reshaping the error handling more uniformly.

Also, point on UnknownComponent vs UnknownComponents: I introduced the second mainly to avoid regressing the existing single-component behavior, but I’m open to simplifying that if we settle on a cleaner model.

And agreed on the tuple, a small struct would likely make that clearer if we keep the grouped variant.

@FranciscoTGouveia
Copy link
Copy Markdown
Contributor

The original intention of 4794 was indeed to install all possible components and report back every invalid one.

For example, running rustup component add rust-analyzere rustc-dev llvm-toolse, should install rustc-dev successfully, while returning two separate errors, one for each misspelled component.

On another note, I briefly tested the patch and believe it would be worth adding a test case covering the example above, as this is the current output:

$ my-rustup component add rust-analyzere rustc-dev llvm-toolse
error: toolchain '1.90-x86_64-unknown-linux-gnu' does not contain component 'rust-analyzere' for target 'x86_64-unknown-linux-gnu'; did you mean 'rust-analyzer'?

@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 8, 2026

The original intention of 4794 was indeed to install all possible components and report back every invalid one.

@FranciscoTGouveia Thanks for your reply! Given that, my suggestions would be:

  • It'd be easier to just aggregate all UnknownComponent* errors in the installation process and bail out immediately like before if other kinds of errors do occur.

  • On the distinction of UnknownComponent vs UnknownComponents, I'd say it's more idiomatic to special-case the display format on a unified UnknownComponents, rather than doing two enum variants. In other words: if the Vec is of length 1 we print like the old UnknownComponent, otherwise we use the new printing format as introduced here.

  • It'd be better if the helper struct could be introduced.

@Inconnu08 What do you think?

@rami3l rami3l requested a review from FranciscoTGouveia April 8, 2026 19:26
@rami3l rami3l removed their assignment Apr 8, 2026
@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 8, 2026

(Assigned @FranciscoTGouveia for review upon his request.)

Comment on lines +259 to +260
"toolchain '{desc}' does not contain component '{component}'{}",
suggest_message(suggestion),
Copy link
Copy Markdown
Contributor

@FranciscoTGouveia FranciscoTGouveia Apr 8, 2026

Choose a reason for hiding this comment

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

View changes since the review

What if we insert a newline before calling suggest_message?
Otherwise, we could easily exceed 80 chars :/

));
assert!(!cx.config.rustupdir.has(path.parent().unwrap()));
}
}
Copy link
Copy Markdown
Contributor

@FranciscoTGouveia FranciscoTGouveia Apr 8, 2026

Choose a reason for hiding this comment

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

View changes since the review

There should also be a third case in which a valid component appears between invalid ones, ensuring that errors are correctly reported for both invalid components.

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.

Make the removal of components consistent

5 participants