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}