Skip to main content

slopguard_core/fix/fixers/
mod.rs

1//! Builtin fixers: rewrites a single ast-grep template cannot express, because
2//! they edit several nodes at once (an enum's derive list and the impl it
3//! replaces, two adjacent match arms) or read another file (the module a glob
4//! re-export points at).
5//!
6//! A fixer receives one detection match and returns every edit of its rewrite,
7//! applied together or not at all. It returns `None` whenever it cannot prove
8//! the rewrite compiles and preserves behavior: the finding then stays reported
9//! and the code untouched.
10
11use std::path::Path;
12
13use ast_grep_core::{Doc, NodeMatch};
14use serde::{Deserialize, Serialize};
15use strum::Display;
16
17use super::RawEdit;
18
19mod derive_clone;
20mod match_arms;
21mod reexport;
22mod strum_str;
23mod syntax;
24
25/// Builtin fixers a rule can name in its `fixer` field. Builtin only: a custom
26/// YAML may reuse one, never define a new one, so an unknown name is a parse
27/// error.
28#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "snake_case")]
30#[strum(serialize_all = "snake_case")]
31pub enum FixerKind {
32    /// Replace a hand-written enum-to-string `match` (a `Display` impl or an
33    /// `as_str`-style method) with a strum derive.
34    StrumEnumStr,
35    /// Merge two adjacent match arms with the same body into one `A | B` arm.
36    MergeMatchArms,
37    /// Expand `pub use module::*;` into the explicit list of what it exports.
38    ExplicitReexport,
39    /// Replace a field-by-field `impl Clone` with `#[derive(Clone)]`.
40    DeriveClone,
41}
42
43/// What a fixer may consult beyond the matched node.
44pub(super) struct FixEnv<'a> {
45    /// The file being fixed, `None` for an in-memory snippet (`slopguard
46    /// test`): a fixer that needs another file then gives up.
47    pub path: Option<&'a Path>,
48    /// The crate the strum derive macros are reachable through for this file
49    /// (`strum` with its `derive` feature, or `strum_macros`), `None` when the
50    /// governing `Cargo.toml` declares neither.
51    pub strum_crate: Option<&'a str>,
52}
53
54/// Compute the edits of `kind` for one detection match, or `None` when the
55/// rewrite cannot be proven correct.
56pub(super) fn fix<D: Doc>(
57    kind: FixerKind,
58    node_match: &NodeMatch<'_, D>,
59    env: &FixEnv<'_>,
60) -> Option<Vec<RawEdit>> {
61    let node = node_match.get_node();
62    match kind {
63        FixerKind::StrumEnumStr => strum_str::fix(node, env),
64        FixerKind::MergeMatchArms => match_arms::fix(node),
65        FixerKind::ExplicitReexport => reexport::fix(node, env),
66        FixerKind::DeriveClone => derive_clone::fix(node),
67    }
68}
69
70#[cfg(test)]
71mod tests;