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

don't fail on recoverable parser errors in ignored files #3782

Merged
merged 9 commits into from
Oct 7, 2019

Conversation

calebcartwright
Copy link
Member

@calebcartwright calebcartwright commented Sep 6, 2019

Resolves #3779 and refs #3777

This updates how rustfmt handles parser errors when the parser was able to recover from all the errors, and all those errors came from ignored files:

  1. Don't emit recovered parser error text for ignored files
  2. Don't fail rustfmt when the only parse errors originated in ignored files and were recovered by the parser

This was implemented via a custom Emitter that checks each of the parser errors as they roll through, and keeps track of whether all of the parser errors originated from ignored files. If the parser encountered errors, but was able to recover from them all, then that indicator is checked to determine if the error count can be reset.

AFAICT it is necessary to track that status within the emitter as neither the Parser, ParseSess, nor Handler publically expose details about the parser errors encountered; only a true/false flag indicating whether any errors were encountered.

I also investigated whether we could cancel/remove parser errors that met the ignore file condition from within the emitter, but this was not viable either as a mutable reference is required to be able to cancel the DiagnosticBuilder, reseting the DB's Handler error count from the emitter does not remove the error from the parser (see below #3782 (comment) for why this is the case), etc.

@calebcartwright calebcartwright changed the title don't fail with non-zero exit on a parse error in ignored file [WIP] don't fail with non-zero exit on a parse error in ignored file Sep 6, 2019
@calebcartwright calebcartwright force-pushed the issue-3779 branch 2 times, most recently from 51d1b41 to 0765170 Compare September 6, 2019 04:25
@calebcartwright calebcartwright changed the title [WIP] don't fail with non-zero exit on a parse error in ignored file [WIP] don't error on recoverable parser errors in ignored files Sep 8, 2019
@calebcartwright calebcartwright changed the title [WIP] don't error on recoverable parser errors in ignored files don't fail on recoverable parser errors in ignored files Sep 8, 2019
Handler::with_tty_emitter(color_cfg, true, None, Some(source_map.clone()))

let emitter_writer =
EmitterWriter::stderr(color_cfg, Some(source_map.clone()), false, false, None);
Copy link
Member Author

Choose a reason for hiding this comment

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

this is the emitter that Handler::with_tty_emitter creates internally, so creating it here and including it in the custom Emitter info so that parser errors that occur in non-ignored files are still displayed as usual

@calebcartwright calebcartwright changed the title don't fail on recoverable parser errors in ignored files [WIP] don't fail on recoverable parser errors in ignored files Sep 10, 2019
@calebcartwright calebcartwright changed the title [WIP] don't fail on recoverable parser errors in ignored files don't fail on recoverable parser errors in ignored files Sep 10, 2019
src/formatting.rs Outdated Show resolved Hide resolved
src/formatting.rs Outdated Show resolved Hide resolved
ignore_path_set: Rc<IgnorePathSet>,
source_map: Rc<SourceMap>,
emitter: EmitterWriter,
can_reset: bool,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind explaining the purpose of can_reset? I could not see what this field is used for.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure! I was trying to avoid having to do a borrow_mut and update the value unless absolutely necessary, and that was accomplished via both can_reset and has_non_ignorable_parser_errors. If there was only has_non_ignorable_parser_errors, then there could be a lot of repetitive borrow_mut setting the value to true over and over.

I'm not sure if/how problematic that would be, but that was my thought process anyway. I could add some inline comments to explain that, or I could remove it altogether if you'd prefer

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, so is can_reset used to avoid the overhead of calling borrow_mut multiple times? If that is the case, IMHO, this seems premature optimization to me. RefMut::borrow_mut is quite cheap (some non-atomic counter operations), and the code path is not hot anyway (it's only run on parse error).

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to remove can_reset and has_non_ignorable_parser_errors without changing the semantic? If possible, then I prefer to remove them to keep the code simple.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, so is can_reset used to avoid the overhead of calling borrow_mut multiple times?

Basically yes, and also because I wasn't sure if there'd ever be any competing borrow attempts which I'd read would panic. Sounds like there's no issues there so I'll remove that 👍

Is it possible to remove can_reset and has_non_ignorable_parser_errors without changing the semantic?

I can remove one of them, but AFAICT we'll need at least one flag to indicate whether parser errors from non-ignored files were encountered to ensure that we still fail in such a scenario. For example, if there are two files that have parser errors, foo.rs and bar.rs, and bar.rs is ignored, and foo is processed first and then bar. We should still fail due to the parser error in foo.rs

Copy link
Member Author

@calebcartwright calebcartwright Sep 25, 2019

Choose a reason for hiding this comment

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

I've updated this to only use the one flag. The only way I could see to remove them both would be to set the top level reset value to true, and then updating the emitter logic.

let can_reset_parser_errors = Rc::new(RefCell::new(false));

However, I don't think that the default behavior should be to ignore/reset parse errors encountered while parsing the crate. I fear that could result in spurious ignores of parser errors which would later cause problems when trying to parse/format the modules.

IMO it's better to have the emitter keep track of whether it saw a parser error from a non-ignored file via a flag, but please let me know if you'd prefer I change to the other approach!

src/formatting.rs Outdated Show resolved Hide resolved
src/formatting.rs Outdated Show resolved Hide resolved
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.

Sorry for the late review. The code looks good to me.

For further refactoring, we better start with updating the libsyntax to expose its internal data structures.

.is_match(&FileName::Real(path.to_path_buf()))
{
if !self.has_non_ignorable_parser_errors {
*self.can_reset.borrow_mut() = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

I first thought that we could simply call db.handler.reset_err_count(); here instead, but looks like the rustc parser bumps the error count after calling emit_diagnostic:

https://github.com/rust-lang/rust/blob/f2023ac599c38a59f86552089e6791c5a73412d3/src/librustc_errors/lib.rs#L781-L791

Also like you mentioned, resetting the error count may cause skipping important parsing errors.

Copy link
Member Author

Choose a reason for hiding this comment

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

I first thought that we could simply call db.handler.reset_err_count();

That's what I was expecting originally too 👍

here instead, but looks like the rustc parser bumps the error count after calling emit_diagnostic:

Thank you so much for finding that! I was baffled how calling reset_err_count() inside emit_diagnostic seemed to be doing nothing, but that makes much more sense now

@topecongiro
Copy link
Contributor

@calebcartwright Working on #3835, I found that there were some refactorings around rustc_errors and breaking changes to its API. As #3835 is required to fix the broken build on the latest nightly, I am going to merge it first, but that would likely cause conflicts with this PR. Sorry for the inconvenience.

@calebcartwright
Copy link
Member Author

@topecongiro - no worries! I'll update this to address any merge conflicts afterwards

@calebcartwright
Copy link
Member Author

calebcartwright commented Oct 5, 2019

Fortunately looks like the merge conflicts are fairly minimal. I need to grab nightly-2019-10-05-x86_64-unknown-linux-gnu to make sure this is still working as expected since there were changes in rustc_errors. Will push the merge resolution after 👍

@topecongiro topecongiro merged commit ba4bf03 into rust-lang:master Oct 7, 2019
@topecongiro topecongiro added this to the 1.4.9 milestone Oct 7, 2019
@calebcartwright calebcartwright deleted the issue-3779 branch October 7, 2019 01:24
@topecongiro
Copy link
Contributor

Perfect, thank you for all the work 🎉

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.

gracefully handle recoverable parser errors in ignored files
2 participants