marker_adapter/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(clippy::pedantic)]
3#![warn(clippy::index_refutable_slice)]
4#![allow(clippy::module_name_repetitions)]
5
6mod error;
7mod loader;
8
9pub mod context;
10
11pub use error::{Error, Result};
12pub use loader::LintCrateInfo;
13
14use loader::LintCrateRegistry;
15use marker_api::Lint;
16use marker_api::{
17    ast::{Body, Crate, EnumVariant, ExprKind, ItemField, ItemKind, StmtKind},
18    context::MarkerContext,
19    LintPass, LintPassInfo,
20};
21use marker_utils::visitor::{self, Visitor};
22use std::{cell::RefCell, ops::ControlFlow};
23
24pub const LINT_CRATES_ENV: &str = "MARKER_LINT_CRATES";
25
26/// This struct is the interface used by lint drivers to load lint crates, pass
27/// `marker_api` objects to external lint passes and all other magic you can think of.
28#[derive(Debug)]
29pub struct Adapter {
30    /// [`LintPass`] functions are called with a mutable `self` parameter as the
31    /// first argument. This `RefCell` acts as a wrapper to hide the internal
32    /// mutability from drivers.
33    ///
34    /// The effects of the mutability should never reach the driver anyways and
35    /// this just makes it way easier to handle the adapter in drivers.
36    inner: RefCell<AdapterInner>,
37}
38
39#[derive(Debug)]
40struct AdapterInner {
41    external_lint_crates: LintCrateRegistry,
42}
43
44impl Adapter {
45    /// This creates a new [`Adapter`] instance
46    ///
47    /// # Errors
48    ///
49    /// This function will return an error if an error occurs during the lint
50    /// loading process.
51    pub fn new(lint_crates: &[LintCrateInfo]) -> Result<Self> {
52        let external_lint_crates = LintCrateRegistry::new(lint_crates)?;
53        Ok(Self {
54            inner: RefCell::new(AdapterInner { external_lint_crates }),
55        })
56    }
57
58    pub fn marker_lints(&self) -> Vec<&'static Lint> {
59        self.lint_pass_infos()
60            .iter()
61            .flat_map(marker_api::LintPassInfo::lints)
62            .copied()
63            .collect()
64    }
65
66    #[must_use]
67    fn lint_pass_infos(&self) -> Vec<LintPassInfo> {
68        self.inner.borrow().external_lint_crates.collect_lint_pass_info()
69    }
70
71    pub fn process_krate<'ast>(&self, cx: &'ast MarkerContext<'ast>, krate: &'ast Crate<'ast>) {
72        let inner = &mut *self.inner.borrow_mut();
73
74        inner.external_lint_crates.set_ast_context(cx);
75
76        inner.external_lint_crates.check_crate(cx, krate);
77        visitor::traverse_item::<()>(cx, inner, ItemKind::Mod(krate.root_mod()));
78    }
79}
80
81impl Visitor<()> for AdapterInner {
82    fn scope(&self) -> visitor::VisitorScope {
83        visitor::VisitorScope::AllBodies
84    }
85
86    fn visit_item<'ast>(&mut self, cx: &'ast MarkerContext<'ast>, item: ItemKind<'ast>) -> ControlFlow<()> {
87        self.external_lint_crates.check_item(cx, item);
88        ControlFlow::Continue(())
89    }
90
91    fn visit_field<'ast>(&mut self, cx: &'ast MarkerContext<'ast>, field: &'ast ItemField<'ast>) -> ControlFlow<()> {
92        self.external_lint_crates.check_field(cx, field);
93        ControlFlow::Continue(())
94    }
95
96    fn visit_variant<'ast>(
97        &mut self,
98        cx: &'ast MarkerContext<'ast>,
99        variant: &'ast EnumVariant<'ast>,
100    ) -> ControlFlow<()> {
101        self.external_lint_crates.check_variant(cx, variant);
102        ControlFlow::Continue(())
103    }
104
105    fn visit_body<'ast>(&mut self, cx: &'ast MarkerContext<'ast>, body: &'ast Body<'ast>) -> ControlFlow<()> {
106        self.external_lint_crates.check_body(cx, body);
107        ControlFlow::Continue(())
108    }
109
110    fn visit_stmt<'ast>(&mut self, cx: &'ast MarkerContext<'ast>, stmt: StmtKind<'ast>) -> ControlFlow<()> {
111        self.external_lint_crates.check_stmt(cx, stmt);
112        ControlFlow::Continue(())
113    }
114
115    fn visit_expr<'ast>(&mut self, cx: &'ast MarkerContext<'ast>, expr: ExprKind<'ast>) -> ControlFlow<()> {
116        self.external_lint_crates.check_expr(cx, expr);
117        ControlFlow::Continue(())
118    }
119}