Skip to content

Implemented rough draft of check write mode. #2539

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

Merged
merged 11 commits into from
Apr 19, 2018
Merged

Conversation

davidbarsky
Copy link
Contributor

@davidbarsky davidbarsky commented Mar 17, 2018

This is an attempt to address #2536. Of the items to handle in the list, this PR handles:

  • check to the rustfmt bin options
  • implement check like diff mode, but with different exit codes.

It does not, at press time:

  • document the check mode
  • add a test to make sure this works.

I'll add the latter two this weekend.

(This PR is somewhat clunky, as I was able to propagate WriteMode by adding an additional field to the Summary struct. Alternatives include returning a FmtResult<(Summary, Option<WriteMode>)>, but that felt messy.)

@davidbarsky
Copy link
Contributor Author

In commit 533df58, I introduced a simple test case for --write-mode=check using assert-cli, but that introduces a new dependency, and I'm not sure that's a good decision. If introducing this dependency is okay, I'd happily write some additional test cases for the other WriteModes and/or introduce more testing for the CLI options.

Additionally, in commit b0ece6b5, I've documented the check option in the README.

I'm not too happy with how I'm handling propagation of WriteMode—I feel like I'm using the Summary struct as a de-facto context object. Would appreciate advice on handling this better.

src/bin/main.rs Outdated
@@ -342,6 +346,8 @@ fn main() {
Ok(summary) => {
if summary.has_operational_errors() {
1
} else if summary.has_diff && summary.write_mode == Some(WriteMode::Check) {
1
Copy link
Member

Choose a reason for hiding this comment

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

It will probably be easier to change the other exit codes now, rather than later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alrighty! I'll implement the stuff suggested in this comment: #1977 (comment)

@@ -183,6 +183,9 @@ configuration_option_enum! { WriteMode:
Checkstyle,
// Output the changed lines (for internal value only)
Modified,
// Displays how much of the input was processed, but if anything was modified, rustfmt quits
// with exit code 0. This option is intended to be run as part of a CI pipeline.
Copy link
Member

Choose a reason for hiding this comment

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

I think the exit code is backwards - it should quit with 1 if anything was changed. It displays a diff, if there is one, not progress exactly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

D'oh—you're right. I'll fix in subsequent commits.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed/resolved.

@@ -26,6 +28,9 @@ pub struct Summary {
// Formatted code differs from existing code (write-mode diff only).
pub has_diff: bool,

// What write mode rustfmt was invoked with, if any.
pub write_mode: Option<WriteMode>,
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this needs to be optional - even if there is no explicit write mode, then there is a default.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I'll default it to overwrite?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved, defaults to Overwrite.

README.md Outdated

* `overwrite` Is the default and overwrites the original files _without_ creating backups.
* `replace` Overwrites the original files after creating backups of the files.
* `display` Will print the formatted files to stdout.
* `plain` Also writes to stdout, but with no metadata.
* `diff` Will print a diff between the original files and formatted files to stdout.
Will also exit with an error code if there are any differences.
* `check` Similar to `diff`, but is intended to be run in during CI and returns error code 1
on failure instead of 4 as `diff` does.
Copy link
Member

Choose a reason for hiding this comment

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

I would document check mode in its own right, rather than comparing to diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point; will do.

@nrc
Copy link
Member

nrc commented Mar 18, 2018

Thanks for the PR! The 'hard' bit of running the new write mode looks great. I agree that it would be better to avoid adding the write mode to the summary and have suggested a possible solution in a review comment.

I think adding assert-cli as a dev-dependency is fine. Would be good to test a few of the exit code situations.

@davidbarsky
Copy link
Contributor Author

Thanks for the PR! The 'hard' bit of running the new write mode looks great. I agree that it would be better to avoid adding the write mode to the summary and have suggested a possible solution in a review comment.

Thanks! Will address shortly.

I think adding assert-cli as a dev-dependency is fine. Would be good to test a few of the exit code situations.

Sounds good, I'll write test cases for those. Should I move the assert-cli tests away from main.rs, or...?

@davidbarsky
Copy link
Contributor Author

davidbarsky commented Mar 19, 2018

So far:

  • Added better documentation for WriteMode::Check, both in --help and README.md.
  • Implemented Default for WriteMode, where default is Overwrite. This removes several Options in summary and main.

Still to do:

  • Refactor/simplify exit codes (I think the acceptance criteria for this PR should include this.)
  • Write assert-cli tests for the new exit code system.

Copy link
Contributor

@topecongiro topecongiro left a comment

Choose a reason for hiding this comment

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

Thank you the PR! I think the code handling exit code needs some update, but otherwise LGTM.

src/bin/main.rs Outdated
@@ -342,6 +346,8 @@ fn main() {
Ok(summary) => {
if summary.has_operational_errors() {
1
Copy link
Contributor

Choose a reason for hiding this comment

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

This part is a bit wrong. IIRC in check mode, the exit code should be either 0 or 1, but this change does not seem to respect it.

Copy link
Contributor Author

@davidbarsky davidbarsky Mar 19, 2018

Choose a reason for hiding this comment

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

Thanks for the feedback! I haven't gotten around to implementing #1977 (comment), but when I do later today, this the exit code handling should be far nicer and correct, wheras with my PR, it is not.

@davidbarsky
Copy link
Contributor Author

Sorry for the delay; work happened. I correct the exit code handling as I understood #1977 (comment). I'd like to clarify—should I log cause with eprintln! for non-zero exit codes?

@nrc
Copy link
Member

nrc commented Mar 22, 2018

should I log cause with eprintln! for non-zero exit codes?

Yes, I think there should be some kind of error message. However, if we're emitting errors or warnings or whatever anyway, then I don't think we need to emit an additional message.

src/bin/main.rs Outdated
} else if summary.has_diff {
// should only happen in diff mode
4
1
Copy link
Member

Choose a reason for hiding this comment

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

You could use || rather than having multiple else if branches here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, will update later today.

src/bin/main.rs Outdated
if let Some(mode) = options.write_mode {
error_summary.add_write_mode(mode)
}

Copy link
Member

Choose a reason for hiding this comment

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

I thought I suggested a refactoring to avoid saving the write mode in the error summary, but maybe that was on another PR since I can't find any trace of it.

I think that by moving some code around in the main function so that we know the write mode (i.e., process the opts outside of execute), then we don't need to save it into the summary.

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 thought I suggested a refactoring to avoid saving the write mode in the error summary, but maybe that was on another PR since I can't find any trace of it.

No worries.

I think that by moving some code around in the main function so that we know the write mode (i.e., process the opts outside of execute), then we don't need to save it into the summary.

I think that's a good idea. I wanted to introduce a minimally invasive change with this PR, but if exit codes are gonna change, so should this.

README.md Outdated

* `overwrite` Is the default and overwrites the original files _without_ creating backups.
* `replace` Overwrites the original files after creating backups of the files.
* `display` Will print the formatted files to stdout.
* `plain` Also writes to stdout, but with no metadata.
* `diff` Will print a diff between the original files and formatted files to stdout.
Will also exit with an error code if there are any differences.
* `check` Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
Copy link
Member

Choose a reason for hiding this comment

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

I would re-word, something like: "Checks if the program's formatting matches what rustfmt would do. Silently exits with code 0 if so, emits a diff and exits with code 1 if not. This option is designed to be run in CI-like where a non-zero exit signifies incorrect formatting."

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 like that; can do.

Copy link
Contributor Author

@davidbarsky davidbarsky Mar 25, 2018

Choose a reason for hiding this comment

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

Resolved.

@davidbarsky
Copy link
Contributor Author

Sorry about the slowness on my end. I've been on-call at work, but that's over. With the coming holiday, I should be able to devote some time to landing this issue. Anyways:

  • I updated the README.md as per @nrc's suggestion.
  • Made the exit-code handling less bad.
  • Moved WriteMode out of the summary, handling parsing in fn main() {...}

That being said, the way I'm handling WriteMode isn't great. I think if I'm to refactor how exit code handling and argument parsing are handled, I'd like to use StructOpt, but I'd like to be sure that it's okay I introduce this dependency. Thoughts?

@nrc
Copy link
Member

nrc commented Mar 28, 2018

I'd like to use StructOpt, but I'd like to be sure that it's okay I introduce this dependency. Thoughts?

How does using StructOpt help? I'm kind of OK (I like StructOpt), but worry it might be churn for no benefit.

@davidbarsky
Copy link
Contributor Author

How does using StructOpt help? I'm kind of OK (I like StructOpt), but worry it might be churn for no benefit.

I had some more time to think about it—it won't. Oops.

@nrc
Copy link
Member

nrc commented Apr 9, 2018

@davidbarsky How's it going on this? Got any questions I can answer?

@nrc nrc mentioned this pull request Apr 10, 2018
2 tasks
@davidbarsky
Copy link
Contributor Author

@nrc Hi Nick! Yes! Some guidance around changing fn execute. I'm thinking that, based off your suggestion above—quoted for reference:

I think that by moving some code around in the main function so that we know the write mode (i.e., process the opts outside of execute), then we don't need to save it into the summary.”

fn execute can be broken into a function that handles the parsing of getopts/reading of configuration files, and another function than does the the actual formatting process. The results of the command/config parsing can threaded through main so that the result of the attempted run of rustfmt can be associated with the user's command config, thereby not overloading fn execute or Summary with extraneous information. Does this seem reasonable?

(apologies for the delay.)

@nrc
Copy link
Member

nrc commented Apr 10, 2018

Does this seem reasonable?

Yes, I think so

@davidbarsky
Copy link
Contributor Author

Will need to rebase on mainline, but this I'm getting errors with rustc-ap-rustc_errors. Main thing—I'm not sure how I can make the handling of WriteMode/exit codes better even with refactoring. I'd like to avoid holding up further work/progress on this, I'll post what I've got. If this isn't what y'all are looking for, feel free to take this work and build atop of it/drop it.

# Conflicts:
#	Cargo.lock
#	src/bin/main.rs
@davidbarsky
Copy link
Contributor Author

Okay, rebased. Should be conflict-free.

@nrc
Copy link
Member

nrc commented Apr 19, 2018

This looks great, thank you!

@nrc nrc merged commit f9532ba into rust-lang:master Apr 19, 2018
nrc added a commit that referenced this pull request May 6, 2018
Update README.md to reflect change in #2539
bradjc added a commit to tock/tock that referenced this pull request May 18, 2018
Per rust-lang/rustfmt#2539 I think check
and diff do the same thing, except that check now returns a nonzero exit
code if there are changes that rustfmt would make.
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.

3 participants