#![allow(dead_code)]
use quote::quote;
use syn::{Expr, ExprCall, ExprMethodCall};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SmartPointerType {
Box,
Rc,
Arc,
RefCell,
Cell,
WeakRc,
WeakArc,
Pin,
Cow,
}
impl SmartPointerType {
pub fn name(&self) -> &'static str {
match self {
SmartPointerType::Box => "Box",
SmartPointerType::Rc => "Rc",
SmartPointerType::Arc => "Arc",
SmartPointerType::RefCell => "RefCell",
SmartPointerType::Cell => "Cell",
SmartPointerType::WeakRc => "Weak(Rc)",
SmartPointerType::WeakArc => "Weak(Arc)",
SmartPointerType::Pin => "Pin",
SmartPointerType::Cow => "Cow",
}
}
pub fn is_reference_counted(&self) -> bool {
matches!(self, SmartPointerType::Rc | SmartPointerType::Arc)
}
pub fn has_interior_mutability(&self) -> bool {
matches!(self, SmartPointerType::RefCell | SmartPointerType::Cell)
}
pub fn is_thread_safe(&self) -> bool {
matches!(self, SmartPointerType::Arc | SmartPointerType::WeakArc)
}
pub fn is_weak(&self) -> bool {
matches!(self, SmartPointerType::WeakRc | SmartPointerType::WeakArc)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SmartPointerOp {
New(SmartPointerType),
Clone(SmartPointerType),
Borrow,
BorrowMut,
CellGet,
CellSet,
Downgrade(SmartPointerType),
WeakUpgrade(SmartPointerType),
WeakClone(SmartPointerType),
BoxPin,
PinNew,
PinIntoInner,
CowBorrowed,
CowOwned,
CowToMut,
BoxIntoRaw,
BoxFromRaw,
OnceCellNew,
OnceCellSet,
OnceCellGet,
OnceCellGetOrInit,
MaybeUninitUninit,
MaybeUninitNew,
MaybeUninitWrite,
MaybeUninitAssumeInit,
MaybeUninitAssumeInitRead,
MaybeUninitAssumeInitDrop,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConcurrencyOp {
ThreadSpawn,
ThreadJoin,
ChannelNew,
ChannelSend,
ChannelRecv,
ChannelTryRecv,
MutexLock,
MutexTryLock,
RwLockRead,
RwLockWrite,
}
pub fn detect_smart_pointer_new(expr: &Expr) -> Option<SmartPointerType> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string();
let normalized = path_str.replace(" ", "");
if normalized.contains("Box::new") {
return Some(SmartPointerType::Box);
}
if normalized.contains("Rc::new") {
return Some(SmartPointerType::Rc);
}
if normalized.contains("Arc::new") {
return Some(SmartPointerType::Arc);
}
if normalized.contains("RefCell::new") {
return Some(SmartPointerType::RefCell);
}
if normalized.contains("Cell::new")
&& !normalized.contains("UnsafeCell::new")
&& !normalized.contains("OnceCell::new")
&& !normalized.contains("OnceLock::new") {
return Some(SmartPointerType::Cell);
}
}
}
None
}
pub fn detect_rc_clone(expr: &Expr) -> Option<SmartPointerType> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string();
let normalized = path_str.replace(" ", "");
if normalized.contains("Rc::clone") {
return Some(SmartPointerType::Rc);
}
if normalized.contains("Arc::clone") {
return Some(SmartPointerType::Arc);
}
}
}
None
}
pub fn detect_refcell_borrow(expr: &Expr) -> Option<bool> {
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
let method_name = method.to_string();
if method_name == "borrow" {
return Some(false); }
if method_name == "borrow_mut" {
return Some(true); }
}
None
}
pub fn detect_cell_operation(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
let method_name = method.to_string();
if method_name == "get" {
return Some(SmartPointerOp::CellGet);
}
if method_name == "set" {
return Some(SmartPointerOp::CellSet);
}
}
None
}
pub fn detect_box_pin(expr: &Expr) -> bool {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
return path_str.contains("Box::pin");
}
}
false
}
pub fn detect_box_raw_op(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("Box::into_raw") {
return Some(SmartPointerOp::BoxIntoRaw);
}
if path_str.contains("Box::from_raw") {
return Some(SmartPointerOp::BoxFromRaw);
}
}
}
None
}
pub fn detect_pin_operation(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("Pin::new") {
return Some(SmartPointerOp::PinNew);
}
if path_str.contains("Pin::into_inner") {
return Some(SmartPointerOp::PinIntoInner);
}
}
}
None
}
pub fn detect_cow_creation(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("Cow::Borrowed") {
return Some(SmartPointerOp::CowBorrowed);
}
if path_str.contains("Cow::Owned") {
return Some(SmartPointerOp::CowOwned);
}
}
}
None
}
pub fn detect_cow_to_mut(expr: &Expr) -> bool {
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
return method.to_string() == "to_mut";
}
false
}
pub fn detect_downgrade(expr: &Expr) -> Option<SmartPointerType> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("Rc::downgrade") {
return Some(SmartPointerType::WeakRc);
}
if path_str.contains("Arc::downgrade") {
return Some(SmartPointerType::WeakArc);
}
}
}
None
}
pub fn detect_weak_upgrade(expr: &Expr) -> bool {
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
return method.to_string() == "upgrade";
}
false
}
pub fn detect_once_cell_new(expr: &Expr) -> Option<bool> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("OnceCell::new") {
return Some(false); }
if path_str.contains("OnceLock::new") {
return Some(true); }
}
}
None
}
pub fn detect_once_cell_method(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
let method_name = method.to_string();
match method_name.as_str() {
"set" => return Some(SmartPointerOp::OnceCellSet),
"get" => return Some(SmartPointerOp::OnceCellGet),
"get_or_init" => return Some(SmartPointerOp::OnceCellGetOrInit),
_ => {}
}
}
None
}
pub fn detect_maybe_uninit_new(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("MaybeUninit::uninit") {
return Some(SmartPointerOp::MaybeUninitUninit);
}
if path_str.contains("MaybeUninit::new") {
return Some(SmartPointerOp::MaybeUninitNew);
}
}
}
None
}
pub fn detect_maybe_uninit_method(expr: &Expr) -> Option<SmartPointerOp> {
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
let method_name = method.to_string();
match method_name.as_str() {
"write" => return Some(SmartPointerOp::MaybeUninitWrite),
"assume_init" => return Some(SmartPointerOp::MaybeUninitAssumeInit),
"assume_init_read" => return Some(SmartPointerOp::MaybeUninitAssumeInitRead),
"assume_init_drop" => return Some(SmartPointerOp::MaybeUninitAssumeInitDrop),
_ => {}
}
}
None
}
pub fn detect_concurrency_op(expr: &Expr) -> Option<ConcurrencyOp> {
if let Expr::Call(ExprCall { func, .. }) = expr {
if let Expr::Path(path) = &**func {
let path_str = quote!(#path).to_string().replace(" ", "");
if path_str.contains("thread::spawn") || path_str.contains("std::thread::spawn") {
return Some(ConcurrencyOp::ThreadSpawn);
}
if path_str.contains("mpsc::channel") || path_str.contains("sync::mpsc::channel") {
return Some(ConcurrencyOp::ChannelNew);
}
}
}
if let Expr::MethodCall(ExprMethodCall { method, .. }) = expr {
let method_name = method.to_string();
match method_name.as_str() {
"join" => return Some(ConcurrencyOp::ThreadJoin),
"send" => return Some(ConcurrencyOp::ChannelSend),
"recv" => return Some(ConcurrencyOp::ChannelRecv),
"try_recv" => return Some(ConcurrencyOp::ChannelTryRecv),
"lock" => return Some(ConcurrencyOp::MutexLock),
"try_lock" => return Some(ConcurrencyOp::MutexTryLock),
"read" => return Some(ConcurrencyOp::RwLockRead),
"write" => return Some(ConcurrencyOp::RwLockWrite),
_ => {}
}
}
None
}
pub fn is_smart_pointer_operation(expr: &Expr) -> Option<SmartPointerOp> {
if let Some(sp_type) = detect_smart_pointer_new(expr) {
return Some(SmartPointerOp::New(sp_type));
}
if let Some(sp_type) = detect_rc_clone(expr) {
return Some(SmartPointerOp::Clone(sp_type));
}
if let Some(is_mut) = detect_refcell_borrow(expr) {
return Some(if is_mut {
SmartPointerOp::BorrowMut
} else {
SmartPointerOp::Borrow
});
}
if let Some(op) = detect_cell_operation(expr) {
return Some(op);
}
if detect_box_pin(expr) {
return Some(SmartPointerOp::BoxPin);
}
if let Some(op) = detect_box_raw_op(expr) {
return Some(op);
}
if let Some(op) = detect_pin_operation(expr) {
return Some(op);
}
if let Some(op) = detect_cow_creation(expr) {
return Some(op);
}
if detect_cow_to_mut(expr) {
return Some(SmartPointerOp::CowToMut);
}
if let Some(sp_type) = detect_downgrade(expr) {
return Some(SmartPointerOp::Downgrade(sp_type));
}
if detect_weak_upgrade(expr) {
return Some(SmartPointerOp::WeakUpgrade(SmartPointerType::WeakRc));
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_detect_box_new() {
let expr: Expr = parse_quote! { Box::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Box));
}
#[test]
fn test_detect_rc_new() {
let expr: Expr = parse_quote! { Rc::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Rc));
}
#[test]
fn test_detect_arc_new() {
let expr: Expr = parse_quote! { Arc::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Arc));
}
#[test]
fn test_detect_refcell_new() {
let expr: Expr = parse_quote! { RefCell::new(42) };
assert_eq!(
detect_smart_pointer_new(&expr),
Some(SmartPointerType::RefCell)
);
}
#[test]
fn test_detect_cell_new() {
let expr: Expr = parse_quote! { Cell::new(42) };
assert_eq!(
detect_smart_pointer_new(&expr),
Some(SmartPointerType::Cell)
);
}
#[test]
fn test_detect_rc_clone() {
let expr: Expr = parse_quote! { Rc::clone(&x) };
assert_eq!(detect_rc_clone(&expr), Some(SmartPointerType::Rc));
}
#[test]
fn test_detect_arc_clone() {
let expr: Expr = parse_quote! { Arc::clone(&x) };
assert_eq!(detect_rc_clone(&expr), Some(SmartPointerType::Arc));
}
#[test]
fn test_detect_refcell_borrow() {
let expr: Expr = parse_quote! { x.borrow() };
assert_eq!(detect_refcell_borrow(&expr), Some(false));
}
#[test]
fn test_detect_refcell_borrow_mut() {
let expr: Expr = parse_quote! { x.borrow_mut() };
assert_eq!(detect_refcell_borrow(&expr), Some(true));
}
#[test]
fn test_detect_cell_get() {
let expr: Expr = parse_quote! { x.get() };
assert_eq!(detect_cell_operation(&expr), Some(SmartPointerOp::CellGet));
}
#[test]
fn test_detect_cell_set() {
let expr: Expr = parse_quote! { x.set(42) };
assert_eq!(detect_cell_operation(&expr), Some(SmartPointerOp::CellSet));
}
#[test]
fn test_smart_pointer_type_properties() {
assert_eq!(SmartPointerType::Box.name(), "Box");
assert_eq!(SmartPointerType::Rc.name(), "Rc");
assert_eq!(SmartPointerType::Arc.name(), "Arc");
assert!(SmartPointerType::Rc.is_reference_counted());
assert!(SmartPointerType::Arc.is_reference_counted());
assert!(!SmartPointerType::Box.is_reference_counted());
assert!(SmartPointerType::RefCell.has_interior_mutability());
assert!(SmartPointerType::Cell.has_interior_mutability());
assert!(!SmartPointerType::Box.has_interior_mutability());
assert!(SmartPointerType::Arc.is_thread_safe());
assert!(!SmartPointerType::Rc.is_thread_safe());
}
#[test]
fn test_is_smart_pointer_operation_box() {
let expr: Expr = parse_quote! { Box::new(42) };
assert_eq!(
is_smart_pointer_operation(&expr),
Some(SmartPointerOp::New(SmartPointerType::Box))
);
}
#[test]
fn test_is_smart_pointer_operation_rc_clone() {
let expr: Expr = parse_quote! { Rc::clone(&x) };
assert_eq!(
is_smart_pointer_operation(&expr),
Some(SmartPointerOp::Clone(SmartPointerType::Rc))
);
}
#[test]
fn test_is_smart_pointer_operation_refcell_borrow() {
let expr: Expr = parse_quote! { x.borrow() };
assert_eq!(
is_smart_pointer_operation(&expr),
Some(SmartPointerOp::Borrow)
);
}
#[test]
fn test_non_smart_pointer_expression() {
let expr: Expr = parse_quote! { 42 };
assert_eq!(is_smart_pointer_operation(&expr), None);
let expr: Expr = parse_quote! { String::from("hello") };
assert_eq!(is_smart_pointer_operation(&expr), None);
}
#[test]
fn test_detect_with_full_path() {
let expr: Expr = parse_quote! { std::boxed::Box::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Box));
let expr: Expr = parse_quote! { std::rc::Rc::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Rc));
let expr: Expr = parse_quote! { std::sync::Arc::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Arc));
}
#[test]
fn test_detect_with_use_statement() {
let expr: Expr = parse_quote! { Rc::new(42) };
assert_eq!(detect_smart_pointer_new(&expr), Some(SmartPointerType::Rc));
}
}