bevy_lint/
callback.rs

1//! The [`BevyLintCallback`] definition and supporting code.
2
3use std::sync::atomic::{AtomicPtr, Ordering};
4
5use rustc_driver::Callbacks;
6use rustc_interface::interface::Config;
7use rustc_lint_defs::RegisteredTools;
8use rustc_middle::ty::TyCtxt;
9use rustc_session::utils::was_invoked_from_cargo;
10use rustc_span::{Ident, Symbol};
11
12/// A pointer to the original [`registered_tools()`](TyCtxt::registered_tools) query function.
13///
14/// # Safety
15///
16/// This pointer must be of the type [`fn(TyCtxt<'tcx>, ()) -> RegisteredTools`](fn).
17static ORIGINAL_REGISTERED_TOOLS: AtomicPtr<()> = {
18    fn default(_: TyCtxt<'_>, _: ()) -> RegisteredTools {
19        unreachable!("This function will be overwritten when `BevyLintCallback::config()` is run.");
20    }
21
22    AtomicPtr::new(default as *mut ())
23};
24
25/// The `rustc` [`Callbacks`] that register Bevy's lints.
26pub struct BevyLintCallback;
27
28impl Callbacks for BevyLintCallback {
29    fn config(&mut self, config: &mut Config) {
30        crate::config::load_config(config);
31
32        // Add `--cfg bevy_lint` so programs can conditionally configure lints.
33        config.crate_cfg.push("bevy_lint".to_string());
34
35        // We're overwriting `register_lints`, but we don't want to completely delete the original
36        // function. Instead, we save it so we can call it ourselves inside its replacement.
37        let previous = config.register_lints.take();
38
39        config.register_lints = Some(Box::new(move |session, store| {
40            // If there was a previous `register_lints`, call it first.
41            if let Some(previous) = &previous {
42                (previous)(session, store);
43            }
44
45            crate::lints::register_lints(store);
46            crate::lints::register_passes(store);
47            crate::lints::register_groups(store);
48        }));
49
50        config.override_queries = Some(|_session, providers| {
51            // Save the original query so we can access it later.
52            ORIGINAL_REGISTERED_TOOLS.store(
53                providers.queries.registered_tools as *mut (),
54                Ordering::Relaxed,
55            );
56
57            // Overwrite the query with our own custom version.
58            providers.queries.registered_tools = registered_tools;
59        });
60
61        // Clone the input so we can `move` it into our custom `psess_created()`.
62        let input = config.input.clone();
63
64        config.psess_created = Some(Box::new(move |psess| {
65            if !was_invoked_from_cargo() {
66                return;
67            }
68
69            let file_depinfo = psess.file_depinfo.get_mut();
70
71            for workspace in [false, true] {
72                // Get the paths to the crate or workspace `Cargo.toml`, if they exist.
73                let manifest_path = crate::utils::cargo::locate_manifest(&input, workspace);
74
75                // If locating the manifest was successful, try to convert the path into a UTF-8
76                // string that we can intern.
77                if let Ok(path) = manifest_path
78                    && let Some(path) = path.to_str()
79                {
80                    // Insert the manifest path into `file_depinfo`. Now if the manifest is
81                    // changed, checks will re-run.
82                    file_depinfo.insert(Symbol::intern(path));
83                }
84            }
85        }));
86    }
87}
88
89/// A custom version of the [`registered_tools()`](TyCtxt::registered_tools) query that
90/// automatically adds "bevy" as a tool.
91fn registered_tools<'tcx>(tcx: TyCtxt<'tcx>, _: ()) -> RegisteredTools {
92    // Fetch the original version of the query.
93    //
94    // SAFETY: The pointer is guaranteed to be a `fn(TyCtxt<'tcx>, ()) -> RegisteredTools` as per
95    // `ORIGINAL_REGISTERED_TOOLS`'s safety docs.
96    let original: fn(TyCtxt<'tcx>, ()) -> RegisteredTools =
97        unsafe { std::mem::transmute(ORIGINAL_REGISTERED_TOOLS.load(Ordering::Relaxed)) };
98
99    let mut tools = (original)(tcx, ());
100
101    tools.insert(Ident::from_str("bevy"));
102
103    tools
104}