1use serde::Serialize;
2
3use super::RuleLevel;
4
5#[cfg(test)]
6mod tests;
7#[cfg(test)]
8mod validate;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
11#[serde(rename_all = "kebab-case")]
12pub enum Producer {
13 Clippy,
14 CargoHealth,
15 SourceKernel,
16 Structure,
17 Repo,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
26pub struct CatalogEntry {
27 pub id: &'static str,
28 pub category: &'static str,
29 pub producer: Producer,
30 pub default_level: RuleLevel,
31 pub tier: RuleTier,
32 pub help: &'static str,
33}
34
35#[must_use]
37pub fn catalog() -> Vec<CatalogEntry> {
38 CATALOG
39 .iter()
40 .map(|definition| CatalogEntry {
41 id: definition.id,
42 category: definition.category,
43 producer: definition.producer,
44 default_level: definition.default_level,
45 tier: definition.tier,
46 help: definition.help,
47 })
48 .collect()
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
61pub enum RuleTier {
62 P0,
63 P1,
64 P2,
65 P3,
66}
67
68impl RuleTier {
69 #[cfg(test)]
70 pub(crate) const ALL: [Self; 4] = [Self::P0, Self::P1, Self::P2, Self::P3];
71
72 pub const fn as_str(self) -> &'static str {
73 match self {
74 Self::P0 => "P0",
75 Self::P1 => "P1",
76 Self::P2 => "P2",
77 Self::P3 => "P3",
78 }
79 }
80
81 #[cfg(test)]
85 pub(crate) fn parse(value: &str) -> Option<Self> {
86 match value {
87 "P0" => Some(Self::P0),
88 "P1" => Some(Self::P1),
89 "P2" => Some(Self::P2),
90 "P3" => Some(Self::P3),
91 _ => None,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
97pub(crate) struct RuleDefinition {
98 pub(crate) id: &'static str,
99 pub(crate) category: &'static str,
100 pub(crate) producer: Producer,
101 pub(crate) default_level: RuleLevel,
102 pub(crate) tier: RuleTier,
103 pub(crate) help: &'static str,
104}
105
106pub(crate) const CATEGORIES: [&str; 6] = [
111 "correctness",
112 "dependencies",
113 "maintainability",
114 "performance",
115 "reliability",
116 "security",
117];
118
119pub(crate) static CLIPPY_ARC_WITH_NON_SEND_SYNC: RuleDefinition = RuleDefinition {
120 id: "clippy::arc_with_non_send_sync",
121 category: "correctness",
122 producer: Producer::Clippy,
123 default_level: RuleLevel::Warn,
124 tier: RuleTier::P1,
125 help: "Use Rc for single-threaded sharing, or make the inner value Send and Sync before sharing it across threads.",
126};
127pub(crate) static CLIPPY_AWAIT_HOLDING_LOCK: RuleDefinition = RuleDefinition {
128 id: "clippy::await_holding_lock",
129 category: "correctness",
130 producer: Producer::Clippy,
131 default_level: RuleLevel::Warn,
132 tier: RuleTier::P1,
133 help: "Drop the guard before the await point, or use a lock designed to be held across await.",
134};
135pub(crate) static CLIPPY_AWAIT_HOLDING_REFCELL_REF: RuleDefinition = RuleDefinition {
136 id: "clippy::await_holding_refcell_ref",
137 category: "correctness",
138 producer: Producer::Clippy,
139 default_level: RuleLevel::Warn,
140 tier: RuleTier::P1,
141 help: "Copy the borrowed value and drop the borrow before the await point.",
142};
143pub(crate) static CLIPPY_DBG_MACRO: RuleDefinition = RuleDefinition {
144 id: "clippy::dbg_macro",
145 category: "maintainability",
146 producer: Producer::Clippy,
147 default_level: RuleLevel::Warn,
148 tier: RuleTier::P3,
149 help: "Remove dbg! or replace it with intentional logging.",
150};
151pub(crate) static CLIPPY_EXIT: RuleDefinition = RuleDefinition {
152 id: "clippy::exit",
153 category: "reliability",
154 producer: Producer::Clippy,
155 default_level: RuleLevel::Warn,
156 tier: RuleTier::P2,
157 help: "Return an error to the caller and let the entry point decide the exit status.",
158};
159pub(crate) static CLIPPY_EXPECT_USED: RuleDefinition = RuleDefinition {
160 id: "clippy::expect_used",
161 category: "reliability",
162 producer: Producer::Clippy,
163 default_level: RuleLevel::Warn,
164 tier: RuleTier::P3,
165 help: "Propagate the error with ? or handle the missing value explicitly instead of panicking.",
166};
167pub(crate) static CLIPPY_FORMAT_COLLECT: RuleDefinition = RuleDefinition {
168 id: "clippy::format_collect",
169 category: "performance",
170 producer: Producer::Clippy,
171 default_level: RuleLevel::Warn,
172 tier: RuleTier::P3,
173 help: "Write into one String with write! or push_str instead of allocating one String per item.",
174};
175pub(crate) static CLIPPY_INDEXING_SLICING: RuleDefinition = RuleDefinition {
176 id: "clippy::indexing_slicing",
177 category: "reliability",
178 producer: Producer::Clippy,
179 default_level: RuleLevel::Warn,
180 tier: RuleTier::P3,
181 help: "Use get or get_mut and handle the absent element instead of indexing, which panics out of bounds.",
182};
183pub(crate) static CLIPPY_LARGE_TYPES_PASSED_BY_VALUE: RuleDefinition = RuleDefinition {
184 id: "clippy::large_types_passed_by_value",
185 category: "performance",
186 producer: Producer::Clippy,
187 default_level: RuleLevel::Warn,
188 tier: RuleTier::P3,
189 help: "Pass the large value by reference to avoid copying it at every call.",
190};
191pub(crate) static CLIPPY_MANUAL_MEMCPY: RuleDefinition = RuleDefinition {
192 id: "clippy::manual_memcpy",
193 category: "performance",
194 producer: Producer::Clippy,
195 default_level: RuleLevel::Warn,
196 tier: RuleTier::P3,
197 help: "Use copy_from_slice or clone_from_slice instead of copying element by element.",
198};
199pub(crate) static CLIPPY_MEM_FORGET: RuleDefinition = RuleDefinition {
200 id: "clippy::mem_forget",
201 category: "reliability",
202 producer: Producer::Clippy,
203 default_level: RuleLevel::Warn,
204 tier: RuleTier::P2,
205 help: "Avoid leaking a value with drop semantics; use an explicit ownership or lifetime strategy.",
206};
207pub(crate) static CLIPPY_MISSING_SAFETY_DOC: RuleDefinition = RuleDefinition {
208 id: "clippy::missing_safety_doc",
209 category: "maintainability",
210 producer: Producer::Clippy,
211 default_level: RuleLevel::Warn,
212 tier: RuleTier::P3,
213 help: "Document in a `# Safety` section the invariants the caller must uphold before calling.",
214};
215pub(crate) static CLIPPY_MUT_MUTEX_LOCK: RuleDefinition = RuleDefinition {
216 id: "clippy::mut_mutex_lock",
217 category: "correctness",
218 producer: Producer::Clippy,
219 default_level: RuleLevel::Warn,
220 tier: RuleTier::P2,
221 help: "Use get_mut when the mutex is already exclusively borrowed; locking it again can deadlock.",
222};
223pub(crate) static CLIPPY_NON_SEND_FIELDS_IN_SEND_TY: RuleDefinition = RuleDefinition {
224 id: "clippy::non_send_fields_in_send_ty",
225 category: "correctness",
226 producer: Producer::Clippy,
227 default_level: RuleLevel::Warn,
228 tier: RuleTier::P1,
229 help: "Remove the unsafe Send implementation or ensure every field is safe to send between threads.",
230};
231pub(crate) static CLIPPY_PANIC: RuleDefinition = RuleDefinition {
232 id: "clippy::panic",
233 category: "reliability",
234 producer: Producer::Clippy,
235 default_level: RuleLevel::Warn,
236 tier: RuleTier::P3,
237 help: "Return an error instead of aborting the process on an input the caller can recover from.",
238};
239pub(crate) static CLIPPY_PANIC_IN_RESULT_FN: RuleDefinition = RuleDefinition {
240 id: "clippy::panic_in_result_fn",
241 category: "correctness",
242 producer: Producer::Clippy,
243 default_level: RuleLevel::Warn,
244 tier: RuleTier::P2,
245 help: "A function that already returns Result should report the failure as Err instead of panicking.",
246};
247pub(crate) static CLIPPY_PERMISSIONS_SET_READONLY_FALSE: RuleDefinition = RuleDefinition {
248 id: "clippy::permissions_set_readonly_false",
249 category: "security",
250 producer: Producer::Clippy,
251 default_level: RuleLevel::Warn,
252 tier: RuleTier::P1,
253 help: "Set explicit Unix permission bits instead of clearing readonly on Unix.",
254};
255pub(crate) static CLIPPY_PRINT_STDERR: RuleDefinition = RuleDefinition {
256 id: "clippy::print_stderr",
257 category: "maintainability",
258 producer: Producer::Clippy,
259 default_level: RuleLevel::Warn,
260 tier: RuleTier::P3,
261 help: "Write to a caller-provided writer or a logger instead of hard-wiring stderr.",
262};
263pub(crate) static CLIPPY_PRINT_STDOUT: RuleDefinition = RuleDefinition {
264 id: "clippy::print_stdout",
265 category: "maintainability",
266 producer: Producer::Clippy,
267 default_level: RuleLevel::Warn,
268 tier: RuleTier::P3,
269 help: "Write to a caller-provided writer or a logger instead of hard-wiring stdout.",
270};
271pub(crate) static CLIPPY_PTR_ARG: RuleDefinition = RuleDefinition {
272 id: "clippy::ptr_arg",
273 category: "performance",
274 producer: Producer::Clippy,
275 default_level: RuleLevel::Warn,
276 tier: RuleTier::P3,
277 help: "Take &[T] or &str so callers can pass any borrowed slice without owning one first.",
278};
279pub(crate) static CLIPPY_RC_BUFFER: RuleDefinition = RuleDefinition {
280 id: "clippy::rc_buffer",
281 category: "performance",
282 producer: Producer::Clippy,
283 default_level: RuleLevel::Warn,
284 tier: RuleTier::P3,
285 help: "Share the slice itself with Rc<str> or Rc<[T]> instead of wrapping an owned buffer.",
286};
287pub(crate) static CLIPPY_RC_MUTEX: RuleDefinition = RuleDefinition {
288 id: "clippy::rc_mutex",
289 category: "correctness",
290 producer: Producer::Clippy,
291 default_level: RuleLevel::Warn,
292 tier: RuleTier::P2,
293 help: "Use RefCell inside Rc for single-threaded sharing, or Arc<Mutex<T>> when the value really crosses threads.",
294};
295pub(crate) static CLIPPY_REDUNDANT_ALLOCATION: RuleDefinition = RuleDefinition {
296 id: "clippy::redundant_allocation",
297 category: "performance",
298 producer: Producer::Clippy,
299 default_level: RuleLevel::Warn,
300 tier: RuleTier::P2,
301 help: "Remove the inner allocation; one pointer indirection is enough.",
302};
303pub(crate) static CLIPPY_STABLE_SORT_PRIMITIVE: RuleDefinition = RuleDefinition {
304 id: "clippy::stable_sort_primitive",
305 category: "performance",
306 producer: Producer::Clippy,
307 default_level: RuleLevel::Warn,
308 tier: RuleTier::P3,
309 help: "Use sort_unstable on primitives; stability carries no meaning and costs an allocation.",
310};
311pub(crate) static CLIPPY_STRING_SLICE: RuleDefinition = RuleDefinition {
312 id: "clippy::string_slice",
313 category: "reliability",
314 producer: Producer::Clippy,
315 default_level: RuleLevel::Warn,
316 tier: RuleTier::P3,
317 help: "Use get on the string range and handle the absent slice; byte indexing panics inside a UTF-8 character.",
318};
319pub(crate) static CLIPPY_SUSPICIOUS_COMMAND_ARG_SPACE: RuleDefinition = RuleDefinition {
320 id: "clippy::suspicious_command_arg_space",
321 category: "correctness",
322 producer: Producer::Clippy,
323 default_level: RuleLevel::Warn,
324 tier: RuleTier::P2,
325 help: "Pass each process argument separately instead of embedding spaces in one argument.",
326};
327pub(crate) static CLIPPY_TODO: RuleDefinition = RuleDefinition {
328 id: "clippy::todo",
329 category: "correctness",
330 producer: Producer::Clippy,
331 default_level: RuleLevel::Warn,
332 tier: RuleTier::P2,
333 help: "Replace todo! with the intended implementation or remove the reachable placeholder.",
334};
335pub(crate) static CLIPPY_TOO_MANY_ARGUMENTS: RuleDefinition = RuleDefinition {
336 id: "clippy::too_many_arguments",
337 category: "maintainability",
338 producer: Producer::Clippy,
339 default_level: RuleLevel::Warn,
340 tier: RuleTier::P3,
341 help: "Group the related parameters into a struct so the signature names what it takes.",
342};
343pub(crate) static CLIPPY_TYPE_COMPLEXITY: RuleDefinition = RuleDefinition {
344 id: "clippy::type_complexity",
345 category: "maintainability",
346 producer: Producer::Clippy,
347 default_level: RuleLevel::Warn,
348 tier: RuleTier::P3,
349 help: "Name the nested type with a type alias or a dedicated struct so signatures say what they carry.",
350};
351pub(crate) static CLIPPY_UNIMPLEMENTED: RuleDefinition = RuleDefinition {
352 id: "clippy::unimplemented",
353 category: "correctness",
354 producer: Producer::Clippy,
355 default_level: RuleLevel::Warn,
356 tier: RuleTier::P1,
357 help: "Implement this code path or remove the reachable placeholder.",
358};
359pub(crate) static CLIPPY_UNNECESSARY_TO_OWNED: RuleDefinition = RuleDefinition {
360 id: "clippy::unnecessary_to_owned",
361 category: "performance",
362 producer: Producer::Clippy,
363 default_level: RuleLevel::Warn,
364 tier: RuleTier::P3,
365 help: "Pass the borrowed value directly; the callee never needs the owned copy.",
366};
367pub(crate) static CLIPPY_UNREACHABLE: RuleDefinition = RuleDefinition {
368 id: "clippy::unreachable",
369 category: "correctness",
370 producer: Producer::Clippy,
371 default_level: RuleLevel::Warn,
372 tier: RuleTier::P2,
373 help: "Make the remaining case explicit or return an error; an unreachable! that is reached aborts the process.",
374};
375pub(crate) static CLIPPY_UNUSED_ASYNC: RuleDefinition = RuleDefinition {
376 id: "clippy::unused_async",
377 category: "maintainability",
378 producer: Producer::Clippy,
379 default_level: RuleLevel::Warn,
380 tier: RuleTier::P3,
381 help: "Remove the async marker, or await the work the function was meant to drive.",
382};
383pub(crate) static CLIPPY_UNWRAP_USED: RuleDefinition = RuleDefinition {
384 id: "clippy::unwrap_used",
385 category: "reliability",
386 producer: Producer::Clippy,
387 default_level: RuleLevel::Warn,
388 tier: RuleTier::P3,
389 help: "Propagate the error with ? or provide a default instead of panicking on the absent value.",
390};
391pub(crate) static CLIPPY_USELESS_VEC: RuleDefinition = RuleDefinition {
392 id: "clippy::useless_vec",
393 category: "performance",
394 producer: Producer::Clippy,
395 default_level: RuleLevel::Warn,
396 tier: RuleTier::P3,
397 help: "Use an array or a slice literal; this value never needs a heap allocation.",
398};
399pub(crate) static CLIPPY_VEC_INIT_THEN_PUSH: RuleDefinition = RuleDefinition {
400 id: "clippy::vec_init_then_push",
401 category: "performance",
402 producer: Producer::Clippy,
403 default_level: RuleLevel::Warn,
404 tier: RuleTier::P3,
405 help: "Build the vector with the vec! literal so it is allocated once at its final size.",
406};
407pub(crate) static CLIPPY_ZOMBIE_PROCESSES: RuleDefinition = RuleDefinition {
408 id: "clippy::zombie_processes",
409 category: "reliability",
410 producer: Producer::Clippy,
411 default_level: RuleLevel::Warn,
412 tier: RuleTier::P2,
413 help: "Wait on the child process or otherwise reap it before the handle is dropped.",
414};
415pub(crate) static CARGO_DUPLICATE_MAJOR_VERSIONS: RuleDefinition = RuleDefinition {
416 id: "rust_doctor::cargo::duplicate_major_versions",
417 category: "dependencies",
418 producer: Producer::CargoHealth,
419 default_level: RuleLevel::Warn,
420 tier: RuleTier::P2,
421 help: "Align the requirements so one major version of the crate is resolved; duplicates ship twice and their types do not interoperate.",
422};
423pub(crate) static CARGO_MISSING_LOCKFILE: RuleDefinition = RuleDefinition {
424 id: "rust_doctor::cargo::missing_lockfile",
425 category: "dependencies",
426 producer: Producer::CargoHealth,
427 default_level: RuleLevel::Warn,
428 tier: RuleTier::P2,
429 help: "Commit Cargo.lock next to the manifest so every build of this binary resolves the same dependency versions.",
430};
431pub(crate) static CARGO_PERMISSIVE_LINT_TABLE: RuleDefinition = RuleDefinition {
432 id: "rust_doctor::cargo::permissive_lint_table",
433 category: "reliability",
434 producer: Producer::CargoHealth,
435 default_level: RuleLevel::Warn,
436 tier: RuleTier::P2,
437 help: "Remove the allow entry from [lints] and fix what it silences; a manifest-level allow hides the rule from every scan of this workspace.",
438};
439pub(crate) static CARGO_PERMISSIVE_RUSTFLAGS: RuleDefinition = RuleDefinition {
440 id: "rust_doctor::cargo::permissive_rustflags",
441 category: "reliability",
445 producer: Producer::CargoHealth,
446 default_level: RuleLevel::Warn,
447 tier: RuleTier::P2,
448 help: "Remove the flag from .cargo/config.toml and fix what it silences; the closed list judged here is --cap-lints allow, -A warnings and -C overflow-checks=off, each of which disables a check for every build of this workspace.",
449};
450pub(crate) static CARGO_RELEASE_DEBUG_SYMBOLS: RuleDefinition = RuleDefinition {
451 id: "rust_doctor::cargo::release_debug_symbols",
452 category: "reliability",
453 producer: Producer::CargoHealth,
454 default_level: RuleLevel::Warn,
455 tier: RuleTier::P3,
456 help: "Set strip = \"symbols\" or remove debug from [profile.release]: full debug info ships absolute build paths inside the binary. Only [profile.release] itself is judged; profiles inheriting from it are not resolved.",
457};
458pub(crate) static CARGO_TEST_ONLY_DEPENDENCY: RuleDefinition = RuleDefinition {
459 id: "rust_doctor::cargo::test_only_dependency",
460 category: "dependencies",
461 producer: Producer::CargoHealth,
462 default_level: RuleLevel::Warn,
463 tier: RuleTier::P2,
464 help: "Move the entry to [dev-dependencies]: only the test suite references it, and every consumer of the library compiles it anyway.",
465};
466pub(crate) static CARGO_UNUSED_DEPENDENCY: RuleDefinition = RuleDefinition {
467 id: "rust_doctor::cargo::unused_dependency",
468 category: "dependencies",
469 producer: Producer::CargoHealth,
470 default_level: RuleLevel::Warn,
471 tier: RuleTier::P2,
472 help: "Remove the entry no source references, or switch this rule off with --rule or rust-doctor.toml for a crate needed for linking alone; references made only through macro expansion or doctests are not seen.",
473};
474pub(crate) static CARGO_UNCHECKED_RELEASE_OVERFLOW: RuleDefinition = RuleDefinition {
475 id: "rust_doctor::cargo::unchecked_release_overflow",
476 category: "reliability",
480 producer: Producer::CargoHealth,
481 default_level: RuleLevel::Warn,
482 tier: RuleTier::P3,
483 help: "Set overflow-checks = true under [profile.release] so integer overflow panics instead of wrapping silently; the measured cost is a few percent on integer-heavy code, which is the tradeoff this finding asks you to decide.",
484};
485pub(crate) static CARGO_PATH_DEPENDENCY_OUTSIDE_WORKSPACE: RuleDefinition = RuleDefinition {
486 id: "rust_doctor::cargo::path_dependency_outside_workspace",
487 category: "dependencies",
488 producer: Producer::CargoHealth,
489 default_level: RuleLevel::Warn,
490 tier: RuleTier::P1,
491 help: "Vendor the crate inside the workspace or publish it; a path leaving the workspace only resolves on the author's machine.",
492};
493pub(crate) static CARGO_UNBOUNDED_REGISTRY: RuleDefinition = RuleDefinition {
494 id: "rust_doctor::cargo::unbounded_registry_dependency",
495 category: "reliability",
496 producer: Producer::CargoHealth,
497 default_level: RuleLevel::Warn,
498 tier: RuleTier::P3,
499 help: "Replace the unbounded version requirement with the minimum compatible version intended by the project.",
500};
501pub(crate) static CARGO_UNPINNED_GIT: RuleDefinition = RuleDefinition {
502 id: "rust_doctor::cargo::unpinned_git_dependency",
503 category: "security",
504 producer: Producer::CargoHealth,
505 default_level: RuleLevel::Warn,
506 tier: RuleTier::P1,
507 help: "Set rev to the full 40-character commit SHA intended by the project.",
508};
509pub(crate) static REPO_HARDCODED_CREDENTIAL: RuleDefinition = RuleDefinition {
510 id: "rust_doctor::repo::hardcoded_credential",
511 category: "security",
512 producer: Producer::Repo,
513 default_level: RuleLevel::Warn,
514 tier: RuleTier::P1,
515 help: "Remove the credential from the source and rotate it; the closed list judged here is AKIA, ghp_, github_pat_, sk-, xoxb- and BEGIN PRIVATE KEY blocks, and the matched value is never republished by the report.",
516};
517pub(crate) static REPO_TRACKED_SECRET_FILE: RuleDefinition = RuleDefinition {
518 id: "rust_doctor::repo::tracked_secret_file",
519 category: "security",
520 producer: Producer::Repo,
521 default_level: RuleLevel::Warn,
522 tier: RuleTier::P1,
523 help: "Remove the file from version control with git rm --cached, rotate what it contains, and add its name to .gitignore so it stays out; the report names the path and never its contents.",
524};
525pub(crate) static REPO_UNIGNORED_BUILD_OUTPUT: RuleDefinition = RuleDefinition {
526 id: "rust_doctor::repo::unignored_build_output",
527 category: "maintainability",
528 producer: Producer::Repo,
529 default_level: RuleLevel::Warn,
530 tier: RuleTier::P3,
531 help: "Add the target directory to .gitignore so build artifacts stay out of the repository; the directory judged is Cargo's, including a custom target-dir set in .cargo/config.toml, and any ignore source git honors counts.",
532};
533pub(crate) static SOURCE_DISABLED_TLS: RuleDefinition = RuleDefinition {
534 id: "rust_doctor::source::disabled_tls_verification",
535 category: "security",
536 producer: Producer::SourceKernel,
537 default_level: RuleLevel::Warn,
538 tier: RuleTier::P0,
539 help: "Keep TLS verification enabled and configure the required trust roots or server name instead.",
540};
541pub(crate) static SOURCE_DYNAMIC_SHELL: RuleDefinition = RuleDefinition {
542 id: "rust_doctor::source::dynamic_shell_command",
543 category: "security",
544 producer: Producer::SourceKernel,
545 default_level: RuleLevel::Warn,
546 tier: RuleTier::P0,
547 help: "Avoid the shell and pass values as separate Command arguments; otherwise apply shell-specific escaping at the trust boundary.",
548};
549pub(crate) static STRUCTURE_COMPLEX_FUNCTION: RuleDefinition = RuleDefinition {
550 id: "rust_doctor::structure::complex_function",
551 category: "maintainability",
552 producer: Producer::Structure,
553 default_level: RuleLevel::Warn,
554 tier: RuleTier::P3,
555 help: "Split the branching into smaller functions, or flatten it with early returns, so one reading holds the whole path.",
556};
557pub(crate) static STRUCTURE_CRATE_LEVEL_ALLOW: RuleDefinition = RuleDefinition {
558 id: "rust_doctor::structure::crate_level_allow",
559 category: "reliability",
563 producer: Producer::Structure,
564 default_level: RuleLevel::Warn,
565 tier: RuleTier::P2,
566 help: "Scope the allow to the item that needs it; a file-wide or module-wide exemption, reasoned or not, also silences every future finding in its reach.",
567};
568pub(crate) static STRUCTURE_DUPLICATE_FUNCTION_BODY: RuleDefinition = RuleDefinition {
569 id: "rust_doctor::structure::duplicate_function_body",
570 category: "maintainability",
571 producer: Producer::Structure,
572 default_level: RuleLevel::Warn,
573 tier: RuleTier::P3,
574 help: "Keep one definition and call it from the other sites, or make what differs between them a parameter.",
575};
576pub(crate) static STRUCTURE_NEAR_DUPLICATE_FUNCTION_BODY: RuleDefinition = RuleDefinition {
577 id: "rust_doctor::structure::near_duplicate_function_body",
578 category: "maintainability",
579 producer: Producer::Structure,
580 default_level: RuleLevel::Warn,
581 tier: RuleTier::P3,
582 help: "Factor the shared shape into one function and pass what differs, or keep both and record why they must stay apart.",
583};
584pub(crate) static STRUCTURE_ORPHAN_MODULE_FILE: RuleDefinition = RuleDefinition {
585 id: "rust_doctor::structure::orphan_module_file",
586 category: "maintainability",
587 producer: Producer::Structure,
588 default_level: RuleLevel::Warn,
589 tier: RuleTier::P3,
590 help: "Declare the file with a mod declaration, or delete it: Cargo compiles no file the module tree does not reach.",
591};
592pub(crate) static STRUCTURE_OVERSIZED_UNIT: RuleDefinition = RuleDefinition {
593 id: "rust_doctor::structure::oversized_unit",
594 category: "maintainability",
595 producer: Producer::Structure,
596 default_level: RuleLevel::Warn,
597 tier: RuleTier::P3,
598 help: "Split the file, function, impl block or module along its responsibilities before growth makes the split harder.",
599};
600pub(crate) static STRUCTURE_STACKED_ALLOW: RuleDefinition = RuleDefinition {
601 id: "rust_doctor::structure::stacked_allow_attribute",
602 category: "maintainability",
603 producer: Producer::Structure,
604 default_level: RuleLevel::Warn,
605 tier: RuleTier::P3,
606 help: "Keep the one exemption the item actually needs and fix what the others hide; attributes produced by cfg_attr are not counted here.",
607};
608pub(crate) static STRUCTURE_UNREASONED_ALLOW: RuleDefinition = RuleDefinition {
609 id: "rust_doctor::structure::unreasoned_allow_attribute",
610 category: "maintainability",
611 producer: Producer::Structure,
612 default_level: RuleLevel::Warn,
613 tier: RuleTier::P3,
614 help: "Fix what the lint reports, or keep the allow and state why with reason = \"...\" so the exemption survives review.",
615};
616pub(crate) static STRUCTURE_UNREFERENCED_FEATURE: RuleDefinition = RuleDefinition {
617 id: "rust_doctor::structure::unreferenced_feature",
618 category: "maintainability",
619 producer: Producer::Structure,
620 default_level: RuleLevel::Warn,
621 tier: RuleTier::P3,
622 help: "Delete the feature nothing reads, or declare the one a cfg already gates; switch this rule off for a stub deliberately kept as published surface.",
623};
624
625pub(crate) const CATALOG: [&RuleDefinition; 62] = [
626 &CLIPPY_ARC_WITH_NON_SEND_SYNC,
627 &CLIPPY_AWAIT_HOLDING_LOCK,
628 &CLIPPY_AWAIT_HOLDING_REFCELL_REF,
629 &CLIPPY_DBG_MACRO,
630 &CLIPPY_EXIT,
631 &CLIPPY_EXPECT_USED,
632 &CLIPPY_FORMAT_COLLECT,
633 &CLIPPY_INDEXING_SLICING,
634 &CLIPPY_LARGE_TYPES_PASSED_BY_VALUE,
635 &CLIPPY_MANUAL_MEMCPY,
636 &CLIPPY_MEM_FORGET,
637 &CLIPPY_MISSING_SAFETY_DOC,
638 &CLIPPY_MUT_MUTEX_LOCK,
639 &CLIPPY_NON_SEND_FIELDS_IN_SEND_TY,
640 &CLIPPY_PANIC,
641 &CLIPPY_PANIC_IN_RESULT_FN,
642 &CLIPPY_PERMISSIONS_SET_READONLY_FALSE,
643 &CLIPPY_PRINT_STDERR,
644 &CLIPPY_PRINT_STDOUT,
645 &CLIPPY_PTR_ARG,
646 &CLIPPY_RC_BUFFER,
647 &CLIPPY_RC_MUTEX,
648 &CLIPPY_REDUNDANT_ALLOCATION,
649 &CLIPPY_STABLE_SORT_PRIMITIVE,
650 &CLIPPY_STRING_SLICE,
651 &CLIPPY_SUSPICIOUS_COMMAND_ARG_SPACE,
652 &CLIPPY_TODO,
653 &CLIPPY_TOO_MANY_ARGUMENTS,
654 &CLIPPY_TYPE_COMPLEXITY,
655 &CLIPPY_UNIMPLEMENTED,
656 &CLIPPY_UNNECESSARY_TO_OWNED,
657 &CLIPPY_UNREACHABLE,
658 &CLIPPY_UNUSED_ASYNC,
659 &CLIPPY_UNWRAP_USED,
660 &CLIPPY_USELESS_VEC,
661 &CLIPPY_VEC_INIT_THEN_PUSH,
662 &CLIPPY_ZOMBIE_PROCESSES,
663 &CARGO_DUPLICATE_MAJOR_VERSIONS,
664 &CARGO_MISSING_LOCKFILE,
665 &CARGO_PATH_DEPENDENCY_OUTSIDE_WORKSPACE,
666 &CARGO_PERMISSIVE_LINT_TABLE,
667 &CARGO_PERMISSIVE_RUSTFLAGS,
668 &CARGO_RELEASE_DEBUG_SYMBOLS,
669 &CARGO_TEST_ONLY_DEPENDENCY,
670 &CARGO_UNBOUNDED_REGISTRY,
671 &CARGO_UNCHECKED_RELEASE_OVERFLOW,
672 &CARGO_UNPINNED_GIT,
673 &CARGO_UNUSED_DEPENDENCY,
674 &REPO_HARDCODED_CREDENTIAL,
675 &REPO_TRACKED_SECRET_FILE,
676 &REPO_UNIGNORED_BUILD_OUTPUT,
677 &SOURCE_DISABLED_TLS,
678 &SOURCE_DYNAMIC_SHELL,
679 &STRUCTURE_COMPLEX_FUNCTION,
680 &STRUCTURE_CRATE_LEVEL_ALLOW,
681 &STRUCTURE_DUPLICATE_FUNCTION_BODY,
682 &STRUCTURE_NEAR_DUPLICATE_FUNCTION_BODY,
683 &STRUCTURE_ORPHAN_MODULE_FILE,
684 &STRUCTURE_OVERSIZED_UNIT,
685 &STRUCTURE_STACKED_ALLOW,
686 &STRUCTURE_UNREASONED_ALLOW,
687 &STRUCTURE_UNREFERENCED_FEATURE,
688];
689
690pub(crate) fn find(id: &str) -> Option<&'static RuleDefinition> {
691 find_in(&CATALOG, id)
692}
693
694pub(super) fn find_in<'a>(catalog: &'a [&RuleDefinition], id: &str) -> Option<&'a RuleDefinition> {
695 super::by_id(catalog, id, |definition| definition.id).copied()
696}