Skip to content

Commit 74fe948

Browse files
chore(clippy): bring cargo clippy to zero warnings and enforce -D warnings in CI (#205)
* chore(clippy): relocate cfg(test)-gated imports into test modules EnvPlaceholder (mcp_registry.rs) and DEFAULT_MCP_SERVER_PORT (mcp_server.rs) were imported at module level but only consumed inside #[cfg(test)] blocks. This made them look unused to clippy's lib pass and invited a cargo-fix regression that would strip them and break the test build. Moving the imports into the test module where they are used eliminates the warning without risking the test build. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): prefix unused variables with underscore Resolves 'unused variable' warnings across 6 files by adding the _ prefix idiom. In claude_settings.rs the mut binding was also dropped because the variable was never mutated after the read. All sites are in test code or Tauri command handlers where the variable captures a value kept for lifetime purposes (app_handle.clone()) but not referenced directly. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): replace useless vec! with arrays Sites in test code that collect JSONL lines or MCP configs into a temporary collection only to call .join() or pass as a slice. Array literal is cheaper (no heap allocation) and expresses intent more directly. 26 sites in session_explorer.rs tests, 2 in config_writer.rs tests — all previously flagged by 'clippy::useless_vec'. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): resolve dead-code warnings via targeted allows 66 dead-code warnings broken into four sub-patterns, each with its own appropriate fix: 1. Test-helper pub(crate) fns (45 sites across 9 files): #[cfg_attr(not(test), allow(dead_code))]. These are #[cfg(test)]-consumed helpers exposed as pub(crate) so the test module can bypass Tauri State<> wrappers. The attr suppresses the warning for non-test builds while preserving genuine dead-code detection in test builds. 2. Vestigial typed-parser vendor hierarchy: 4 enums (CodexMcp, CopilotMcp, CursorMcp, GeminiMcp) + 8 Stdio/Http variant structs. These were scaffolded for a typed intermediate-representation parse path that was never wired up; current code uses ParsedXxxMcp normalization directly. Annotated with #[allow(dead_code)] + a comment noting the design intent so they remain available for future typed-parse work. 3. Serde-deser false positives (DevcontainerConfig, ProfileItem, SpinnerVerbConfig, 3 fields on RawRecord/RawMessage/ToolManagerServer). Clippy's 'never constructed' lint does not see serde's Deserialize-generated constructors. Standard #[allow(dead_code)] idiom. 4. One genuine deletion: Database::from_connection in db/schema.rs. Already #[cfg(test)]-gated with zero callers; sibling Database::in_memory() is the actual entry point used throughout tests. Dead-code warning count: 66 -> 0. Total clippy warnings: 134 -> 68. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): apply mechanical autofix suggestions Broad pass applying clippy's machine-applicable suggestions now that the cfg(test)-gated-import and unused-variable landmines have been resolved. Covers: - manual Option::map rewrites (several sites in mcp_client.rs, skills.rs, session_explorer.rs) - assert_eq!(x, true/false) -> assert!(x) / assert!(!x) - useless format! dropped - redundant closures replaced with the function itself - Iterator::last on DoubleEndedIterator -> .next_back() - map_or simplification - Option::map().flatten() -> and_then - map().values() iteration cleanup - assorted rustfmt normalization introduced by the rewrites Additionally: replaced a tautological bool assertion in debug_logger.rs (assert!(enabled == true || enabled == false)) with 'let _ = is_debug_enabled();' matching the adjacent test's 'let _ = path;' pattern — the assertion was already always-true before clippy rewrote it, and the newer nonminimal_bool lint now flags the explicit form as an error under -D warnings. Clippy warning count: 68 -> 33. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): accept &Path instead of &PathBuf in helper signatures Clippy::ptr_arg prefers &Path over &PathBuf because the former is a slice type and avoids a pointless heap indirection. 10 sites across 3 files: - src/services/debug_logger.rs (enable_debug_logging, get_logs_dir, get_debug_flag_path) - src/utils/paths.rs (project_mcp_file, project_settings_file) - src/utils/opencode_paths.rs (5 project_opencode_* helpers) Call sites already pass &PathBuf, which auto-dereferences to &Path — no caller changes required. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): allow type_complexity on DB-row-tuple helpers 7 command helpers use multi-field tuples (7-10 fields) for rusqlite row extraction. These tuples ARE the domain shape — introducing type aliases would just duplicate the tuple next to the fn without adding meaning, and the existing file-local McpTuple aliases in services/*_config.rs follow a different shape (writer tuples, not reader tuples). Annotating with #[allow(clippy::type_complexity)] on the containing fn matches the established pattern for 'this IS the data shape' cases. Co-Authored-By: Claude <noreply@anthropic.com> * chore(clippy): resolve long-tail warnings to reach zero Final cleanup batch: - opencode_paths.rs: #[cfg(test)] use std::path::Path; — Path is only needed by the cfg(test)-gated helpers that now take &Path. - debug_logger.rs: &PathBuf -> &Path on 3 more helpers (persist_debug_enabled, is_debug_persisted, init_from_persisted). Missed in the first PathBuf pass. - db/models.rs: #[cfg_attr(not(test), allow(dead_code))] on is_powerline and is_powerline_round — test-only consumers. - docker/client.rs: #[allow(dead_code)] on connect_with_params (private helper used via ping_host) and ping_host (public API held for future use). - docker/devcontainer.rs: #[allow(dead_code)] on the impl block — several methods are defined for the DevcontainerConfig API shape but not all are consumed today. - commands/mod.rs: #[allow(clippy::module_inception)] on `pub mod commands` — the nested name mirrors the Claude Code commands domain concept; renaming would cascade through imports. - mcp_gateway/tools.rs: #[allow(clippy::manual_async_fn)] on impl ServerHandler — rmcp's trait uses impl Future return shape; async fn in traits isn't usable here without widening the bound. - commands/scanner.rs: removed tautological assert!(true) in the compilation-smoke-test (comment documents intent instead). - services/scanner.rs: - #[allow(clippy::too_many_arguments)] on get_or_create_mcp (9/7) - strip_prefix("---") rewrite in parse_frontmatter - assert!(!fm.contains_key("empty_key")) replaces .get().is_none() - #[allow(non_snake_case)] on test_parse_skill_file_with_allowedTools_camelCase where the camelCase mirrors the schema field under test. - services/mcp_registry.rs: filter_map always-Some -> map. Clippy warning count: 17 -> 0. Tests: 2021 passing. Ready to flip CI gate. Co-Authored-By: Claude <noreply@anthropic.com> * ci(clippy): enforce -D warnings on lib + tests Flips the Clippy job to block CI on warnings. Two tightenings: - Drop 'continue-on-error: true'. The comment said 'initially' — eight commits of cleanup later, the baseline is zero warnings, so the gate becomes meaningful. - Add '--lib --tests' to the clippy invocation so the ~120 warnings that lived exclusively in the test target are covered too. Without this the prior gate (if enforced) would have allowed test-only lint debt to accumulate invisibly. All other CI jobs untouched. No policy change beyond Clippy enforcement. Co-Authored-By: Claude <noreply@anthropic.com> * fix(clippy): address rust 1.95 warnings --------- Co-authored-by: Scot Campbell <prefrontalsys@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 63671e3 commit 74fe948

43 files changed

Lines changed: 252 additions & 202 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/rust-tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ jobs:
9292

9393
- name: Run Clippy
9494
working-directory: src-tauri
95-
run: cargo clippy --all-features -- -D warnings
96-
continue-on-error: true # Don't fail the build on clippy warnings initially
95+
run: cargo clippy --all-targets --all-features -- -D warnings
9796

9897
fmt:
9998
name: Format

src-tauri/src/commands/commands.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,11 @@ pub fn get_global_commands(
329329
db: State<'_, Arc<Mutex<Database>>>,
330330
) -> Result<Vec<GlobalCommand>, String> {
331331
let db = db.lock().map_err(|e| e.to_string())?;
332-
let query = format!(
333-
"SELECT gc.id, gc.command_id, gc.is_enabled,
332+
let query = "SELECT gc.id, gc.command_id, gc.is_enabled,
334333
c.id, c.name, c.description, c.content, c.allowed_tools, c.argument_hint, c.model, c.tags, c.source, c.source_path, c.is_favorite, c.created_at, c.updated_at
335334
FROM global_commands gc
336335
JOIN commands c ON gc.command_id = c.id
337-
ORDER BY c.name"
338-
);
336+
ORDER BY c.name".to_string();
339337
let mut stmt = db.conn().prepare(&query).map_err(|e| e.to_string())?;
340338

341339
let commands = stmt
@@ -644,13 +642,11 @@ pub fn toggle_project_command(
644642
.map_err(|e| e.to_string())?;
645643

646644
// Get the command and project path
647-
let query = format!(
648-
"SELECT c.id, c.name, c.description, c.content, c.allowed_tools, c.argument_hint, c.model, c.tags, c.source, c.source_path, c.is_favorite, c.created_at, c.updated_at, p.path
645+
let query = "SELECT c.id, c.name, c.description, c.content, c.allowed_tools, c.argument_hint, c.model, c.tags, c.source, c.source_path, c.is_favorite, c.created_at, c.updated_at, p.path
649646
FROM project_commands pc
650647
JOIN commands c ON pc.command_id = c.id
651648
JOIN projects p ON pc.project_id = p.id
652-
WHERE pc.id = ?"
653-
);
649+
WHERE pc.id = ?".to_string();
654650
let mut stmt = db_guard.conn().prepare(&query).map_err(|e| e.to_string())?;
655651

656652
let (command, project_path): (Command, String) = stmt
@@ -706,14 +702,12 @@ pub fn get_project_commands(
706702
project_id: i64,
707703
) -> Result<Vec<ProjectCommand>, String> {
708704
let db = db.lock().map_err(|e| e.to_string())?;
709-
let query = format!(
710-
"SELECT pc.id, pc.command_id, pc.is_enabled,
705+
let query = "SELECT pc.id, pc.command_id, pc.is_enabled,
711706
c.id, c.name, c.description, c.content, c.allowed_tools, c.argument_hint, c.model, c.tags, c.source, c.source_path, c.is_favorite, c.created_at, c.updated_at
712707
FROM project_commands pc
713708
JOIN commands c ON pc.command_id = c.id
714709
WHERE pc.project_id = ?
715-
ORDER BY c.name"
716-
);
710+
ORDER BY c.name".to_string();
717711
let mut stmt = db.conn().prepare(&query).map_err(|e| e.to_string())?;
718712

719713
let commands = stmt

src-tauri/src/commands/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub fn sync_global_config(db: State<'_, Arc<Mutex<Database>>>) -> Result<(), Str
7070
}
7171

7272
/// Sync global config from database to disk (reusable helper without Tauri State)
73+
#[allow(clippy::type_complexity)]
7374
pub(crate) fn sync_global_config_from_db(db: &Database) -> Result<(), String> {
7475
use crate::commands::settings::get_enabled_editors_from_db;
7576
use crate::services::{

src-tauri/src/commands/docker_hosts.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub fn get_all_docker_hosts(
1414
#[tauri::command]
1515
pub fn create_docker_host(
1616
db: State<'_, Arc<Mutex<Database>>>,
17-
request: CreateDockerHostRequest,
17+
_request: CreateDockerHostRequest,
1818
) -> Result<DockerHost, String> {
1919
let _db = db.lock().map_err(|e| e.to_string())?;
2020
Err("Docker host feature not yet implemented".to_string())
@@ -23,20 +23,20 @@ pub fn create_docker_host(
2323
#[tauri::command]
2424
pub fn update_docker_host(
2525
db: State<'_, Arc<Mutex<Database>>>,
26-
id: i64,
27-
request: CreateDockerHostRequest,
26+
_id: i64,
27+
_request: CreateDockerHostRequest,
2828
) -> Result<DockerHost, String> {
2929
let _db = db.lock().map_err(|e| e.to_string())?;
3030
Err("Docker host feature not yet implemented".to_string())
3131
}
3232

3333
#[tauri::command]
34-
pub fn delete_docker_host(db: State<'_, Arc<Mutex<Database>>>, id: i64) -> Result<(), String> {
34+
pub fn delete_docker_host(db: State<'_, Arc<Mutex<Database>>>, _id: i64) -> Result<(), String> {
3535
let _db = db.lock().map_err(|e| e.to_string())?;
3636
Err("Docker host feature not yet implemented".to_string())
3737
}
3838

3939
#[tauri::command]
40-
pub fn test_docker_host(id: i64) -> Result<bool, String> {
40+
pub fn test_docker_host(_id: i64) -> Result<bool, String> {
4141
Err("Docker host feature not yet implemented".to_string())
4242
}

src-tauri/src/commands/hooks.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ pub fn duplicate_hook(
910910
// ============================================================================
911911

912912
/// Create a hook in the database (no file sync)
913+
#[cfg_attr(not(test), allow(dead_code))]
913914
pub(crate) fn create_hook_in_db(db: &Database, hook: &CreateHookRequest) -> Result<Hook, String> {
914915
let tags_json = hook
915916
.tags
@@ -939,6 +940,7 @@ pub(crate) fn create_hook_in_db(db: &Database, hook: &CreateHookRequest) -> Resu
939940
}
940941

941942
/// Get a hook by ID from the database
943+
#[cfg_attr(not(test), allow(dead_code))]
942944
pub(crate) fn get_hook_by_id(db: &Database, id: i64) -> Result<Hook, String> {
943945
let mut stmt = db
944946
.conn()
@@ -971,6 +973,7 @@ pub(crate) fn get_all_hooks_from_db(db: &Database) -> Result<Vec<Hook>, String>
971973
}
972974

973975
/// Update a hook in the database (no file sync)
976+
#[cfg_attr(not(test), allow(dead_code))]
974977
pub(crate) fn update_hook_in_db(
975978
db: &Database,
976979
id: i64,
@@ -1020,6 +1023,7 @@ pub(crate) fn update_hook_in_db(
10201023
}
10211024

10221025
/// Delete a hook from the database (no file sync)
1026+
#[cfg_attr(not(test), allow(dead_code))]
10231027
pub(crate) fn delete_hook_from_db(db: &Database, id: i64) -> Result<(), String> {
10241028
db.conn()
10251029
.execute("DELETE FROM hooks WHERE id = ?", [id])
@@ -1028,6 +1032,7 @@ pub(crate) fn delete_hook_from_db(db: &Database, id: i64) -> Result<(), String>
10281032
}
10291033

10301034
/// Add a hook to global hooks in the database (no file sync)
1035+
#[cfg_attr(not(test), allow(dead_code))]
10311036
pub(crate) fn add_global_hook_in_db(db: &Database, hook_id: i64) -> Result<(), String> {
10321037
db.conn()
10331038
.execute(
@@ -1039,6 +1044,7 @@ pub(crate) fn add_global_hook_in_db(db: &Database, hook_id: i64) -> Result<(), S
10391044
}
10401045

10411046
/// Get all global hooks from the database
1047+
#[cfg_attr(not(test), allow(dead_code))]
10421048
pub(crate) fn get_global_hooks_from_db(db: &Database) -> Result<Vec<GlobalHook>, String> {
10431049
let mut stmt = db
10441050
.conn()
@@ -1068,6 +1074,7 @@ pub(crate) fn get_global_hooks_from_db(db: &Database) -> Result<Vec<GlobalHook>,
10681074
}
10691075

10701076
/// Toggle a global hook in the database (no file sync)
1077+
#[cfg_attr(not(test), allow(dead_code))]
10711078
pub(crate) fn toggle_global_hook_in_db(
10721079
db: &Database,
10731080
id: i64,
@@ -1083,6 +1090,7 @@ pub(crate) fn toggle_global_hook_in_db(
10831090
}
10841091

10851092
/// Remove a global hook from the database (no file sync)
1093+
#[cfg_attr(not(test), allow(dead_code))]
10861094
pub(crate) fn remove_global_hook_from_db(db: &Database, hook_id: i64) -> Result<(), String> {
10871095
db.conn()
10881096
.execute("DELETE FROM global_hooks WHERE hook_id = ?", [hook_id])

src-tauri/src/commands/mcp.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ pub(crate) fn delete_mcp_impl(db: &Database, id: i64) -> Result<(), String> {
235235
}
236236

237237
/// Duplicate an MCP in the database
238+
#[allow(clippy::type_complexity)]
238239
pub(crate) fn duplicate_mcp_impl(db: &Database, id: i64) -> Result<Mcp, String> {
239240
// Get original
240241
let mut stmt = db
@@ -319,18 +320,22 @@ pub(crate) fn generate_duplicate_name(name: &str) -> String {
319320
}
320321

321322
// Convenience aliases (promoted from #[cfg(test)] for cross-module reuse)
323+
#[cfg_attr(not(test), allow(dead_code))]
322324
pub(crate) fn create_mcp_in_db(db: &Database, mcp: &CreateMcpRequest) -> Result<Mcp, String> {
323325
create_mcp_impl(db, mcp)
324326
}
325327

328+
#[cfg_attr(not(test), allow(dead_code))]
326329
pub(crate) fn get_mcp_by_id(db: &Database, id: i64) -> Result<Mcp, String> {
327330
get_mcp_impl(db, id)
328331
}
329332

333+
#[cfg_attr(not(test), allow(dead_code))]
330334
pub(crate) fn get_all_mcps_from_db(db: &Database) -> Result<Vec<Mcp>, String> {
331335
get_all_mcps_impl(db)
332336
}
333337

338+
#[cfg_attr(not(test), allow(dead_code))]
334339
pub(crate) fn update_mcp_in_db(
335340
db: &Database,
336341
id: i64,
@@ -339,10 +344,12 @@ pub(crate) fn update_mcp_in_db(
339344
update_mcp_impl(db, id, mcp)
340345
}
341346

347+
#[cfg_attr(not(test), allow(dead_code))]
342348
pub(crate) fn delete_mcp_from_db(db: &Database, id: i64) -> Result<(), String> {
343349
delete_mcp_impl(db, id)
344350
}
345351

352+
#[cfg_attr(not(test), allow(dead_code))]
346353
pub(crate) fn toggle_global_mcp_in_db(db: &Database, id: i64, enabled: bool) -> Result<(), String> {
347354
toggle_global_mcp_impl(db, id, enabled)
348355
}
@@ -708,7 +715,7 @@ mod tests {
708715
let db = Database::in_memory().unwrap();
709716
let req = sample_stdio_mcp();
710717
let created = create_mcp_in_db(&db, &req).unwrap();
711-
let original_updated = created.updated_at.clone();
718+
let _original_updated = created.updated_at.clone();
712719

713720
// Small delay to ensure timestamp changes
714721
std::thread::sleep(std::time::Duration::from_millis(10));

src-tauri/src/commands/mcp_registry.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::db::Database;
2-
use crate::services::mcp_registry::{EnvPlaceholder, RegistryClient, RegistryMcpEntry};
2+
use crate::services::mcp_registry::{RegistryClient, RegistryMcpEntry};
33
use rusqlite::params;
44
use serde::{Deserialize, Serialize};
55
use std::sync::{Arc, Mutex};
@@ -94,18 +94,15 @@ pub fn import_mcp_from_registry_in_db(
9494
let args_json = entry
9595
.args
9696
.as_ref()
97-
.map(|a| serde_json::to_string(a).ok())
98-
.flatten();
97+
.and_then(|a| serde_json::to_string(a).ok());
9998
let headers_json = entry
10099
.headers
101100
.as_ref()
102-
.map(|h| serde_json::to_string(h).ok())
103-
.flatten();
101+
.and_then(|h| serde_json::to_string(h).ok());
104102
let env_json = entry
105103
.env
106104
.as_ref()
107-
.map(|e| serde_json::to_string(e).ok())
108-
.flatten();
105+
.and_then(|e| serde_json::to_string(e).ok());
109106

110107
db.conn()
111108
.execute(
@@ -166,6 +163,7 @@ pub fn get_registry_mcp_by_id(db: &Database, id: i64) -> Result<RegistryMcpEntry
166163
#[cfg(test)]
167164
mod tests {
168165
use super::*;
166+
use crate::services::mcp_registry::EnvPlaceholder;
169167
use std::collections::HashMap;
170168

171169
// =========================================================================

src-tauri/src/commands/mcp_server.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
//! These commands allow the frontend to start/stop and configure the MCP server.
44
55
use crate::db::Database;
6-
use crate::mcp_server::server::{
7-
generate_self_mcp_entry, McpServerConfig, McpServerStatus, DEFAULT_MCP_SERVER_PORT,
8-
};
6+
use crate::mcp_server::server::{generate_self_mcp_entry, McpServerConfig, McpServerStatus};
97
use crate::mcp_server::McpServerState;
108
use log::info;
119
use serde_json::Value;
@@ -148,6 +146,7 @@ pub fn is_self_mcp_in_library(db: State<'_, Arc<Mutex<Database>>>) -> Result<boo
148146
#[cfg(test)]
149147
mod tests {
150148
use super::*;
149+
use crate::mcp_server::server::DEFAULT_MCP_SERVER_PORT;
151150

152151
#[test]
153152
fn test_mcp_server_config_serde() {

src-tauri/src/commands/mcp_session.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct McpSessionData {
2525
}
2626

2727
/// Extract MCP session data from the database (no Tauri State dependency)
28+
#[allow(clippy::type_complexity)]
2829
pub(crate) fn get_mcp_session_data_from_db(
2930
db: &Database,
3031
mcp_id: i64,
@@ -84,6 +85,7 @@ pub(crate) fn get_mcp_session_data_from_db(
8485
}
8586

8687
/// Validate MCP session data before starting a session
88+
#[cfg_attr(not(test), allow(dead_code))]
8789
pub(crate) fn validate_mcp_session_data(data: &McpSessionData) -> Result<(), String> {
8890
if data.source == "system" {
8991
if data.url.is_none() {

src-tauri/src/commands/mcp_test.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::sync::{Arc, Mutex};
1010
use tauri::State;
1111

1212
/// Extract MCP test data including source field from the database (no Tauri State dependency)
13+
#[allow(clippy::type_complexity)]
1314
pub fn get_mcp_test_data_with_source_from_db(
1415
db: &Database,
1516
mcp_id: i64,
@@ -163,6 +164,8 @@ pub fn test_mcp_config(
163164
// ============================================================================
164165

165166
/// Extract MCP data from database for testing
167+
#[cfg_attr(not(test), allow(dead_code))]
168+
#[allow(clippy::type_complexity)]
166169
pub fn get_mcp_test_data_from_db(
167170
db: &Database,
168171
mcp_id: i64,
@@ -217,6 +220,7 @@ pub fn get_mcp_test_data_from_db(
217220
}
218221

219222
/// Validate MCP config before testing
223+
#[cfg_attr(not(test), allow(dead_code))]
220224
pub fn validate_mcp_config(
221225
mcp_type: &str,
222226
command: Option<&str>,

0 commit comments

Comments
 (0)