# ScanDefaults Trait Refactoring

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Eliminate scan_dir duplication by adding a `ScanDefaults` trait so each processor's scan defaults live in exactly one place — the config struct.

**Architecture:** Add a `ScanDefaults` trait with methods `default_src_dirs()`, `default_src_extensions()`, `default_src_exclude_dirs()`. Each config struct implements it (generated by `checker_config!`/`generator_config!` macros or manually). `ScanConfig::resolve_from::<T>()` replaces the old `resolve(scan_dir, exts, excl)`. The registry tuple drops its `(scan_dir, extensions, excludes)`.

**Tech Stack:** Rust, macros (`macro_rules!`)

---

### Task 1: Add ScanDefaults trait and resolve_from method

**Files:**
- Modify: `src/config/mod.rs`

- [ ] **Step 1: Run tests to establish green baseline**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 2: Add the ScanDefaults trait**

In `src/config/mod.rs`, add after the `KnownFields` trait (after line 50):

```rust
/// Trait providing default scan configuration for a processor config struct.
/// Each config struct implements this to declare its default src_dirs, extensions, and exclude dirs.
/// This is the single source of truth — no separate registry tuple needed.
pub(crate) trait ScanDefaults {
    /// Default directories to scan. Empty slice means "project root" (user must set src_dirs).
    fn default_src_dirs() -> &'static [&'static str] { &[] }
    /// Default file extensions to match.
    fn default_src_extensions() -> &'static [&'static str] { &[] }
    /// Default directory segments to exclude from scanning.
    fn default_src_exclude_dirs() -> &'static [&'static str] { &[] }
}
```

- [ ] **Step 3: Add resolve_from and from_defaults to ScanConfig**

In `src/config/mod.rs`, add these methods to the `impl ScanConfig` block (after the existing `resolve` method, which stays for now):

```rust
    /// Fill in None fields from the config struct's ScanDefaults.
    pub(crate) fn resolve_from<T: ScanDefaults>(&mut self) {
        if self.src_dirs.is_none() {
            self.src_dirs = Some(T::default_src_dirs().iter().map(|s| s.to_string()).collect());
        }
        if self.src_extensions.is_none() {
            self.src_extensions = Some(T::default_src_extensions().iter().map(|s| s.to_string()).collect());
        }
        if self.src_exclude_dirs.is_none() {
            self.src_exclude_dirs = Some(T::default_src_exclude_dirs().iter().map(|s| s.to_string()).collect());
        }
        if self.src_exclude_files.is_none() {
            self.src_exclude_files = Some(Vec::new());
        }
        if self.src_exclude_paths.is_none() {
            self.src_exclude_paths = Some(Vec::new());
        }
        if self.src_files.is_none() {
            self.src_files = Some(Vec::new());
        }
    }

    /// Create a ScanConfig pre-populated from a ScanDefaults impl.
    /// Used in Default impls so the struct's defaults match its ScanDefaults trait.
    pub(crate) fn from_defaults<T: ScanDefaults>() -> Self {
        Self {
            src_dirs: {
                let dirs = T::default_src_dirs();
                if dirs.is_empty() { None } else { Some(dirs.iter().map(|s| s.to_string()).collect()) }
            },
            src_extensions: {
                let exts = T::default_src_extensions();
                if exts.is_empty() { None } else { Some(exts.iter().map(|s| s.to_string()).collect()) }
            },
            src_exclude_dirs: None,
            src_exclude_files: None,
            src_exclude_paths: None,
            src_files: None,
        }
    }
```

- [ ] **Step 4: Run tests**

Run: `cargo test`
Expected: All tests pass. New trait and methods exist but aren't called yet.

- [ ] **Step 5: Commit**

```bash
git add src/config/mod.rs
git commit -m "feat: add ScanDefaults trait, resolve_from, and from_defaults methods"
```

---

### Task 2: Add ScanDefaults impls to checker_config! macro

**Files:**
- Modify: `src/config/processor_configs.rs:17-144` (checker_config! macro)

The `checker_config!` macro already receives extensions and optionally scan_dir in its public entry points. We'll generate a `ScanDefaults` impl from those same values, and switch the `Default` impl to use `ScanConfig::from_defaults`.

