]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/lintcheck/src/recursive.rs
Rollup merge of #104581 - notriddle:notriddle/js-iife-2, r=GuillaumeGomez
[rust.git] / src / tools / clippy / lintcheck / src / recursive.rs
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
5
6 use crate::ClippyWarning;
7 use crate::RecursiveOptions;
8
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};
13 use std::thread;
14
15 use cargo_metadata::diagnostic::Diagnostic;
16 use crossbeam_channel::{Receiver, Sender};
17 use serde::de::DeserializeOwned;
18 use serde::{Deserialize, Serialize};
19
20 #[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)]
21 pub(crate) struct DriverInfo {
22     pub package_name: String,
23     pub crate_name: String,
24     pub version: String,
25 }
26
27 pub(crate) fn serialize_line<T, W>(value: &T, writer: &mut W)
28 where
29     T: Serialize,
30     W: Write,
31 {
32     let mut buf = serde_json::to_vec(&value).expect("failed to serialize");
33     buf.push(b'\n');
34     writer.write_all(&buf).expect("write_all failed");
35 }
36
37 pub(crate) fn deserialize_line<T, R>(reader: &mut R) -> T
38 where
39     T: DeserializeOwned,
40     R: BufRead,
41 {
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")
45 }
46
47 fn process_stream(
48     stream: TcpStream,
49     sender: &Sender<ClippyWarning>,
50     options: &RecursiveOptions,
51     seen: &Mutex<HashSet<DriverInfo>>,
52 ) {
53     let mut stream = BufReader::new(stream);
54
55     let driver_info: DriverInfo = deserialize_line(&mut stream);
56
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;
60
61     serialize_line(&should_run, stream.get_mut());
62
63     let mut stderr = String::new();
64     stream.read_to_string(&mut stderr).unwrap();
65
66     let messages = stderr
67         .lines()
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));
70
71     for message in messages {
72         sender.send(message).unwrap();
73     }
74 }
75
76 pub(crate) struct LintcheckServer {
77     pub local_addr: SocketAddr,
78     receiver: Receiver<ClippyWarning>,
79     sender: Arc<Sender<ClippyWarning>>,
80 }
81
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();
86
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
90         // indefinitely
91         let sender_weak = Arc::downgrade(&sender);
92
93         // Ignore dependencies multiple times, e.g. for when it's both checked and compiled for a
94         // build dependency
95         let seen = Mutex::default();
96
97         thread::spawn(move || {
98             thread::scope(|s| {
99                 s.spawn(|| {
100                     while let Ok((stream, _)) = listener.accept() {
101                         let sender = sender_weak.upgrade().expect("received connection after server closed");
102                         let options = &options;
103                         let seen = &seen;
104                         s.spawn(move || process_stream(stream, &sender, options, seen));
105                     }
106                 });
107             });
108         });
109
110         Self {
111             local_addr,
112             receiver,
113             sender,
114         }
115     }
116
117     pub fn warnings(self) -> impl Iterator<Item = ClippyWarning> {
118         // causes the channel to become disconnected so that the receiver iterator ends
119         drop(self.sender);
120
121         self.receiver.into_iter()
122     }
123 }