11// High level formatting functions.
22
3+ use std:: cell:: RefCell ;
34use std:: collections:: HashMap ;
45use std:: io:: { self , Write } ;
56use std:: panic:: { catch_unwind, AssertUnwindSafe } ;
67use std:: rc:: Rc ;
78use std:: time:: { Duration , Instant } ;
89
910use syntax:: ast;
10- use syntax:: errors:: emitter:: { ColorConfig , Emitter } ;
11+ use syntax:: errors:: emitter:: { ColorConfig , Emitter , EmitterWriter } ;
1112use syntax:: errors:: { Diagnostic , DiagnosticBuilder , Handler } ;
1213use syntax:: parse:: { self , ParseSess } ;
1314use syntax:: source_map:: { FilePathMapping , SourceMap , Span , DUMMY_SP } ;
@@ -67,16 +68,22 @@ fn format_project<T: FormatHandler>(
6768 let input_is_stdin = main_file == FileName :: Stdin ;
6869
6970 let ignore_path_set = match IgnorePathSet :: from_ignore_list ( & config. ignore ( ) ) {
70- Ok ( set) => set,
71+ Ok ( set) => Rc :: new ( set) ,
7172 Err ( e) => return Err ( ErrorKind :: InvalidGlobPattern ( e) ) ,
7273 } ;
7374 if config. skip_children ( ) && ignore_path_set. is_match ( & main_file) {
7475 return Ok ( FormatReport :: new ( ) ) ;
7576 }
7677
7778 // Parse the crate.
79+ let can_reset_parser_errors = Rc :: new ( RefCell :: new ( false ) ) ;
7880 let source_map = Rc :: new ( SourceMap :: new ( FilePathMapping :: empty ( ) ) ) ;
79- let mut parse_session = make_parse_sess ( source_map. clone ( ) , config) ;
81+ let mut parse_session = make_parse_sess (
82+ source_map. clone ( ) ,
83+ config,
84+ Rc :: clone ( & ignore_path_set) ,
85+ can_reset_parser_errors. clone ( ) ,
86+ ) ;
8087 let mut report = FormatReport :: new ( ) ;
8188 let directory_ownership = input. to_directory_ownership ( ) ;
8289 let krate = match parse_crate (
@@ -85,6 +92,7 @@ fn format_project<T: FormatHandler>(
8592 config,
8693 & mut report,
8794 directory_ownership,
95+ can_reset_parser_errors. clone ( ) ,
8896 ) {
8997 Ok ( krate) => krate,
9098 // Surface parse error via Session (errors are merged there from report)
@@ -620,6 +628,7 @@ fn parse_crate(
620628 config : & Config ,
621629 report : & mut FormatReport ,
622630 directory_ownership : Option < parse:: DirectoryOwnership > ,
631+ can_reset_parser_errors : Rc < RefCell < bool > > ,
623632) -> Result < ast:: Crate , ErrorKind > {
624633 let input_is_stdin = input. is_text ( ) ;
625634
@@ -667,6 +676,15 @@ fn parse_crate(
667676 if !parse_session. span_diagnostic . has_errors ( ) {
668677 return Ok ( c) ;
669678 }
679+ // This scenario occurs when the parser encountered errors
680+ // but was still able to recover. If all of the parser errors
681+ // occurred in files that are ignored, then reset
682+ // the error count and continue.
683+ // https://github.com/rust-lang/rustfmt/issues/3779
684+ if * can_reset_parser_errors. borrow ( ) {
685+ parse_session. span_diagnostic . reset_err_count ( ) ;
686+ return Ok ( c) ;
687+ }
670688 }
671689 Ok ( Err ( mut diagnostics) ) => diagnostics. iter_mut ( ) . for_each ( DiagnosticBuilder :: emit) ,
672690 Err ( _) => {
@@ -683,6 +701,40 @@ fn parse_crate(
683701 Err ( ErrorKind :: ParseError )
684702}
685703
704+ struct SilentOnIgnoredFilesEmitter {
705+ ignore_path_set : Rc < IgnorePathSet > ,
706+ source_map : Rc < SourceMap > ,
707+ emitter : EmitterWriter ,
708+ has_non_ignorable_parser_errors : bool ,
709+ can_reset : Rc < RefCell < bool > > ,
710+ }
711+
712+ impl Emitter for SilentOnIgnoredFilesEmitter {
713+ fn emit_diagnostic ( & mut self , db : & Diagnostic ) {
714+ if let Some ( primary_span) = & db. span . primary_span ( ) {
715+ let file_name = self . source_map . span_to_filename ( * primary_span) ;
716+ match file_name {
717+ syntax_pos:: FileName :: Real ( ref path) => {
718+ if self
719+ . ignore_path_set
720+ . is_match ( & FileName :: Real ( path. to_path_buf ( ) ) )
721+ {
722+ if !self . has_non_ignorable_parser_errors {
723+ * self . can_reset . borrow_mut ( ) = true ;
724+ }
725+ return ;
726+ }
727+ }
728+ _ => ( ) ,
729+ } ;
730+ }
731+
732+ self . has_non_ignorable_parser_errors = true ;
733+ * self . can_reset . borrow_mut ( ) = false ;
734+ self . emitter . emit_diagnostic ( db) ;
735+ }
736+ }
737+
686738/// Emitter which discards every error.
687739struct SilentEmitter ;
688740
@@ -694,7 +746,12 @@ fn silent_emitter() -> Box<SilentEmitter> {
694746 Box :: new ( SilentEmitter { } )
695747}
696748
697- fn make_parse_sess ( source_map : Rc < SourceMap > , config : & Config ) -> ParseSess {
749+ fn make_parse_sess (
750+ source_map : Rc < SourceMap > ,
751+ config : & Config ,
752+ ignore_path_set : Rc < IgnorePathSet > ,
753+ can_reset : Rc < RefCell < bool > > ,
754+ ) -> ParseSess {
698755 let tty_handler = if config. hide_parse_errors ( ) {
699756 let silent_emitter = silent_emitter ( ) ;
700757 Handler :: with_emitter ( true , None , silent_emitter)
@@ -705,7 +762,23 @@ fn make_parse_sess(source_map: Rc<SourceMap>, config: &Config) -> ParseSess {
705762 } else {
706763 ColorConfig :: Never
707764 } ;
708- Handler :: with_tty_emitter ( color_cfg, true , None , Some ( source_map. clone ( ) ) )
765+
766+ let emitter_writer = EmitterWriter :: stderr (
767+ color_cfg,
768+ Some ( source_map. clone ( ) ) ,
769+ false ,
770+ false ,
771+ None ,
772+ false ,
773+ ) ;
774+ let emitter = Box :: new ( SilentOnIgnoredFilesEmitter {
775+ has_non_ignorable_parser_errors : false ,
776+ ignore_path_set : ignore_path_set,
777+ source_map : Rc :: clone ( & source_map) ,
778+ emitter : emitter_writer,
779+ can_reset,
780+ } ) ;
781+ Handler :: with_emitter ( true , None , emitter)
709782 } ;
710783
711784 ParseSess :: with_span_handler ( tty_handler, source_map)
0 commit comments