- [ ] **Step 1: Add ScanDefaults generation helper arms**

Add these helper arms to `checker_config!` (before the existing `@no_linter` arm):

```rust
    // Helper: generate a ScanDefaults method — empty list uses trait default, non-empty overrides
    (@scan_default_method $method:ident, []) => {};
    (@scan_default_method $method:ident, [$($val:expr),+]) => {
        fn $method() -> &'static [&'static str] { &[$($val),+] }
    };
```

- [ ] **Step 2: Update @no_linter to generate ScanDefaults and use from_defaults**

Change the `@no_linter` arm signature to accept scan defaults info:

```rust
    (@no_linter $name:ident, [$($ai:expr),*], dirs: [$($dir:expr),*], exts: [$($ext:expr),+], excl: [$($excl:expr),*]) => {
```

In the expansion, replace `scan: $scan,` in the `Default` impl with:
```rust
                    scan: ScanConfig::from_defaults::<$name>(),
```

And add after the `impl KnownFields` block:
```rust
        impl ScanDefaults for $name {
            checker_config!(@scan_default_method default_src_dirs, [$($dir),*]);
            checker_config!(@scan_default_method default_src_extensions, [$($ext),+]);
            checker_config!(@scan_default_method default_src_exclude_dirs, [$($excl),*]);
        }
```

- [ ] **Step 3: Update @with_command the same way**

Change `@with_command` arm signature:

```rust
    (@with_command $name:ident, [$($ai:expr),*], $command:expr, dirs: [$($dir:expr),*], exts: [$($ext:expr),+], excl: [$($excl:expr),*]) => {
```

Replace `scan: $scan,` with `scan: ScanConfig::from_defaults::<$name>(),`.

Add the same `impl ScanDefaults` block.

- [ ] **Step 4: Update all 5 public entry points**

Each entry point now passes the raw values instead of a `$scan` expression.

Basic (extensions only):
```rust
    ($name:ident, src_extensions: [$($ext:expr),+ $(,)?]) => {
        checker_config!(@no_linter $name, [], dirs: [], exts: [$($ext),+], excl: []);
    };
```

With scan_dir:
```rust
    ($name:ident, scan_dir: $dir:expr, src_extensions: [$($ext:expr),+ $(,)?]) => {
        checker_config!(@no_linter $name, [], dirs: [$dir], exts: [$($ext),+], excl: []);
    };
```

With dep_auto:
```rust
    ($name:ident, src_extensions: [$($ext:expr),+ $(,)?], dep_auto: [$($ai:expr),+ $(,)?]) => {
        checker_config!(@no_linter $name, [$($ai),+], dirs: [], exts: [$($ext),+], excl: []);
    };
```

With command:
```rust
    ($name:ident, src_extensions: [$($ext:expr),+ $(,)?], command: $command:expr) => {
        checker_config!(@with_command $name, [], $command, dirs: [], exts: [$($ext),+], excl: []);
    };
```

With command + dep_auto:
```rust
    ($name:ident, src_extensions: [$($ext:expr),+ $(,)?], command: $command:expr, dep_auto: [$($ai:expr),+ $(,)?]) => {
        checker_config!(@with_command $name, [$($ai),+], $command, dirs: [], exts: [$($ext),+], excl: []);
    };
```

- [ ] **Step 5: Remove the default_scan! macro**

It's no longer called anywhere. Delete lines 148-169.

- [ ] **Step 6: Run tests**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 7: Commit**

```bash
git add src/config/processor_configs.rs
git commit -m "feat: generate ScanDefaults from checker_config! macro, remove default_scan!"
```

---

### Task 3: Add ScanDefaults impls to generator_config! macro

**Files:**
- Modify: `src/config/processor_configs.rs:178-388` (generator_config! macro)

The `generator_config!` macro takes a `$scan:expr` positional parameter. We replace that with explicit dirs/exts/excl parameters and generate `ScanDefaults`.

- [ ] **Step 1: Add ScanDefaults helper arms to generator_config!**

