@@ -8,9 +8,10 @@ use lsp_types::{
8
8
WorkDoneProgressEnd , WorkDoneProgressReport ,
9
9
} ;
10
10
use std:: {
11
+ error, fmt,
11
12
io:: { BufRead , BufReader } ,
12
13
path:: { Path , PathBuf } ,
13
- process:: { Child , Command , Stdio } ,
14
+ process:: { Command , Stdio } ,
14
15
thread:: JoinHandle ,
15
16
time:: Instant ,
16
17
} ;
@@ -70,10 +71,10 @@ impl std::ops::Drop for CheckWatcher {
70
71
fn drop ( & mut self ) {
71
72
if let Some ( handle) = self . handle . take ( ) {
72
73
// Take the sender out of the option
73
- let recv = self . cmd_send . take ( ) ;
74
+ let cmd_send = self . cmd_send . take ( ) ;
74
75
75
76
// Dropping the sender finishes the thread loop
76
- drop ( recv ) ;
77
+ drop ( cmd_send ) ;
77
78
78
79
// Join the thread, it should finish shortly. We don't really care
79
80
// whether it panicked, so it is safe to ignore the result
@@ -246,11 +247,21 @@ enum CheckEvent {
246
247
End ,
247
248
}
248
249
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
+
249
260
pub fn run_cargo (
250
261
args : & [ String ] ,
251
262
current_dir : Option < & Path > ,
252
263
on_message : & mut dyn FnMut ( cargo_metadata:: Message ) -> bool ,
253
- ) -> Child {
264
+ ) -> Result < ( ) , CargoError > {
254
265
let mut command = Command :: new ( "cargo" ) ;
255
266
if let Some ( current_dir) = current_dir {
256
267
command. current_dir ( current_dir) ;
@@ -259,7 +270,7 @@ pub fn run_cargo(
259
270
let mut child = command
260
271
. args ( args)
261
272
. stdout ( Stdio :: piped ( ) )
262
- . stderr ( Stdio :: piped ( ) )
273
+ . stderr ( Stdio :: null ( ) )
263
274
. stdin ( Stdio :: null ( ) )
264
275
. spawn ( )
265
276
. expect ( "couldn't launch cargo" ) ;
@@ -273,6 +284,8 @@ pub fn run_cargo(
273
284
// simply skip a line if it doesn't parse, which just ignores any
274
285
// erroneus output.
275
286
let stdout = BufReader :: new ( child. stdout . take ( ) . unwrap ( ) ) ;
287
+ let mut read_at_least_one_message = false ;
288
+
276
289
for line in stdout. lines ( ) {
277
290
let line = match line {
278
291
Ok ( line) => line,
@@ -291,12 +304,27 @@ pub fn run_cargo(
291
304
}
292
305
} ;
293
306
307
+ read_at_least_one_message = true ;
308
+
294
309
if !on_message ( message) {
295
310
break ;
296
311
}
297
312
}
298
313
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) )
300
328
}
301
329
302
330
impl WatchThread {
@@ -325,7 +353,7 @@ impl WatchThread {
325
353
// which will break out of the loop, and continue the shutdown
326
354
let _ = message_send. send ( CheckEvent :: Begin ) ;
327
355
328
- let mut child = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
356
+ let res = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
329
357
// Skip certain kinds of messages to only spend time on what's useful
330
358
match & message {
331
359
Message :: CompilerArtifact ( artifact) if artifact. fresh => return true ,
@@ -334,39 +362,19 @@ impl WatchThread {
334
362
_ => { }
335
363
}
336
364
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 ( )
346
367
} ) ;
347
368
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
+
348
375
// We can ignore any error here, as we are already in the progress
349
376
// of shutting down.
350
377
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
- }
370
378
} ) )
371
379
} else {
372
380
None
0 commit comments