cargo_perf/lib.rs
1//! # cargo-perf
2//!
3//! Static analysis for async Rust performance anti-patterns.
4//!
5//! cargo-perf complements `clippy` with checks specific to async code and
6//! common loop-related performance issues that clippy doesn't catch.
7//!
8//! ## Quick Start
9//!
10//! ```no_run
11//! use cargo_perf::{analyze, Config};
12//! use std::path::Path;
13//!
14//! let config = Config::default();
15//! let diagnostics = analyze(Path::new("src/"), &config).unwrap();
16//!
17//! for diag in diagnostics {
18//! println!("{}: {} at {}:{}",
19//! diag.rule_id,
20//! diag.message,
21//! diag.file_path.display(),
22//! diag.line
23//! );
24//! }
25//! ```
26//!
27//! ## Rules
28//!
29//! cargo-perf includes 12 rules organized into categories:
30//!
31//! ### Async Rules (Errors)
32//! - `async-block-in-async`: Blocking std calls in async functions
33//! - `lock-across-await`: Lock guards held across `.await` points
34//!
35//! ### Database Rules (Errors)
36//! - `n-plus-one-query`: Database queries inside loops (SQLx, Diesel, SeaORM)
37//!
38//! ### Async Warnings
39//! - `unbounded-channel`: Unbounded channels that can cause memory exhaustion
40//! - `unbounded-spawn`: Task spawning in loops without concurrency limits
41//!
42//! ### Loop Rules (Warnings)
43//! - `clone-in-hot-loop`: `.clone()` on heap types inside loops
44//! - `regex-in-loop`: `Regex::new()` inside loops
45//! - `format-in-loop`: `format!()` inside loops
46//! - `string-concat-loop`: String `+` operator in loops
47//! - `vec-no-capacity`: `Vec::new()` + push in loop
48//! - `mutex-in-loop`: `Mutex::lock()` inside loops
49//!
50//! ### Iterator Rules (Warnings)
51//! - `collect-then-iterate`: `.collect().iter()` anti-pattern
52//!
53//! ## Extending with Custom Rules
54//!
55//! Use the [`plugin`] module to add custom rules:
56//!
57//! ```rust,ignore
58//! use cargo_perf::plugin::{PluginRegistry, analyze_with_plugins};
59//!
60//! let mut registry = PluginRegistry::new();
61//! registry.add_builtin_rules();
62//! registry.add_rule(Box::new(MyCustomRule));
63//!
64//! let diagnostics = analyze_with_plugins(path, &config, ®istry)?;
65//! ```
66//!
67//! ## Configuration
68//!
69//! Rules can be configured via `cargo-perf.toml`:
70//!
71//! ```toml
72//! [rules]
73//! async-block-in-async = "deny" # error
74//! clone-in-hot-loop = "warn" # warning
75//! regex-in-loop = "allow" # disabled
76//! ```
77//!
78//! ## Suppression
79//!
80//! Suppress warnings inline:
81//!
82//! ```rust,ignore
83//! // Attribute-based (function/item scope)
84//! #[allow(cargo_perf::clone_in_hot_loop)]
85//! fn my_function() { /* ... */ }
86//!
87//! // Comment-based (next line only)
88//! // cargo-perf-ignore: clone-in-hot-loop
89//! let x = data.clone();
90//! ```
91
92pub mod baseline;
93pub mod config;
94pub mod discovery;
95pub mod engine;
96pub mod error;
97pub mod fix;
98#[cfg(feature = "lsp")]
99pub mod lsp;
100pub mod plugin;
101pub mod reporter;
102pub mod rules;
103pub mod suppression;
104
105pub use baseline::Baseline;
106pub use config::Config;
107pub use engine::{AnalysisContext, AnalysisProgress, Engine};
108pub use error::{Error, Result};
109pub use fix::FixError;
110pub use plugin::{analyze_with_plugins, PluginRegistry, PluginRegistryBuilder};
111pub use rules::{Diagnostic, Fix, Replacement, Rule, Severity};
112
113/// Analyze Rust files at the given path for performance anti-patterns.
114///
115/// # Arguments
116///
117/// * `path` - A file or directory to analyze. Directories are traversed recursively.
118/// * `config` - Configuration controlling rule severity and output options.
119///
120/// # Returns
121///
122/// A vector of diagnostics found during analysis. Empty if no issues detected.
123///
124/// # Errors
125///
126/// Returns an error if file I/O fails or parsing encounters invalid syntax.
127///
128/// # Example
129///
130/// ```no_run
131/// use cargo_perf::{analyze, Config, Severity};
132/// use std::path::Path;
133///
134/// let config = Config::default();
135/// let diagnostics = analyze(Path::new("src/"), &config)?;
136///
137/// let errors: Vec<_> = diagnostics
138/// .iter()
139/// .filter(|d| d.severity == Severity::Error)
140/// .collect();
141///
142/// if !errors.is_empty() {
143/// eprintln!("Found {} errors", errors.len());
144/// }
145/// # Ok::<(), cargo_perf::Error>(())
146/// ```
147pub fn analyze(path: &std::path::Path, config: &Config) -> Result<Vec<Diagnostic>> {
148 let engine = Engine::new(config);
149 engine.analyze(path)
150}