Skip to main content

pedant_core/check_config/
naming.rs

1use std::sync::{Arc, LazyLock};
2
3use serde::Deserialize;
4
5use super::string_list::{deserialize_arc_str_slice, deserialize_option_arc_str_slice};
6
7/// Default list of generic variable names that LLMs overuse.
8const DEFAULT_GENERIC_NAMES: &[&str] = &[
9    "tmp", "temp", "data", "val", "value", "result", "res", "ret", "buf", "buffer", "item", "elem",
10    "obj", "input", "output", "info", "ctx", "args", "params", "thing", "stuff", "foo", "bar",
11    "baz",
12];
13
14/// Thresholds for the generic-naming check (`tmp`, `val`, `data`, etc.).
15#[derive(Debug, Deserialize, Clone)]
16#[serde(deny_unknown_fields)]
17pub struct NamingCheck {
18    /// Master switch; `false` skips the naming check entirely.
19    #[serde(default)]
20    pub enabled: bool,
21    /// Words considered generic. Replaces the built-in list when provided.
22    #[serde(
23        default = "default_generic_names",
24        deserialize_with = "deserialize_arc_str_slice"
25    )]
26    pub generic_names: Arc<[Arc<str>]>,
27    /// Fraction of bindings that must be generic before flagging (0.0..=1.0).
28    #[serde(default = "default_max_generic_ratio")]
29    pub max_generic_ratio: f64,
30    /// Absolute minimum generic count before the ratio check kicks in.
31    #[serde(default = "default_min_generic_count")]
32    pub min_generic_count: usize,
33}
34
35impl Default for NamingCheck {
36    fn default() -> Self {
37        Self {
38            enabled: false,
39            generic_names: default_generic_names(),
40            max_generic_ratio: default_max_generic_ratio(),
41            min_generic_count: default_min_generic_count(),
42        }
43    }
44}
45
46impl NamingCheck {
47    /// Merge a path-specific override, replacing fields that are set.
48    pub fn apply_override(&mut self, ovr: &NamingOverride) {
49        if let Some(enabled) = ovr.enabled {
50            self.enabled = enabled;
51        }
52        if let Some(ref names) = ovr.generic_names {
53            self.generic_names = names.clone();
54        }
55        if let Some(ratio) = ovr.max_generic_ratio {
56            self.max_generic_ratio = ratio;
57        }
58        if let Some(count) = ovr.min_generic_count {
59            self.min_generic_count = count;
60        }
61    }
62}
63
64/// Path-specific overrides for the naming check. `None` inherits from base config.
65#[derive(Debug, Deserialize, Default, Clone)]
66#[serde(deny_unknown_fields)]
67pub struct NamingOverride {
68    /// Replace the enabled state.
69    pub enabled: Option<bool>,
70    /// Replace the generic names list.
71    #[serde(default, deserialize_with = "deserialize_option_arc_str_slice")]
72    pub generic_names: Option<Arc<[Arc<str>]>>,
73    /// Replace the maximum generic ratio threshold.
74    pub max_generic_ratio: Option<f64>,
75    /// Replace the minimum generic count threshold.
76    pub min_generic_count: Option<usize>,
77}
78
79static GENERIC_NAMES_ARC: LazyLock<Arc<[Arc<str>]>> = LazyLock::new(|| {
80    DEFAULT_GENERIC_NAMES
81        .iter()
82        .map(|s| Arc::from(*s))
83        .collect()
84});
85
86fn default_generic_names() -> Arc<[Arc<str>]> {
87    Arc::clone(&GENERIC_NAMES_ARC)
88}
89
90fn default_max_generic_ratio() -> f64 {
91    0.3
92}
93
94fn default_min_generic_count() -> usize {
95    2
96}