```rust
    (@scan_default_method $method:ident, []) => {};
    (@scan_default_method $method:ident, [$($val:expr),+]) => {
        fn $method() -> &'static [&'static str] { &[$($val),+] }
    };
```

- [ ] **Step 2: Update all 3 generator_config! variants**

For each variant (multi-format+args, multi-format, single-output), replace the `$scan:expr` parameter with `dirs: [$($dir:expr),*], exts: [$($ext:expr),+], excl: [$($excl:expr),*]`.

Replace `scan: $scan,` in Default impl with `scan: ScanConfig::from_defaults::<$name>(),`.

Add `impl ScanDefaults` block at the end of each variant's expansion:
```rust
        impl ScanDefaults for $name {
            generator_config!(@scan_default_method default_src_dirs, [$($dir),*]);
            generator_config!(@scan_default_method default_src_extensions, [$($ext),+]);
            generator_config!(@scan_default_method default_src_exclude_dirs, [$($excl),*]);
        }
```

- [ ] **Step 3: Update all generator_config! call sites**

Replace each `default_scan!(...)` argument with the equivalent `dirs:`, `exts:`, `excl:` parameters.

Examples:
```rust
// MarpConfig — was: default_scan!(src_extensions: [".md"])
generator_config!(MarpConfig, tool: "marp" marp,
    formats: ["pdf", "pptx", "html"], output_dir: "out/marp",
    dirs: [], exts: [".md"], excl: [],
);

// SassConfig — was: default_scan!(scan_dir: "sass", src_extensions: [".scss", ".sass"])
generator_config!(SassConfig, tool: "sass" sass,
    output_dir: "out/sass",
    dirs: ["sass"], exts: [".scss", ".sass"], excl: [],
);

// ProtobufConfig — was: default_scan!(scan_dir: "proto", src_extensions: [".proto"])
generator_config!(ProtobufConfig, tool: "protoc" protoc,
    output_dir: "out/proto",
    dirs: ["proto"], exts: [".proto"], excl: [],
);
```

Apply this to every generator_config! call site in the file.

- [ ] **Step 4: Run tests**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 5: Commit**

```bash
git add src/config/processor_configs.rs
git commit -m "feat: generate ScanDefaults from generator_config! macro"
```

---

### Task 4: Add ScanDefaults impls to manually-defined config structs

**Files:**
- Modify: `src/config/processor_configs.rs`

Config structs not generated by checker_config!/generator_config! need hand-written `ScanDefaults` impls. Also update their `Default` impls to use `ScanConfig::from_defaults::<Self>()`.

Manual configs (values from registry.rs):

| Config | src_dirs | src_extensions | src_exclude_dirs |
|--------|----------|---------------|-----------------|
| TeraConfig | ["tera.templates"] | [".tera"] | — |
| CppcheckConfig | ["src"] | [".c", ".cc"] | CC_EXCLUDE_DIRS |
| ClangTidyConfig | ["src"] | [".c", ".cc"] | CC_EXCLUDE_DIRS |
| CcSingleFileConfig | ["src"] | [".c", ".cc"] | — |
| CcConfig | — | ["cc.yaml"] | CC_EXCLUDE_DIRS |
| LinuxModuleConfig | — | ["linux-module.yaml"] | — |
| CargoConfig | — | ["Cargo.toml"] | MAKE_CARGO_EXCLUDES |
| ClippyConfig | — | ["Cargo.toml"] | MAKE_CARGO_EXCLUDES |
| MakeConfig | — | ["Makefile"] | MAKE_CARGO_EXCLUDES |
| MarpConfig | ["marp"] | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| MarpImagesConfig | ["marp"] | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| PipConfig | — | ["requirements.txt"] | MAKE_CARGO_EXCLUDES |
| SphinxConfig | — | ["conf.py"] | — |
| MdbookConfig | — | ["book.toml"] | — |
| NpmConfig | — | ["package.json"] | MAKE_CARGO_EXCLUDES |
| GemConfig | — | ["Gemfile"] | — |
| PandocConfig | ["pandoc"] | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| ChromiumConfig | ["out/marp"] | [".html"] | — |
| ObjdumpConfig | ["out/cc_single_file"] | [".elf"] | — |
| PdfuniteConfig | — | ["course.yaml"] | — |
| IpdfuniteConfig | — | ["course.yaml"] | — |
| ScriptConfig | — | — | — |
| GeneratorConfig | — | — | — |
| ExplicitConfig | — | — | — |
| SlidevConfig | — | [".md"] | — |
| TagsConfig | — | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| IsassConfig | ["sass"] | [".scss", ".sass"] | — |
| RustSingleFileConfig | ["src"] | [".rs"] | — |
| Yaml2jsonConfig | — | [".yml", ".yaml"] | — |
| Imarkdown2htmlConfig | — | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| EncodingConfig | — | (large list) | — |
| DuplicateFilesConfig | — | (large list) | — |
| LicenseHeaderConfig | — | (large list) | — |
| TermsConfig | — | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| AsciiConfig | — | [".md"] | MARKDOWN_EXCLUDE_DIRS |
| JekyllConfig | — | ["_config.yml"] | — |

