rust_diff_analyzer/classifier/attr_classifier.rs
1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use crate::{config::Config, types::SemanticUnit};
5
6/// Checks if unit is a test function
7///
8/// # Arguments
9///
10/// * `unit` - Semantic unit to check
11///
12/// # Returns
13///
14/// `true` if unit has #[test] attribute
15///
16/// # Examples
17///
18/// ```
19/// use rust_diff_analyzer::{
20/// classifier::attr_classifier::is_test_unit,
21/// types::{LineSpan, SemanticUnit, SemanticUnitKind, Visibility},
22/// };
23///
24/// let unit = SemanticUnit::new(
25/// SemanticUnitKind::Function,
26/// "test_it".to_string(),
27/// Visibility::Private,
28/// LineSpan::new(1, 10),
29/// vec!["test".to_string()],
30/// );
31///
32/// assert!(is_test_unit(&unit));
33/// ```
34pub fn is_test_unit(unit: &SemanticUnit) -> bool {
35 unit.has_attribute("test")
36}
37
38/// Checks if unit is a benchmark function
39///
40/// # Arguments
41///
42/// * `unit` - Semantic unit to check
43///
44/// # Returns
45///
46/// `true` if unit has #[bench] attribute
47///
48/// # Examples
49///
50/// ```
51/// use rust_diff_analyzer::{
52/// classifier::attr_classifier::is_bench_unit,
53/// types::{LineSpan, SemanticUnit, SemanticUnitKind, Visibility},
54/// };
55///
56/// let unit = SemanticUnit::new(
57/// SemanticUnitKind::Function,
58/// "bench_it".to_string(),
59/// Visibility::Private,
60/// LineSpan::new(1, 10),
61/// vec!["bench".to_string()],
62/// );
63///
64/// assert!(is_bench_unit(&unit));
65/// ```
66pub fn is_bench_unit(unit: &SemanticUnit) -> bool {
67 unit.has_attribute("bench")
68}
69
70/// Checks if unit is inside a #[cfg(test)] module
71///
72/// # Arguments
73///
74/// * `unit` - Semantic unit to check
75///
76/// # Returns
77///
78/// `true` if unit has cfg_test marker
79///
80/// # Examples
81///
82/// ```
83/// use rust_diff_analyzer::{
84/// classifier::attr_classifier::is_in_test_module,
85/// types::{LineSpan, SemanticUnit, SemanticUnitKind, Visibility},
86/// };
87///
88/// let unit = SemanticUnit::new(
89/// SemanticUnitKind::Function,
90/// "helper".to_string(),
91/// Visibility::Private,
92/// LineSpan::new(1, 10),
93/// vec!["cfg_test".to_string()],
94/// );
95///
96/// assert!(is_in_test_module(&unit));
97/// ```
98pub fn is_in_test_module(unit: &SemanticUnit) -> bool {
99 unit.has_attribute("cfg_test")
100}
101
102/// Checks if unit has a test-related feature attribute
103///
104/// # Arguments
105///
106/// * `unit` - Semantic unit to check
107/// * `config` - Configuration with test features
108///
109/// # Returns
110///
111/// `true` if unit has a test feature attribute
112///
113/// # Examples
114///
115/// ```
116/// use rust_diff_analyzer::{
117/// classifier::attr_classifier::has_test_feature,
118/// config::Config,
119/// types::{LineSpan, SemanticUnit, SemanticUnitKind, Visibility},
120/// };
121///
122/// let unit = SemanticUnit::new(
123/// SemanticUnitKind::Function,
124/// "mock_fn".to_string(),
125/// Visibility::Private,
126/// LineSpan::new(1, 10),
127/// vec!["cfg".to_string()],
128/// );
129///
130/// let config = Config::default();
131/// let result = has_test_feature(&unit, &config);
132/// ```
133pub fn has_test_feature(unit: &SemanticUnit, config: &Config) -> bool {
134 let test_features = config.test_features_set();
135
136 for attr in &unit.attributes {
137 if attr.starts_with("cfg") {
138 for feature in &test_features {
139 if attr.contains(feature) {
140 return true;
141 }
142 }
143 }
144 }
145
146 false
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::types::{LineSpan, SemanticUnitKind, Visibility};
153
154 fn make_unit(attrs: Vec<&str>) -> SemanticUnit {
155 SemanticUnit::new(
156 SemanticUnitKind::Function,
157 "test".to_string(),
158 Visibility::Private,
159 LineSpan::new(1, 10),
160 attrs.into_iter().map(String::from).collect(),
161 )
162 }
163
164 #[test]
165 fn test_is_test_unit() {
166 assert!(is_test_unit(&make_unit(vec!["test"])));
167 assert!(!is_test_unit(&make_unit(vec!["inline"])));
168 }
169
170 #[test]
171 fn test_is_bench_unit() {
172 assert!(is_bench_unit(&make_unit(vec!["bench"])));
173 assert!(!is_bench_unit(&make_unit(vec!["test"])));
174 }
175
176 #[test]
177 fn test_is_in_test_module() {
178 assert!(is_in_test_module(&make_unit(vec!["cfg_test"])));
179 assert!(!is_in_test_module(&make_unit(vec!["test"])));
180 }
181}