Skip to main content

whitaker_common/
expr.rs

1//! Lightweight expression helpers for lint analysis.
2#![cfg_attr(test, allow(clippy::expect_used, clippy::unwrap_used))]
3
4use crate::path::SimplePath;
5
6/// A tiny expression model used for helper functions.
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub enum Expr {
9    /// A call expression with a resolved callee path.
10    Call { callee: SimplePath },
11    /// A path expression.
12    Path(SimplePath),
13    /// Any other literal expression (placeholder for expansion).
14    Literal(String),
15}
16
17/// Returns the callee path of a call expression, if one is present.
18///
19/// # Examples
20///
21/// ```
22/// use whitaker_common::expr::{Expr, def_id_of_expr_callee};
23/// use whitaker_common::path::SimplePath;
24///
25/// let expr = Expr::Call { callee: SimplePath::from("std::mem::drop") };
26/// assert_eq!(
27///     def_id_of_expr_callee(&expr).expect("call expression has callee path").segments(),
28///     &["std", "mem", "drop"]
29/// );
30/// ```
31#[must_use]
32pub fn def_id_of_expr_callee(expr: &Expr) -> Option<&SimplePath> {
33    match expr {
34        Expr::Call { callee } => Some(callee),
35        _ => None,
36    }
37}
38
39/// Tests whether a path matches the provided candidate segments.
40///
41/// # Examples
42///
43/// ```
44/// use whitaker_common::expr::is_path_to;
45/// use whitaker_common::path::SimplePath;
46///
47/// let path = SimplePath::from("core::option::Option");
48/// assert!(is_path_to(&path, ["core", "option", "Option"]));
49/// ```
50#[must_use]
51pub fn is_path_to<'a, I>(path: &SimplePath, candidate: I) -> bool
52where
53    I: IntoIterator<Item = &'a str>,
54{
55    path.matches(candidate)
56}
57
58/// Returns `true` when the receiver is `Option` or `Result` regardless of module
59/// path.
60///
61/// # Examples
62///
63/// ```
64/// use whitaker_common::expr::recv_is_option_or_result;
65/// use whitaker_common::path::SimplePath;
66///
67/// assert!(recv_is_option_or_result(&SimplePath::from("std::option::Option")));
68/// assert!(recv_is_option_or_result(&SimplePath::from("Result")));
69/// assert!(!recv_is_option_or_result(&SimplePath::from("crate::Thing")));
70/// ```
71#[must_use]
72pub fn recv_is_option_or_result(path: &SimplePath) -> bool {
73    matches!(path.last(), Some("Option" | "Result"))
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use rstest::rstest;
80
81    #[rstest]
82    fn callee_extraction() {
83        let expr = Expr::Call {
84            callee: SimplePath::from("std::mem::drop"),
85        };
86        assert!(def_id_of_expr_callee(&expr).is_some());
87    }
88
89    #[rstest]
90    fn recognizes_option_like_receivers() {
91        let option_path = SimplePath::from("std::option::Option");
92        let result_path = SimplePath::from("Result");
93        let custom_path = SimplePath::from("crate::Thing");
94
95        assert!(recv_is_option_or_result(&option_path));
96        assert!(recv_is_option_or_result(&result_path));
97        assert!(!recv_is_option_or_result(&custom_path));
98    }
99}