- [ ] **Step 1: Add ScanDefaults impls for all manual configs**

For each struct, add an `impl ScanDefaults` block. Only override methods that differ from the trait defaults (empty slice). Pattern:

```rust
impl ScanDefaults for TeraConfig {
    fn default_src_dirs() -> &'static [&'static str] { &["tera.templates"] }
    fn default_src_extensions() -> &'static [&'static str] { &[".tera"] }
}

impl ScanDefaults for CppcheckConfig {
    fn default_src_dirs() -> &'static [&'static str] { &["src"] }
    fn default_src_extensions() -> &'static [&'static str] { &[".c", ".cc"] }
    fn default_src_exclude_dirs() -> &'static [&'static str] { CC_EXCLUDE_DIRS }
}
```

For ScriptConfig/GeneratorConfig/ExplicitConfig (no defaults at all):
```rust
impl ScanDefaults for ScriptConfig {}
```

**Important:** MarpConfig and MarpImagesConfig must include `default_src_dirs` returning `&["marp"]`. Their current `Default` impls are missing this — the registry was compensating via `resolve()`. This is a bug fix.

- [ ] **Step 2: Update Default impls to use from_defaults**

In each manual struct's `Default` impl, replace the `scan:` field from `default_scan!(...)` to:
```rust
                    scan: ScanConfig::from_defaults::<Self>(),
```

- [ ] **Step 3: Run tests**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 4: Commit**

```bash
git add src/config/processor_configs.rs
git commit -m "feat: implement ScanDefaults for all manual config structs"
```

---

### Task 5: Migrate callers from resolve() to resolve_from() and simplify registry

**Files:**
- Modify: `src/registry.rs`
- Modify: `src/config/mod.rs`
- Modify: `src/builder/mod.rs`
- Modify: `src/main.rs`
- Modify: `src/processors/lua_processor.rs`

- [ ] **Step 1: Simplify the registry**

In `src/registry.rs`, remove the tuple from every entry. Change format from:
```
TERA, tera, TeraConfig, TeraProcessor, ("tera.templates", &[".tera"], &[]);
```
To:
```
TERA, tera, TeraConfig, TeraProcessor;
```

Update all 88 entries. Update the doc comments (lines 7, 13) to remove scan_dir references:

```rust
/// Each entry: `CONST_NAME, field_name, ConfigType, ProcessorType;`
///
/// - `CONST_NAME`: used for `processors::names::CONST_NAME` constant (value = stringify!(field_name))
/// - `field_name`: the field name in `ProcessorConfig` and the TOML section name `[processor.field_name]`
/// - `ConfigType`: the config struct (must impl Default, Serialize, Deserialize, Clone, KnownFields, ScanDefaults)
/// - `ProcessorType`: the processor struct (must impl ProductDiscovery, have `fn new(config) -> Self`)
```

- [ ] **Step 2: Update gen_processor_dispatch in builder/mod.rs**

Change macro signature from:
```rust
    ( $( $const_name:ident, $field:ident, $config_type:ty, $proc_type:ident,
         ($scan_dir:expr, $exts:expr, $excl:expr); )* ) => {
```
To:
```rust
    ( $( $const_name:ident, $field:ident, $config_type:ty, $proc_type:ident; )* ) => {
```

