@@ -8,9 +8,10 @@ use lsp_types::{
88 WorkDoneProgressEnd , WorkDoneProgressReport ,
99} ;
1010use std:: {
11+ error, fmt,
1112 io:: { BufRead , BufReader } ,
1213 path:: { Path , PathBuf } ,
13- process:: { Child , Command , Stdio } ,
14+ process:: { Command , Stdio } ,
1415 thread:: JoinHandle ,
1516 time:: Instant ,
1617} ;
@@ -70,10 +71,10 @@ impl std::ops::Drop for CheckWatcher {
7071 fn drop ( & mut self ) {
7172 if let Some ( handle) = self . handle . take ( ) {
7273 // Take the sender out of the option
73- let recv = self . cmd_send . take ( ) ;
74+ let cmd_send = self . cmd_send . take ( ) ;
7475
7576 // Dropping the sender finishes the thread loop
76- drop ( recv ) ;
77+ drop ( cmd_send ) ;
7778
7879 // Join the thread, it should finish shortly. We don't really care
7980 // whether it panicked, so it is safe to ignore the result
@@ -246,11 +247,21 @@ enum CheckEvent {
246247 End ,
247248}
248249
250+ #[ derive( Debug ) ]
251+ pub struct CargoError ( String ) ;
252+
253+ impl fmt:: Display for CargoError {
254+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
255+ write ! ( f, "Cargo failed: {}" , self . 0 )
256+ }
257+ }
258+ impl error:: Error for CargoError { }
259+
249260pub fn run_cargo (
250261 args : & [ String ] ,
251262 current_dir : Option < & Path > ,
252263 on_message : & mut dyn FnMut ( cargo_metadata:: Message ) -> bool ,
253- ) -> Child {
264+ ) -> Result < ( ) , CargoError > {
254265 let mut command = Command :: new ( "cargo" ) ;
255266 if let Some ( current_dir) = current_dir {
256267 command. current_dir ( current_dir) ;
@@ -259,7 +270,7 @@ pub fn run_cargo(
259270 let mut child = command
260271 . args ( args)
261272 . stdout ( Stdio :: piped ( ) )
262- . stderr ( Stdio :: piped ( ) )
273+ . stderr ( Stdio :: null ( ) )
263274 . stdin ( Stdio :: null ( ) )
264275 . spawn ( )
265276 . expect ( "couldn't launch cargo" ) ;
@@ -273,6 +284,8 @@ pub fn run_cargo(
273284 // simply skip a line if it doesn't parse, which just ignores any
274285 // erroneus output.
275286 let stdout = BufReader :: new ( child. stdout . take ( ) . unwrap ( ) ) ;
287+ let mut read_at_least_one_message = false ;
288+
276289 for line in stdout. lines ( ) {
277290 let line = match line {
278291 Ok ( line) => line,
@@ -291,12 +304,27 @@ pub fn run_cargo(
291304 }
292305 } ;
293306
307+ read_at_least_one_message = true ;
308+
294309 if !on_message ( message) {
295310 break ;
296311 }
297312 }
298313
299- child
314+ // It is okay to ignore the result, as it only errors if the process is already dead
315+ let _ = child. kill ( ) ;
316+
317+ let err_msg = match child. wait ( ) {
318+ Ok ( exit_code) if !exit_code. success ( ) && !read_at_least_one_message => {
319+ // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
320+ // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
321+ format ! ( "the command produced no valid metadata:\n cargo {}" , args. join( " " ) )
322+ }
323+ Err ( err) => format ! ( "io error: {:?}" , err) ,
324+ Ok ( _) => return Ok ( ( ) ) ,
325+ } ;
326+
327+ Err ( CargoError ( err_msg) )
300328}
301329
302330impl WatchThread {
@@ -325,7 +353,7 @@ impl WatchThread {
325353 // which will break out of the loop, and continue the shutdown
326354 let _ = message_send. send ( CheckEvent :: Begin ) ;
327355
328- let mut child = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
356+ let res = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
329357 // Skip certain kinds of messages to only spend time on what's useful
330358 match & message {
331359 Message :: CompilerArtifact ( artifact) if artifact. fresh => return true ,
@@ -334,39 +362,19 @@ impl WatchThread {
334362 _ => { }
335363 }
336364
337- match message_send. send ( CheckEvent :: Msg ( message) ) {
338- Ok ( ( ) ) => { }
339- Err ( _err) => {
340- // The send channel was closed, so we want to shutdown
341- return false ;
342- }
343- } ;
344-
345- true
365+ // if the send channel was closed, so we want to shutdown
366+ message_send. send ( CheckEvent :: Msg ( message) ) . is_ok ( )
346367 } ) ;
347368
369+ if let Err ( err) = res {
370+ // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
371+ // to display user-caused misconfiguration errors instead of just logging them here
372+ log:: error!( "Cargo watcher failed {:?}" , err) ;
373+ }
374+
348375 // We can ignore any error here, as we are already in the progress
349376 // of shutting down.
350377 let _ = message_send. send ( CheckEvent :: End ) ;
351-
352- // It is okay to ignore the result, as it only errors if the process is already dead
353- let _ = child. kill ( ) ;
354-
355- // Again, we are resilient to errors, so we don't try to panic here
356- match child. wait_with_output ( ) {
357- Ok ( output) => match output. status . code ( ) {
358- Some ( 0 ) | None => { }
359- Some ( exit_code) => {
360- let output =
361- std:: str:: from_utf8 ( & output. stderr ) . unwrap_or ( "<bad utf8 output>" ) ;
362-
363- if !output. contains ( "could not compile" ) {
364- log:: error!( "Cargo failed with exit code {} {}" , exit_code, output) ;
365- }
366- }
367- } ,
368- Err ( err) => log:: error!( "Cargo io error: {:?}" , err) ,
369- }
370378 } ) )
371379 } else {
372380 None
0 commit comments