Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 52 additions & 7 deletions src/ops/check.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::hash::Hash;

use rustfix::diagnostics::Diagnostic;
use serde::Deserialize;

Expand Down Expand Up @@ -41,22 +43,65 @@ pub struct Target {
test: bool,
}

#[derive(Deserialize, Hash, PartialEq, Clone, Eq, Debug)]
#[derive(Deserialize, Eq, Clone, Debug)]
#[serde(rename_all(deserialize = "kebab-case"))]
pub enum Kind {
Bin,
Example,
Test,
Bench,
CustomBuild,
#[serde(untagged)]
Lib(LibKind),
#[serde(untagged)]
Bin(BinKind),
#[serde(untagged)]
Other(String),
Comment on lines 48 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused by this change doesn't look the same as cargo's TargetKind which is included in the json output

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to have the target kinds separated into the 3 groups like I mentioned here. So just the variant would be need to be checked to know whether a fix can be applied in the current pass or not

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could we also get rid of Vec from kind: Vec<Kind>,?
I think you had mentioned about it being just a single TargetKind than an vec in cargo.
Since we would need these 3 types to be done together

[ lib ] == [ lib, rlib ] == [ lib, dylib, cdylib ]

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I had missed the #[serde(untagged)]

This is a nice idea for simplifying the categorization!

The downside is when we merge this into cargo. We'll be wanting to read json messages using cargo's definition. In doing so, we'll have to revert to the original TargetKind and a new enum and adapt between the two. Might be good for us to skip ahead to us creating a new enum independent of the json output for use in processing, making the adoption into cargo easier.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we also get rid of Vec from kind: Vec,?
I think you had mentioned about it being just a single TargetKind than an vec in cargo.

Unsure about it only ever having one element. I'd have to look at notes on that or dig back into the code. However, it is only one element for all cases but Lib which is more of an Other branch, see https://github.com/rust-lang/cargo/blob/0bfd820f837d32f1704561de8caaff10fcf2b57f/src/cargo/core/manifest.rs#L226-L241

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you have any suggestions on how I should redesign this? While comparing a BuildUnit, I would need to compare the whole build unit except TargetKind(& CrateType?) and then compare the TargetKind to determine if it should run in the current pass

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I had thought it was assumed. We should have our own data structure for identifying the need for a unique Fix Pass.

}

impl PartialEq for Kind {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Kind::Other(a), Kind::Other(b)) => a == b,
_ => std::mem::discriminant(self) == std::mem::discriminant(other),
}
}
}

impl Hash for Kind {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::CustomBuild => state.write_u8(1),
Self::Lib(l) => {
state.write_u8(2);
l.hash(state);
}
Self::Bin(b) => {
state.write_u8(3);
b.hash(state);
}
Self::Other(a) => {
state.write_u8(4);
a.hash(state);
}
};
}
}
Comment on lines +58 to +85
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be alright to do this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Custom PartialEq, Eq, and Hash are tricky to get correct and I'd recommend re-evaluating a design to use it.


#[derive(Deserialize, Hash, PartialEq, Clone, Eq, Debug)]
#[serde(rename_all(deserialize = "kebab-case"))]
pub enum LibKind {
Lib,
Rlib,
Dylib,
Cdylib,
Staticlib,
ProcMacro,
#[serde(untagged)]
Other(String),
}

#[derive(Deserialize, Hash, PartialEq, Clone, Eq, Debug)]
#[serde(rename_all(deserialize = "kebab-case"))]
pub enum BinKind {
Bin,
Example,
Test,
Bench,
}

#[derive(Deserialize, Hash, PartialEq, Clone, Eq, Debug)]
Expand Down
47 changes: 26 additions & 21 deletions src/ops/fixit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
let mut iteration = 0;

let mut last_errors = IndexMap::new();
let mut current_target: Option<BuildUnit> = None;
let mut current_target: IndexSet<BuildUnit> = IndexSet::new();
let mut seen = HashSet::new();

loop {
Expand All @@ -80,7 +80,7 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
if !args.broken_code && exit_code != Some(0) {
let mut out = String::new();

if current_target.is_some() {
if !current_target.is_empty() {
out.push_str(
"failed to automatically apply fixes suggested by rustc\n\n\
after fixes were automatically applied the \
Expand Down Expand Up @@ -150,7 +150,10 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
let (mut errors, build_unit_map) = collect_errors(messages, &seen);

if iteration >= max_iterations {
if let Some(target) = current_target {
if current_target.is_empty() {
break;
}
for target in &current_target {
if seen.iter().all(|b| b.package_id != target.package_id) {
shell::status("Checking", format_package_id(&target.package_id)?)?;
}
Expand All @@ -160,9 +163,9 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
}
files = IndexMap::new();

let mut errors = errors.shift_remove(&target).unwrap_or_else(IndexSet::new);
let mut errors = errors.shift_remove(target).unwrap_or_else(IndexSet::new);

if let Some(e) = build_unit_map.get(&target) {
if let Some(e) = build_unit_map.get(target) {
for (_, e) in e.iter().flat_map(|(_, s)| s) {
let Some(e) = e else {
continue;
Expand All @@ -174,26 +177,26 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
shell::print_ansi_stderr(format!("{}\n\n", e.trim_end()).as_bytes())?;
}

seen.insert(target);
current_target = None;
iteration = 0;
} else {
break;
seen.insert(target.clone());
}
iteration = 0;
current_target = IndexSet::new();
Comment on lines -178 to +183
Copy link
Contributor

Choose a reason for hiding this comment

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

Like moving iteration = 0; out of the if/for, could you move minor changes in this PR into refactor commits before hand to help make reviewing this commit easier?

}

let mut made_changes = false;
let mut made_changes = HashSet::new();

for (build_unit, file_map) in build_unit_map {
if seen.contains(&build_unit) {
continue;
}

let same = current_target.iter().any(|b| b == &build_unit);

let build_unit_errors = errors
.entry(build_unit.clone())
.or_insert_with(IndexSet::new);

if current_target.is_none() && file_map.is_empty() {
if current_target.is_empty() && file_map.is_empty() {
if seen.iter().all(|b| b.package_id != build_unit.package_id) {
shell::status("Checking", format_package_id(&build_unit.package_id)?)?;
}
Expand All @@ -204,11 +207,11 @@ fn exec(args: FixitArgs) -> CargoResult<()> {

seen.insert(build_unit);
} else if !file_map.is_empty()
&& current_target.get_or_insert(build_unit.clone()) == &build_unit
&& (same || current_target.is_empty())
&& fix_errors(&mut files, file_map, build_unit_errors)?
{
made_changes = true;
break;
current_target.insert(build_unit.clone());
made_changes.insert(build_unit);
}
}

Expand All @@ -218,8 +221,12 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
last_errors = errors;
iteration += 1;

if !made_changes {
if let Some(pkg) = current_target {
if made_changes.is_empty() {
if current_target.is_empty() {
break;
}

for pkg in current_target {
if seen.iter().all(|b| b.package_id != pkg.package_id) {
shell::status("Checking", format_package_id(&pkg.package_id)?)?;
}
Expand All @@ -235,11 +242,9 @@ fn exec(args: FixitArgs) -> CargoResult<()> {
}

seen.insert(pkg);
current_target = None;
iteration = 0;
} else {
break;
}
iteration = 0;
current_target = IndexSet::new();
}
}

Expand Down