In `create_processor_for_instance` (line 103), change:
```rust
                        cfg.scan.resolve($scan_dir, $exts, $excl);
```
To:
```rust
                        cfg.scan.resolve_from::<$config_type>();
```

Simplify `@register_default` arm — remove `$scan_dir, $exts, $excl` params:
```rust
    (@register_default $processors:ident, $const_name:ident, $field:ident, $config_type:ty, $proc_type:ident) => {
        {
            let mut cfg = <$config_type>::default();
            cfg.scan.resolve_from::<$config_type>();
            let proc: proc_mod::$proc_type = proc_mod::$proc_type::new(cfg);
            Builder::register(&mut $processors, proc_names::$const_name, proc);
        }
    };
```

Update the call site in `create_all_default_processors`:
```rust
                gen_processor_dispatch!(@register_default processors, $const_name, $field, $config_type, $proc_type);
```

- [ ] **Step 3: Update gen_processor_config in config/mod.rs**

Change macro signature from:
```rust
    ( $( $const_name:ident, $field:ident, $config_type:ty, $proc_type:ty,
         ($scan_dir:expr, $exts:expr, $excl:expr); )* ) => {
```
To:
```rust
    ( $( $const_name:ident, $field:ident, $config_type:ty, $proc_type:ty; )* ) => {
```

In `resolve_instance_scan_defaults` (line 452), change:
```rust
                        cfg.scan.resolve($scan_dir, $exts, $excl);
```
To:
```rust
                        cfg.scan.resolve_from::<$config_type>();
```

In `defconfig_json` (line 524), change:
```rust
                        cfg.scan.resolve($scan_dir, $exts, $excl);
```
To:
```rust
                        cfg.scan.resolve_from::<$config_type>();
```

Replace `default_scan_dir_for` with `default_src_dirs_for`:
```rust
            pub(crate) fn default_src_dirs_for(type_name: &str) -> Option<&'static [&'static str]> {
                match type_name {
                    $( stringify!($field) => Some(<$config_type as ScanDefaults>::default_src_dirs()), )*
                    _ => None,
                }
            }
```

- [ ] **Step 4: Update caller of default_scan_dir_for**

In `src/config/mod.rs:1022`, change:
```rust
    if let Some("") = ProcessorConfig::default_scan_dir_for(type_name)
```
To:
```rust
    if ProcessorConfig::default_src_dirs_for(type_name).is_some_and(|dirs| dirs.is_empty())
```

- [ ] **Step 5: Update main.rs terms command**

In `src/main.rs:381`, change:
```rust
            terms_config.scan.resolve("", &[".md"], config::MARKDOWN_EXCLUDE_DIRS);
```
To:
```rust
            terms_config.scan.resolve_from::<config::TermsConfig>();
```

- [ ] **Step 6: Remove old resolve() method and update scan_config_from_toml**

In `src/config/mod.rs`, remove the old `resolve()` method.

Update `scan_config_from_toml` signature — change `default_scan_dir: &str` to `default_src_dirs: &[&str]`:

```rust
pub(crate) fn scan_config_from_toml(
    value: &toml::Value,
    default_src_dirs: &[&str],
    default_src_extensions: &[&str],
    default_exclude_dirs: &[&str],
) -> ScanConfig {
```

Inline the resolve logic:
```rust
    if scan.src_dirs.is_none() {
        scan.src_dirs = Some(default_src_dirs.iter().map(|s| s.to_string()).collect());
    }
    if scan.src_extensions.is_none() {
        scan.src_extensions = Some(default_src_extensions.iter().map(|s| s.to_string()).collect());
    }
    if scan.src_exclude_dirs.is_none() {
        scan.src_exclude_dirs = Some(default_exclude_dirs.iter().map(|s| s.to_string()).collect());
    }
    if scan.src_exclude_files.is_none() {
        scan.src_exclude_files = Some(Vec::new());
    }
    if scan.src_exclude_paths.is_none() {
        scan.src_exclude_paths = Some(Vec::new());
    }
    if scan.src_files.is_none() {
        scan.src_files = Some(Vec::new());
    }
    scan
```

