1 //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`,
2 //! this allows [`crate::driver`] to be run for every dependency. The driver connects to
3 //! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running
4 //! clippy on the crate to the server
6 use crate::ClippyWarning;
7 use crate::RecursiveOptions;
9 use std::collections::HashSet;
10 use std::io::{BufRead, BufReader, Read, Write};
11 use std::net::{SocketAddr, TcpListener, TcpStream};
12 use std::sync::{Arc, Mutex};
15 use cargo_metadata::diagnostic::Diagnostic;
16 use crossbeam_channel::{Receiver, Sender};
17 use serde::de::DeserializeOwned;
18 use serde::{Deserialize, Serialize};
20 #[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)]
21 pub(crate) struct DriverInfo {
22 pub package_name: String,
23 pub crate_name: String,
27 pub(crate) fn serialize_line<T, W>(value: &T, writer: &mut W)
32 let mut buf = serde_json::to_vec(&value).expect("failed to serialize");
34 writer.write_all(&buf).expect("write_all failed");
37 pub(crate) fn deserialize_line<T, R>(reader: &mut R) -> T
42 let mut string = String::new();
43 reader.read_line(&mut string).expect("read_line failed");
44 serde_json::from_str(&string).expect("failed to deserialize")
49 sender: &Sender<ClippyWarning>,
50 options: &RecursiveOptions,
51 seen: &Mutex<HashSet<DriverInfo>>,
53 let mut stream = BufReader::new(stream);
55 let driver_info: DriverInfo = deserialize_line(&mut stream);
57 let unseen = seen.lock().unwrap().insert(driver_info.clone());
58 let ignored = options.ignore.contains(&driver_info.package_name);
59 let should_run = unseen && !ignored;
61 serialize_line(&should_run, stream.get_mut());
63 let mut stderr = String::new();
64 stream.read_to_string(&mut stderr).unwrap();
68 .filter_map(|json_msg| serde_json::from_str::<Diagnostic>(json_msg).ok())
69 .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version));
71 for message in messages {
72 sender.send(message).unwrap();
76 pub(crate) struct LintcheckServer {
77 pub local_addr: SocketAddr,
78 receiver: Receiver<ClippyWarning>,
79 sender: Arc<Sender<ClippyWarning>>,
82 impl LintcheckServer {
83 pub fn spawn(options: RecursiveOptions) -> Self {
84 let listener = TcpListener::bind("localhost:0").unwrap();
85 let local_addr = listener.local_addr().unwrap();
87 let (sender, receiver) = crossbeam_channel::unbounded::<ClippyWarning>();
88 let sender = Arc::new(sender);
89 // The spawned threads hold a `Weak<Sender>` so that they don't keep the channel connected
91 let sender_weak = Arc::downgrade(&sender);
93 // Ignore dependencies multiple times, e.g. for when it's both checked and compiled for a
95 let seen = Mutex::default();
97 thread::spawn(move || {
100 while let Ok((stream, _)) = listener.accept() {
101 let sender = sender_weak.upgrade().expect("received connection after server closed");
102 let options = &options;
104 s.spawn(move || process_stream(stream, &sender, options, seen));
117 pub fn warnings(self) -> impl Iterator<Item = ClippyWarning> {
118 // causes the channel to become disconnected so that the receiver iterator ends
121 self.receiver.into_iter()