1
1
// High level formatting functions.
2
2
3
+ use std:: cell:: RefCell ;
3
4
use std:: collections:: HashMap ;
4
5
use std:: io:: { self , Write } ;
5
6
use std:: panic:: { catch_unwind, AssertUnwindSafe } ;
6
7
use std:: rc:: Rc ;
7
8
use std:: time:: { Duration , Instant } ;
8
9
9
10
use syntax:: ast;
10
- use syntax:: errors:: emitter:: { ColorConfig , Emitter } ;
11
+ use syntax:: errors:: emitter:: { ColorConfig , Emitter , EmitterWriter } ;
11
12
use syntax:: errors:: { Diagnostic , DiagnosticBuilder , Handler } ;
12
13
use syntax:: parse:: { self , ParseSess } ;
13
14
use syntax:: source_map:: { FilePathMapping , SourceMap , Span , DUMMY_SP } ;
@@ -67,16 +68,22 @@ fn format_project<T: FormatHandler>(
67
68
let input_is_stdin = main_file == FileName :: Stdin ;
68
69
69
70
let ignore_path_set = match IgnorePathSet :: from_ignore_list ( & config. ignore ( ) ) {
70
- Ok ( set) => set,
71
+ Ok ( set) => Rc :: new ( set) ,
71
72
Err ( e) => return Err ( ErrorKind :: InvalidGlobPattern ( e) ) ,
72
73
} ;
73
74
if config. skip_children ( ) && ignore_path_set. is_match ( & main_file) {
74
75
return Ok ( FormatReport :: new ( ) ) ;
75
76
}
76
77
77
78
// Parse the crate.
79
+ let can_reset_parser_errors = Rc :: new ( RefCell :: new ( false ) ) ;
78
80
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
+ ) ;
80
87
let mut report = FormatReport :: new ( ) ;
81
88
let directory_ownership = input. to_directory_ownership ( ) ;
82
89
let krate = match parse_crate (
@@ -85,6 +92,7 @@ fn format_project<T: FormatHandler>(
85
92
config,
86
93
& mut report,
87
94
directory_ownership,
95
+ can_reset_parser_errors. clone ( ) ,
88
96
) {
89
97
Ok ( krate) => krate,
90
98
// Surface parse error via Session (errors are merged there from report)
@@ -620,6 +628,7 @@ fn parse_crate(
620
628
config : & Config ,
621
629
report : & mut FormatReport ,
622
630
directory_ownership : Option < parse:: DirectoryOwnership > ,
631
+ can_reset_parser_errors : Rc < RefCell < bool > > ,
623
632
) -> Result < ast:: Crate , ErrorKind > {
624
633
let input_is_stdin = input. is_text ( ) ;
625
634
@@ -667,6 +676,15 @@ fn parse_crate(
667
676
if !parse_session. span_diagnostic . has_errors ( ) {
668
677
return Ok ( c) ;
669
678
}
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
+ }
670
688
}
671
689
Ok ( Err ( mut diagnostics) ) => diagnostics. iter_mut ( ) . for_each ( DiagnosticBuilder :: emit) ,
672
690
Err ( _) => {
@@ -683,6 +701,40 @@ fn parse_crate(
683
701
Err ( ErrorKind :: ParseError )
684
702
}
685
703
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
+
686
738
/// Emitter which discards every error.
687
739
struct SilentEmitter ;
688
740
@@ -694,7 +746,12 @@ fn silent_emitter() -> Box<SilentEmitter> {
694
746
Box :: new ( SilentEmitter { } )
695
747
}
696
748
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 {
698
755
let tty_handler = if config. hide_parse_errors ( ) {
699
756
let silent_emitter = silent_emitter ( ) ;
700
757
Handler :: with_emitter ( true , None , silent_emitter)
@@ -705,7 +762,23 @@ fn make_parse_sess(source_map: Rc<SourceMap>, config: &Config) -> ParseSess {
705
762
} else {
706
763
ColorConfig :: Never
707
764
} ;
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)
709
782
} ;
710
783
711
784
ParseSess :: with_span_handler ( tty_handler, source_map)
0 commit comments