Update Lua plugin caller in `src/processors/lua_processor.rs:58`:
```rust
        let scan_config = scan_config_from_toml(&config_value, &[], &[], &[]);
```

- [ ] **Step 7: Run tests**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 8: Commit**

```bash
git add src/registry.rs src/config/mod.rs src/builder/mod.rs src/main.rs src/processors/lua_processor.rs
git commit -m "refactor: remove scan_dir from registry, use ScanDefaults trait everywhere"
```

---

### Task 6: Clean up stale references

**Files:**
- Modify: `src/config/mod.rs` (comments)
- Modify: `src/builder/mod.rs` (comments)
- Modify: `src/main.rs` (example config comments)
- Modify: `src/exit_code.rs` (test string)
- Modify: `src/config/tests.rs` (test field names)

- [ ] **Step 1: Update comments**

- `src/config/mod.rs` — remove/update any comments mentioning `scan_dir` parameter or `resolve_scan_defaults` taking scan_dir
- `src/builder/mod.rs:605` — change "Processors with empty scan_dir scan the project root" to "Processors with empty default_src_dirs scan the project root"
- `src/main.rs:517` — change `# scan_dir = "tera.templates"` to `# src_dirs = ["tera.templates"]` in example config
- `src/main.rs:530` — change `# scan_dir = "src"` to `# src_dirs = ["src"]` in example config

- [ ] **Step 2: Update test strings**

In `src/exit_code.rs:133`, change:
```rust
            "Invalid config:\n[processor.pandoc]: field 'scan_dir' must be a string",
```
To a different example that doesn't reference scan_dir, or remove it if scan_dir is no longer a recognized field. Since `scan_dir` is not a ScanConfig field (never was — it was only internal), this test string may be testing a scenario that can't happen. Change to:
```rust
            "Invalid config:\n[processor.pandoc]: field 'src_dirs' must be an array",
```

In `src/config/tests.rs`, the tests at lines 169, 194, 201 use `scan_dir` as an arbitrary TOML field name for variable substitution. These test variable expansion, not scan_dir. Change them to use `some_field`:

Line 169:
```rust
    let content = "[vars]\nmy_dir = \"templates\"\n\n[processor]\nsome_field = \"${my_dir}\"\n";
    let result = substitute_variables(content).expect("variable substitution failed");
    assert!(result.contains("some_field = \"templates\""));
```

Line 194:
```rust
    let content = "[processor]\nsome_field = \"src\"\n";
```

Line 201:
```rust
    let content = "[processor]\nsome_field = \"${undefined}\"\n";
```

- [ ] **Step 3: Run tests**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 4: Run clippy**

Run: `cargo clippy -- -D warnings`
Expected: No warnings.

- [ ] **Step 5: Commit**

```bash
git add src/config/mod.rs src/builder/mod.rs src/main.rs src/exit_code.rs src/config/tests.rs
git commit -m "chore: clean up stale scan_dir references in comments and tests"
```

---

### Task 7: Final verification

- [ ] **Step 1: Run full test suite**

Run: `cargo test`
Expected: All tests pass.

- [ ] **Step 2: Run clippy**

Run: `cargo clippy -- -D warnings`
Expected: No warnings.

- [ ] **Step 3: Verify no scan_dir in registry**

Run: `grep -n 'scan_dir' src/registry.rs`
Expected: No matches.

- [ ] **Step 4: Verify old resolve() is gone**

Run: `grep -rn '\.resolve(' src/config/mod.rs src/builder/mod.rs src/main.rs`
Expected: No matches. Only `resolve_from` calls should exist.

- [ ] **Step 5: Verify all configs implement ScanDefaults**

Run: `grep -c 'impl ScanDefaults for' src/config/processor_configs.rs`
Expected: 88 (one per processor in the registry).

- [ ] **Step 6: Verify scan_dir doesn't appear in code (except tests/comments about the old approach)**

Run: `grep -rn 'scan_dir' src/ --include='*.rs' | grep -v '//\|test\|tests.rs'`
Expected: Only `scan_config_from_toml`'s parameter name (which is now `default_src_dirs`), so no matches.
