Skip to content
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

feat: remove features when removing dependencies #113

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

njelich
Copy link

@njelich njelich commented Mar 14, 2024

When removing dependencies using the --fix flag, the features for those dependencies are not removed. This adds support for that.

I tested it manually, but tests should probably be written to make sure it works generally. From what I can see there are currently no tests for the --fix functionality anyway.

@njelich
Copy link
Author

njelich commented Mar 16, 2024

@bnjbvr Pending review

@bnjbvr
Copy link
Owner

bnjbvr commented Mar 18, 2024

Hi, as I'm working on my spare time on this project, I might not be very reactive for reviews and all of that.

That being said: how does this work? How can you guess the feature's name? (are you assuming the feature's named after the dependency?) Not sure how prevalent that usage is, so unclear whether we should do that automatically...

@njelich
Copy link
Author

njelich commented Mar 18, 2024

@bnjbvr I added a test case so you can see what it does. You can also add more autofix tests using the same setup.

@njelich
Copy link
Author

njelich commented Mar 18, 2024

In short, for a Cargo.toml with the unused log dependency:

[dependencies]
log = "0.4.14"

[features]
default = ["std"]
std = ["log/std", "other"]
other = []

It sees the log dependency is unused, and that the std feature on the log dependency is used with log/std. When removing log, it also removes the log/std feature.

[dependencies]

[features]
default = ["std"]
std = [ "other"]
other = []

@njelich
Copy link
Author

njelich commented Mar 22, 2024

@bnjbvr Have you had a chance to take a look at this?

Copy link
Owner

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

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

Thanks, took the time to review.

Bonus points if you switch to cargo-insta for the snapshot tests :) (but not mandatory, of course!).

src/main.rs Outdated
if let Some(deps) = deps.as_array_mut() {
deps.retain(|dep| {
if let Some(dep) = dep.as_str() {
!dependencies_list.iter().any(|d| dep.contains(d))
Copy link
Owner

Choose a reason for hiding this comment

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

I think contains is a bit too vague here: a dependency flagged for removal called log would cause removal of a feature enabling something (a feature or another dependency) called flog.

Can you handle that better, and add a test that checks that too, please?

@bnjbvr bnjbvr self-requested a review August 23, 2024 09:40
@bnjbvr
Copy link
Owner

bnjbvr commented Aug 23, 2024

Getting back to this: any idea why the CI doesn't even show up? There's no job and no way for me to approve the run or not 🤔

Copy link
Owner

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

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

I don't know why the CI job fails, but when I tried to run the tests locally by repeating the commands, they failed. Could you take another look please?

@njelich
Copy link
Author

njelich commented Sep 2, 2024

Will check later today

@bnjbvr
Copy link
Owner

bnjbvr commented Jan 6, 2025

hi @njelich, happy new year! Do you plan to work on this anytime soon, or should I close this PR for the time being?

@njelich
Copy link
Author

njelich commented Jan 8, 2025

I could take a short look

@njelich
Copy link
Author

njelich commented Jan 8, 2025

CI/CD passed on my copy of the repo. @bnjbvr Please retry running it.

@njelich njelich requested a review from bnjbvr January 9, 2025 07:17
Copy link
Owner

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

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

Thanks! The test doesn't pass on my machine. Instead of using diff to compare the result, would you be open to rewrite all these tests as unit tests of remove_dependencies? It should be possible to pass a full string representing a valid manifest, and the expected final manifest. This would be simpler to test, notably it would become Just™ a Rust unit test, and it would not require a new CI step. Does it make sense to you?

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
log = "0.4.14"
Copy link
Owner

Choose a reason for hiding this comment

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

I think log should be removed in this expected test result, since it's unused in the src directory?


[features]
default = ["std"]
std = ["log/std", "other"]
Copy link
Owner

Choose a reason for hiding this comment

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

and log/std here too

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
log = "0.4.14"
Copy link
Owner

Choose a reason for hiding this comment

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

and same in this file

Comment on lines +283 to +286
!dependencies_list.iter().any(|d| {
let prefix = d.to_string() + "/";
dep.contains(&prefix)
})
Copy link
Owner

Choose a reason for hiding this comment

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

So, scanning https://doc.rust-lang.org/cargo/reference/features.html, I think the whole set of rules is the following:

  • you can remove the dependency name, if there's no other feature named like it. e.g. if you depend on log, and in the [features] array there's only myfeat = ["log"], then it can be removed automatically. But it can't be removed if such a feature exists with the same name, because in that case it's referring to that feature with the same name (as far as I can tell).
  • there's another way to precise, starting from Rust 1.60, a dependency name over a feature name: dep:{dependency_name}, so it should be handled here too.
  • we can remove anything that starts with {dependency_name}/, as you're doing here 👍 .
  • we can remove anything that starts with {dependency_name}?, for optional dependencies

It would be super sweet to address those four cases completely, or someone could do this as a followup. Do you have a preference?

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