4949//! The current scheduling algorithm is relatively primitive and could likely be 
5050//! improved. 
5151
52- use  std:: cell:: Cell ; 
52+ use  std:: cell:: { Cell ,   RefCell } ; 
5353use  std:: collections:: { BTreeMap ,  BTreeSet ,  HashMap ,  HashSet } ; 
54+ use  std:: fmt:: Write  as  _; 
5455use  std:: io; 
5556use  std:: marker; 
5657use  std:: sync:: Arc ; 
@@ -125,6 +126,14 @@ struct DrainState<'cfg> {
125126
126127    queue :  DependencyQueue < Unit ,  Artifact ,  Job > , 
127128    messages :  Arc < Queue < Message > > , 
129+     /// Diagnostic deduplication support. 
130+      diag_dedupe :  DiagDedupe < ' cfg > , 
131+     /// Count of warnings, used to print a summary after the job succeeds. 
132+      /// 
133+      /// First value is the total number of warnings, and the second value is 
134+      /// the number that were suppressed because they were duplicates of a 
135+      /// previous warning. 
136+      warning_count :  HashMap < JobId ,  ( usize ,  usize ) > , 
128137    active :  HashMap < JobId ,  Unit > , 
129138    compiled :  HashSet < PackageId > , 
130139    documented :  HashSet < PackageId > , 
@@ -174,7 +183,7 @@ impl std::fmt::Display for JobId {
174183/// 
175184/// The job may execute on either a dedicated thread or the main thread. If the job executes on the 
176185/// main thread, the `output` field must be set to prevent a deadlock. 
177- pub  struct  JobState < ' a >  { 
186+ pub  struct  JobState < ' a ,   ' cfg >  { 
178187    /// Channel back to the main thread to coordinate messages and such. 
179188     /// 
180189     /// When the `output` field is `Some`, care must be taken to avoid calling `push_bounded` on 
@@ -191,7 +200,7 @@ pub struct JobState<'a> {
191200     /// interleaved. In the future, it may be wrapped in a `Mutex` instead. In this case 
192201     /// interleaving is still prevented as the lock would be held for the whole printing of an 
193202     /// output message. 
194-      output :  Option < & ' a  Config > , 
203+      output :  Option < & ' a  DiagDedupe < ' cfg > > , 
195204
196205    /// The job id that this state is associated with, used when sending 
197206     /// messages back to the main thread. 
@@ -207,6 +216,36 @@ pub struct JobState<'a> {
207216    _marker :  marker:: PhantomData < & ' a  ( ) > , 
208217} 
209218
219+ /// Handler for deduplicating diagnostics. 
220+ struct  DiagDedupe < ' cfg >  { 
221+     seen :  RefCell < HashSet < u64 > > , 
222+     config :  & ' cfg  Config , 
223+ } 
224+ 
225+ impl < ' cfg >  DiagDedupe < ' cfg >  { 
226+     fn  new ( config :  & ' cfg  Config )  -> Self  { 
227+         DiagDedupe  { 
228+             seen :  RefCell :: new ( HashSet :: new ( ) ) , 
229+             config, 
230+         } 
231+     } 
232+ 
233+     /// Emits a diagnostic message. 
234+      /// 
235+      /// Returns `true` if the message was emitted, or `false` if it was 
236+      /// suppressed for being a duplicate. 
237+      fn  emit_diag ( & self ,  diag :  & str )  -> CargoResult < bool >  { 
238+         let  h = util:: hash_u64 ( diag) ; 
239+         if  !self . seen . borrow_mut ( ) . insert ( h)  { 
240+             return  Ok ( false ) ; 
241+         } 
242+         let  mut  shell = self . config . shell ( ) ; 
243+         shell. print_ansi_stderr ( diag. as_bytes ( ) ) ?; 
244+         shell. err ( ) . write_all ( b"\n " ) ?; 
245+         Ok ( true ) 
246+     } 
247+ } 
248+ 
210249/// Possible artifacts that can be produced by compilations, used as edge values 
211250/// in the dependency graph. 
212251/// 
@@ -232,6 +271,15 @@ enum Message {
232271    BuildPlanMsg ( String ,  ProcessBuilder ,  Arc < Vec < OutputFile > > ) , 
233272    Stdout ( String ) , 
234273    Stderr ( String ) , 
274+     Diagnostic  { 
275+         id :  JobId , 
276+         level :  String , 
277+         diag :  String , 
278+     } , 
279+     WarningCount  { 
280+         id :  JobId , 
281+         emitted :  bool , 
282+     } , 
235283    FixDiagnostic ( diagnostic_server:: Message ) , 
236284    Token ( io:: Result < Acquired > ) , 
237285    Finish ( JobId ,  Artifact ,  CargoResult < ( ) > ) , 
@@ -244,7 +292,7 @@ enum Message {
244292    ReleaseToken ( JobId ) , 
245293} 
246294
247- impl < ' a >  JobState < ' a >  { 
295+ impl < ' a ,   ' cfg >  JobState < ' a ,   ' cfg >  { 
248296    pub  fn  running ( & self ,  cmd :  & ProcessBuilder )  { 
249297        self . messages . push ( Message :: Run ( self . id ,  cmd. to_string ( ) ) ) ; 
250298    } 
@@ -260,17 +308,17 @@ impl<'a> JobState<'a> {
260308    } 
261309
262310    pub  fn  stdout ( & self ,  stdout :  String )  -> CargoResult < ( ) >  { 
263-         if  let  Some ( config )  = self . output  { 
264-             writeln ! ( config. shell( ) . out( ) ,  "{}" ,  stdout) ?; 
311+         if  let  Some ( dedupe )  = self . output  { 
312+             writeln ! ( dedupe . config. shell( ) . out( ) ,  "{}" ,  stdout) ?; 
265313        }  else  { 
266314            self . messages . push_bounded ( Message :: Stdout ( stdout) ) ; 
267315        } 
268316        Ok ( ( ) ) 
269317    } 
270318
271319    pub  fn  stderr ( & self ,  stderr :  String )  -> CargoResult < ( ) >  { 
272-         if  let  Some ( config )  = self . output  { 
273-             let  mut  shell = config. shell ( ) ; 
320+         if  let  Some ( dedupe )  = self . output  { 
321+             let  mut  shell = dedupe . config . shell ( ) ; 
274322            shell. print_ansi_stderr ( stderr. as_bytes ( ) ) ?; 
275323            shell. err ( ) . write_all ( b"\n " ) ?; 
276324        }  else  { 
@@ -279,6 +327,25 @@ impl<'a> JobState<'a> {
279327        Ok ( ( ) ) 
280328    } 
281329
330+     pub  fn  emit_diag ( & self ,  level :  String ,  diag :  String )  -> CargoResult < ( ) >  { 
331+         if  let  Some ( dedupe)  = self . output  { 
332+             let  emitted = dedupe. emit_diag ( & diag) ?; 
333+             if  level == "warning"  { 
334+                 self . messages . push ( Message :: WarningCount  { 
335+                     id :  self . id , 
336+                     emitted, 
337+                 } ) ; 
338+             } 
339+         }  else  { 
340+             self . messages . push_bounded ( Message :: Diagnostic  { 
341+                 id :  self . id , 
342+                 level, 
343+                 diag, 
344+             } ) ; 
345+         } 
346+         Ok ( ( ) ) 
347+     } 
348+ 
282349    /// A method used to signal to the coordinator thread that the rmeta file 
283350     /// for an rlib has been produced. This is only called for some rmeta 
284351     /// builds when required, and can be called at any time before a job ends. 
@@ -410,6 +477,8 @@ impl<'cfg> JobQueue<'cfg> {
410477            // typical messages. If you change this, please update the test 
411478            // caching_large_output, too. 
412479            messages :  Arc :: new ( Queue :: new ( 100 ) ) , 
480+             diag_dedupe :  DiagDedupe :: new ( cx. bcx . config ) , 
481+             warning_count :  HashMap :: new ( ) , 
413482            active :  HashMap :: new ( ) , 
414483            compiled :  HashSet :: new ( ) , 
415484            documented :  HashSet :: new ( ) , 
@@ -563,6 +632,15 @@ impl<'cfg> DrainState<'cfg> {
563632                shell. print_ansi_stderr ( err. as_bytes ( ) ) ?; 
564633                shell. err ( ) . write_all ( b"\n " ) ?; 
565634            } 
635+             Message :: Diagnostic  {  id,  level,  diag }  => { 
636+                 let  emitted = self . diag_dedupe . emit_diag ( & diag) ?; 
637+                 if  level == "warning"  { 
638+                     self . bump_warning_count ( id,  emitted) ; 
639+                 } 
640+             } 
641+             Message :: WarningCount  {  id,  emitted }  => { 
642+                 self . bump_warning_count ( id,  emitted) ; 
643+             } 
566644            Message :: FixDiagnostic ( msg)  => { 
567645                self . print . print ( & msg) ?; 
568646            } 
@@ -586,6 +664,7 @@ impl<'cfg> DrainState<'cfg> {
586664                            self . tokens . extend ( rustc_tokens) ; 
587665                        } 
588666                        self . to_send_clients . remove ( & id) ; 
667+                         self . report_warning_count ( cx. bcx . config ,  id) ; 
589668                        self . active . remove ( & id) . unwrap ( ) 
590669                    } 
591670                    // ... otherwise if it hasn't finished we leave it 
@@ -936,7 +1015,7 @@ impl<'cfg> DrainState<'cfg> {
9361015        let  fresh = job. freshness ( ) ; 
9371016        let  rmeta_required = cx. rmeta_required ( unit) ; 
9381017
939-         let  doit = move  |state :  JobState < ' _ > | { 
1018+         let  doit = move  |state :  JobState < ' _ ,   ' _ > | { 
9401019            let  mut  sender = FinishOnDrop  { 
9411020                messages :  & state. messages , 
9421021                id, 
@@ -992,7 +1071,7 @@ impl<'cfg> DrainState<'cfg> {
9921071                doit ( JobState  { 
9931072                    id, 
9941073                    messages, 
995-                     output :  Some ( cx . bcx . config ) , 
1074+                     output :  Some ( & self . diag_dedupe ) , 
9961075                    rmeta_required :  Cell :: new ( rmeta_required) , 
9971076                    _marker :  marker:: PhantomData , 
9981077                } ) ; 
@@ -1044,6 +1123,44 @@ impl<'cfg> DrainState<'cfg> {
10441123        Ok ( ( ) ) 
10451124    } 
10461125
1126+     fn  bump_warning_count ( & mut  self ,  id :  JobId ,  emitted :  bool )  { 
1127+         let  cnts = self . warning_count . entry ( id) . or_default ( ) ; 
1128+         cnts. 0  += 1 ; 
1129+         if  !emitted { 
1130+             cnts. 1  += 1 ; 
1131+         } 
1132+     } 
1133+ 
1134+     /// Displays a final report of the warnings emitted by a particular job. 
1135+      fn  report_warning_count ( & mut  self ,  config :  & Config ,  id :  JobId )  { 
1136+         let  count = match  self . warning_count . remove ( & id)  { 
1137+             Some ( count)  => count, 
1138+             None  => return , 
1139+         } ; 
1140+         let  unit = & self . active [ & id] ; 
1141+         let  mut  message = format ! ( "`{}` ({}" ,  unit. pkg. name( ) ,  unit. target. description_named( ) ) ; 
1142+         if  unit. mode . is_rustc_test ( )  && !( unit. target . is_test ( )  || unit. target . is_bench ( ) )  { 
1143+             message. push_str ( " test" ) ; 
1144+         }  else  if  unit. mode . is_doc_test ( )  { 
1145+             message. push_str ( " doctest" ) ; 
1146+         }  else  if  unit. mode . is_doc ( )  { 
1147+             message. push_str ( " doc" ) ; 
1148+         } 
1149+         message. push_str ( ") generated " ) ; 
1150+         match  count. 0  { 
1151+             1  => message. push_str ( "1 warning" ) , 
1152+             n => drop ( write ! ( message,  "{} warnings" ,  n) ) , 
1153+         } ; 
1154+         match  count. 1  { 
1155+             0  => { } 
1156+             1  => message. push_str ( " (1 duplicate)" ) , 
1157+             n => drop ( write ! ( message,  " ({} duplicates)" ,  n) ) , 
1158+         } 
1159+         // Errors are ignored here because it is tricky to handle them 
1160+         // correctly, and they aren't important. 
1161+         drop ( config. shell ( ) . warn ( message) ) ; 
1162+     } 
1163+ 
10471164    fn  finish ( 
10481165        & mut  self , 
10491166        id :  JobId , 
0 commit comments