wowlab_tidy/languages/rust/rules/
support.rs1use ra_ap_syntax::{
2 AstNode, Edition, SourceFile,
3 ast::{self, HasModuleItem, LiteralKind},
4};
5
6const ONE_BYTE: u64 = 1;
7const TWO_BYTES: u64 = 2;
8const FOUR_BYTES: u64 = 4;
9const EIGHT_BYTES: u64 = 8;
10const SIXTEEN_BYTES: u64 = 16;
11const THREE_WORDS: u64 = 24;
12
13pub(super) fn parse_use(source: &str) -> Option<ast::Use> {
14 let parse = SourceFile::parse(source, Edition::Edition2024);
15
16 parse
17 .errors()
18 .is_empty()
19 .then(|| parse.tree())?
20 .items()
21 .find_map(|item| match item {
22 ast::Item::Use(item) => Some(item),
23 _ => None,
24 })
25}
26
27pub(super) fn type_name(ty: &ast::Type) -> Option<String> {
28 let ast::Type::PathType(path_type) = ty else {
29 return None;
30 };
31
32 path_type
33 .path()?
34 .segment()?
35 .name_ref()
36 .map(|name| name.text().to_string())
37}
38
39pub(super) fn is_inside_trait(function: &ast::Fn) -> bool {
40 function
41 .syntax()
42 .ancestors()
43 .skip(1)
44 .take_while(|node| ast::Fn::cast(node.clone()).is_none())
45 .any(|node| ast::Trait::cast(node).is_some())
46}
47
48pub(super) fn is_item_or_impl_fn(function: &ast::Fn) -> bool {
49 let Some(parent) = function.syntax().parent() else {
50 return false;
51 };
52
53 if ast::ExternItemList::cast(parent.clone()).is_some() {
54 return false;
55 }
56
57 let Some(items) = ast::AssocItemList::cast(parent) else {
58 return true;
59 };
60
61 items.syntax().parent().and_then(ast::Impl::cast).is_some()
62}
63
64pub(super) fn path_names(path: &ast::Path) -> Vec<String> {
65 let mut names = Vec::new();
66 let mut current = Some(path.clone());
67
68 while let Some(path) = current {
69 if let Some(name) = path.segment().and_then(|segment| segment.name_ref()) {
70 names.push(name.text().to_string());
72 }
73
74 current = path.qualifier();
75 }
76
77 names.reverse();
78
79 names
80}
81
82pub(super) fn estimate_type_size(ty: &ast::Type) -> Option<u64> {
84 match ty {
85 ast::Type::PathType(path) => estimate_path_size(path),
86 ast::Type::ArrayType(array) => {
87 let element = estimate_type_size(&array.ty()?)?;
88 let length = parse_int_expr(&array.const_arg()?.expr()?)?;
89
90 Some(element.saturating_mul(length))
91 }
92 ast::Type::TupleType(tuple) => tuple.fields().try_fold(0u64, |total, element| {
93 Some(total.saturating_add(estimate_type_size(&element)?))
94 }),
95 ast::Type::RefType(_) | ast::Type::PtrType(_) => Some(EIGHT_BYTES),
96 _ => None,
97 }
98}
99
100fn estimate_path_size(path: &ast::PathType) -> Option<u64> {
101 let name = path.path()?.segment()?.name_ref()?;
102
103 match name.text().as_str() {
104 "bool" | "u8" | "i8" => Some(ONE_BYTE),
105 "u16" | "i16" => Some(TWO_BYTES),
106 "u32" | "i32" | "f32" => Some(FOUR_BYTES),
107 "u64" | "i64" | "f64" | "usize" | "isize" | "Box" | "Arc" | "Rc" => Some(EIGHT_BYTES),
108 "u128" | "i128" => Some(SIXTEEN_BYTES),
109 "Vec" | "String" => Some(THREE_WORDS),
110 _ => None,
111 }
112}
113
114pub(super) fn parse_int_expr(expr: &ast::Expr) -> Option<u64> {
115 let ast::Expr::Literal(literal) = expr else {
116 return None;
117 };
118 let LiteralKind::IntNumber(number) = literal.kind() else {
119 return None;
120 };
121
122 number.value().ok()?.try_into().ok()
123}