1use std::sync::LazyLock;
4
5use crate::suppress::IssueKind;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct IssueKindMeta {
14 pub kind: Option<IssueKind>,
16 pub code: &'static str,
18 pub aliases: &'static [&'static str],
20 pub label: &'static str,
22 pub config_key: Option<&'static str>,
24 pub filter_flag: Option<&'static str>,
26 pub mcp_issue_type: Option<&'static str>,
28 pub suppress_token: Option<&'static str>,
30 pub suppress_file_level: bool,
32 pub lsp: bool,
35 pub docs_category: &'static str,
37}
38
39impl IssueKindMeta {
40 #[must_use]
42 pub const fn mcp_pair(self) -> Option<(&'static str, &'static str)> {
43 match (self.mcp_issue_type, self.filter_flag) {
44 (Some(issue_type), Some(flag)) => Some((issue_type, flag)),
45 _ => None,
46 }
47 }
48
49 #[must_use]
51 pub fn has_result_contract(self) -> bool {
52 issue_result_meta_by_code(self.code).is_some()
53 }
54
55 #[must_use]
57 pub fn sarif_rule_ids(self) -> Vec<String> {
58 issue_sarif_rule_ids(self.code)
59 }
60
61 #[must_use]
63 pub fn codeclimate_check_names(self) -> Vec<String> {
64 issue_codeclimate_check_names(self.code)
65 }
66
67 #[must_use]
69 pub fn docs_anchor(self) -> Option<&'static str> {
70 issue_docs_anchor(self.code)
71 }
72
73 #[must_use]
75 pub fn ts_alias(self) -> Option<TsAliasMeta> {
76 issue_ts_alias(self.code)
77 }
78}
79
80pub const ISSUE_KIND_META: &[IssueKindMeta] = &[
82 IssueKindMeta {
83 kind: Some(IssueKind::CodeDuplication),
84 code: "code-duplication",
85 aliases: &[],
86 label: "Code Duplication",
87 config_key: None,
88 filter_flag: None,
89 mcp_issue_type: None,
90 suppress_token: Some("code-duplication"),
91 suppress_file_level: false,
92 lsp: true,
93 docs_category: "dupes",
94 },
95 IssueKindMeta {
96 kind: Some(IssueKind::UnusedFile),
97 code: "unused-file",
98 aliases: &[],
99 label: "Unused Files",
100 config_key: Some("unused-files"),
101 filter_flag: Some("--unused-files"),
102 mcp_issue_type: Some("unused-files"),
103 suppress_token: Some("unused-file"),
104 suppress_file_level: true,
105 lsp: true,
106 docs_category: "source",
107 },
108 IssueKindMeta {
109 kind: Some(IssueKind::UnusedExport),
110 code: "unused-export",
111 aliases: &[],
112 label: "Unused Exports",
113 config_key: Some("unused-exports"),
114 filter_flag: Some("--unused-exports"),
115 mcp_issue_type: Some("unused-exports"),
116 suppress_token: Some("unused-export"),
117 suppress_file_level: false,
118 lsp: true,
119 docs_category: "source",
120 },
121 IssueKindMeta {
122 kind: Some(IssueKind::UnusedType),
123 code: "unused-type",
124 aliases: &[],
125 label: "Unused Types",
126 config_key: Some("unused-types"),
127 filter_flag: Some("--unused-types"),
128 mcp_issue_type: Some("unused-types"),
129 suppress_token: Some("unused-type"),
130 suppress_file_level: false,
131 lsp: true,
132 docs_category: "source",
133 },
134 IssueKindMeta {
135 kind: Some(IssueKind::PrivateTypeLeak),
136 code: "private-type-leak",
137 aliases: &[],
138 label: "Private Type Leaks",
139 config_key: Some("private-type-leaks"),
140 filter_flag: Some("--private-type-leaks"),
141 mcp_issue_type: Some("private-type-leaks"),
142 suppress_token: Some("private-type-leak"),
143 suppress_file_level: false,
144 lsp: true,
145 docs_category: "source",
146 },
147 IssueKindMeta {
148 kind: Some(IssueKind::DeprecatedExportInUse),
149 code: "deprecated-export-in-use",
150 aliases: &[],
151 label: "Deprecated Exports in Use",
152 config_key: Some("deprecated-exports-in-use"),
153 filter_flag: Some("--deprecated-exports-in-use"),
154 mcp_issue_type: Some("deprecated-exports-in-use"),
155 suppress_token: Some("deprecated-export-in-use"),
156 suppress_file_level: false,
157 lsp: false,
158 docs_category: "source",
159 },
160 IssueKindMeta {
161 kind: Some(IssueKind::UnusedDependency),
162 code: "unused-dependency",
163 aliases: &[],
164 label: "Unused Dependencies",
165 config_key: Some("unused-dependencies"),
166 filter_flag: Some("--unused-deps"),
167 mcp_issue_type: Some("unused-deps"),
168 suppress_token: None,
169 suppress_file_level: false,
170 lsp: true,
171 docs_category: "dependency",
172 },
173 IssueKindMeta {
174 kind: Some(IssueKind::UnusedDevDependency),
175 code: "unused-dev-dependency",
176 aliases: &["unused-dev-deps", "unused-dev-dependencies"],
177 label: "Unused Dev Dependencies",
178 config_key: Some("unused-dev-dependencies"),
179 filter_flag: Some("--unused-deps"),
180 mcp_issue_type: None,
181 suppress_token: None,
182 suppress_file_level: false,
183 lsp: true,
184 docs_category: "dependency",
185 },
186 IssueKindMeta {
187 kind: None,
188 code: "unused-optional-dependency",
189 aliases: &["unused-optional-deps", "unused-optional-dependencies"],
190 label: "Unused Optional Dependencies",
191 config_key: Some("unused-optional-dependencies"),
192 filter_flag: Some("--unused-deps"),
193 mcp_issue_type: None,
194 suppress_token: None,
195 suppress_file_level: false,
196 lsp: true,
197 docs_category: "dependency",
198 },
199 IssueKindMeta {
200 kind: Some(IssueKind::UnusedEnumMember),
201 code: "unused-enum-member",
202 aliases: &[],
203 label: "Unused Enum Members",
204 config_key: Some("unused-enum-members"),
205 filter_flag: Some("--unused-enum-members"),
206 mcp_issue_type: Some("unused-enum-members"),
207 suppress_token: Some("unused-enum-member"),
208 suppress_file_level: false,
209 lsp: true,
210 docs_category: "source",
211 },
212 IssueKindMeta {
213 kind: Some(IssueKind::UnusedClassMember),
214 code: "unused-class-member",
215 aliases: &[],
216 label: "Unused Class Members",
217 config_key: Some("unused-class-members"),
218 filter_flag: Some("--unused-class-members"),
219 mcp_issue_type: Some("unused-class-members"),
220 suppress_token: Some("unused-class-member"),
221 suppress_file_level: false,
222 lsp: true,
223 docs_category: "source",
224 },
225 IssueKindMeta {
226 kind: Some(IssueKind::UnusedStoreMember),
227 code: "unused-store-member",
228 aliases: &["unused-store-members"],
229 label: "Unused Store Members",
230 config_key: Some("unused-store-members"),
231 filter_flag: Some("--unused-store-members"),
232 mcp_issue_type: Some("unused-store-members"),
233 suppress_token: Some("unused-store-member"),
234 suppress_file_level: false,
235 lsp: true,
236 docs_category: "framework",
237 },
238 IssueKindMeta {
239 kind: Some(IssueKind::UnresolvedImport),
240 code: "unresolved-import",
241 aliases: &[],
242 label: "Unresolved Imports",
243 config_key: Some("unresolved-imports"),
244 filter_flag: Some("--unresolved-imports"),
245 mcp_issue_type: Some("unresolved-imports"),
246 suppress_token: Some("unresolved-import"),
247 suppress_file_level: false,
248 lsp: true,
249 docs_category: "dependency",
250 },
251 IssueKindMeta {
252 kind: Some(IssueKind::UnlistedDependency),
253 code: "unlisted-dependency",
254 aliases: &[],
255 label: "Unlisted Dependencies",
256 config_key: Some("unlisted-dependencies"),
257 filter_flag: Some("--unlisted-deps"),
258 mcp_issue_type: Some("unlisted-deps"),
259 suppress_token: None,
260 suppress_file_level: false,
261 lsp: true,
262 docs_category: "dependency",
263 },
264 IssueKindMeta {
265 kind: Some(IssueKind::DuplicateExport),
266 code: "duplicate-export",
267 aliases: &[],
268 label: "Duplicate Exports",
269 config_key: Some("duplicate-exports"),
270 filter_flag: Some("--duplicate-exports"),
271 mcp_issue_type: Some("duplicate-exports"),
272 suppress_token: Some("duplicate-export"),
273 suppress_file_level: true,
274 lsp: true,
275 docs_category: "source",
276 },
277 IssueKindMeta {
278 kind: Some(IssueKind::TypeOnlyDependency),
279 code: "type-only-dependency",
280 aliases: &[],
281 label: "Type-Only Dependencies",
282 config_key: Some("type-only-dependencies"),
283 filter_flag: Some("--unused-deps"),
284 mcp_issue_type: None,
285 suppress_token: None,
286 suppress_file_level: false,
287 lsp: true,
288 docs_category: "dependency",
289 },
290 IssueKindMeta {
291 kind: Some(IssueKind::TestOnlyDependency),
292 code: "test-only-dependency",
293 aliases: &[],
294 label: "Test-Only Dependencies",
295 config_key: Some("test-only-dependencies"),
296 filter_flag: Some("--unused-deps"),
297 mcp_issue_type: None,
298 suppress_token: None,
299 suppress_file_level: false,
300 lsp: true,
301 docs_category: "dependency",
302 },
303 IssueKindMeta {
304 kind: Some(IssueKind::DevDependencyInProduction),
305 code: "dev-dependency-in-production",
306 aliases: &[],
307 label: "Dev Dependencies Used in Production",
308 config_key: Some("dev-dependencies-in-production"),
309 filter_flag: Some("--unused-deps"),
310 mcp_issue_type: None,
311 suppress_token: None,
312 suppress_file_level: false,
313 lsp: true,
314 docs_category: "dependency",
315 },
316 IssueKindMeta {
317 kind: Some(IssueKind::CircularDependency),
318 code: "circular-dependency",
319 aliases: &["circular-dependencies"],
320 label: "Circular Dependencies",
321 config_key: Some("circular-dependencies"),
322 filter_flag: Some("--circular-deps"),
323 mcp_issue_type: Some("circular-deps"),
324 suppress_token: Some("circular-dependency"),
325 suppress_file_level: false,
326 lsp: true,
327 docs_category: "architecture",
328 },
329 IssueKindMeta {
330 kind: Some(IssueKind::ReExportCycle),
331 code: "re-export-cycle",
332 aliases: &["re-export-cycles", "reexport-cycle", "reexport-cycles"],
333 label: "Re-Export Cycles",
334 config_key: Some("re-export-cycle"),
335 filter_flag: Some("--re-export-cycles"),
336 mcp_issue_type: Some("re-export-cycles"),
337 suppress_token: Some("re-export-cycle"),
338 suppress_file_level: true,
339 lsp: true,
340 docs_category: "architecture",
341 },
342 IssueKindMeta {
343 kind: Some(IssueKind::PackageCycle),
344 code: "package-cycle",
345 aliases: &["package-cycles"],
346 label: "Package Cycles",
347 config_key: Some("package-cycle"),
348 filter_flag: Some("--package-cycles"),
349 mcp_issue_type: Some("package-cycles"),
350 suppress_token: Some("package-cycle"),
351 suppress_file_level: false,
352 lsp: true,
353 docs_category: "architecture",
354 },
355 IssueKindMeta {
356 kind: Some(IssueKind::BoundaryViolation),
357 code: "boundary-violation",
358 aliases: &[],
359 label: "Boundary Violations",
360 config_key: Some("boundary-violation"),
361 filter_flag: Some("--boundary-violations"),
362 mcp_issue_type: Some("boundary-violations"),
363 suppress_token: Some("boundary-violation"),
364 suppress_file_level: false,
365 lsp: true,
366 docs_category: "architecture",
367 },
368 IssueKindMeta {
369 kind: None,
370 code: "boundary-coverage",
371 aliases: &["boundary-coverage-violations"],
372 label: "Boundary Coverage",
373 config_key: Some("boundary-violation"),
374 filter_flag: Some("--boundary-violations"),
375 mcp_issue_type: None,
376 suppress_token: Some("boundary-violation"),
377 suppress_file_level: true,
378 lsp: false,
379 docs_category: "architecture",
380 },
381 IssueKindMeta {
382 kind: Some(IssueKind::BoundaryViolation),
383 code: "boundary-call-violation",
384 aliases: &["boundary-calls", "boundary-call-violations"],
385 label: "Boundary Call Violations",
386 config_key: Some("boundary-violation"),
387 filter_flag: Some("--boundary-violations"),
388 mcp_issue_type: None,
389 suppress_token: Some("boundary-call-violation"),
390 suppress_file_level: false,
391 lsp: false,
392 docs_category: "architecture",
393 },
394 IssueKindMeta {
395 kind: Some(IssueKind::PolicyViolation),
396 code: "policy-violation",
397 aliases: &["policy-violations"],
398 label: "Policy Violations",
399 config_key: Some("policy-violation"),
400 filter_flag: Some("--policy-violations"),
401 mcp_issue_type: Some("policy-violations"),
402 suppress_token: Some("policy-violation"),
403 suppress_file_level: false,
404 lsp: true,
405 docs_category: "architecture",
406 },
407 IssueKindMeta {
408 kind: Some(IssueKind::InvalidClientExport),
409 code: "invalid-client-export",
410 aliases: &["invalid-client-exports"],
411 label: "Invalid Client Exports",
412 config_key: Some("invalid-client-export"),
413 filter_flag: None,
414 mcp_issue_type: None,
415 suppress_token: Some("invalid-client-export"),
416 suppress_file_level: false,
417 lsp: true,
418 docs_category: "framework",
419 },
420 IssueKindMeta {
421 kind: Some(IssueKind::MixedClientServerBarrel),
422 code: "mixed-client-server-barrel",
423 aliases: &["mixed-client-server-barrels"],
424 label: "Mixed Client/Server Barrels",
425 config_key: Some("mixed-client-server-barrel"),
426 filter_flag: None,
427 mcp_issue_type: None,
428 suppress_token: Some("mixed-client-server-barrel"),
429 suppress_file_level: false,
430 lsp: true,
431 docs_category: "framework",
432 },
433 IssueKindMeta {
434 kind: Some(IssueKind::MisplacedDirective),
435 code: "misplaced-directive",
436 aliases: &["misplaced-directives"],
437 label: "Misplaced Directives",
438 config_key: Some("misplaced-directive"),
439 filter_flag: None,
440 mcp_issue_type: None,
441 suppress_token: Some("misplaced-directive"),
442 suppress_file_level: false,
443 lsp: true,
444 docs_category: "framework",
445 },
446 IssueKindMeta {
447 kind: Some(IssueKind::UnprovidedInject),
448 code: "unprovided-inject",
449 aliases: &["unprovided-injects"],
450 label: "Unprovided Injects",
451 config_key: Some("unprovided-injects"),
452 filter_flag: Some("--unprovided-injects"),
453 mcp_issue_type: Some("unprovided-injects"),
454 suppress_token: Some("unprovided-inject"),
455 suppress_file_level: false,
456 lsp: true,
457 docs_category: "framework",
458 },
459 IssueKindMeta {
460 kind: Some(IssueKind::UnrenderedComponent),
461 code: "unrendered-component",
462 aliases: &["unrendered-components"],
463 label: "Unrendered Components",
464 config_key: Some("unrendered-components"),
465 filter_flag: Some("--unrendered-components"),
466 mcp_issue_type: Some("unrendered-components"),
467 suppress_token: Some("unrendered-component"),
468 suppress_file_level: false,
469 lsp: true,
470 docs_category: "framework",
471 },
472 IssueKindMeta {
473 kind: Some(IssueKind::UnusedComponentProp),
474 code: "unused-component-prop",
475 aliases: &["unused-component-props"],
476 label: "Unused Component Props",
477 config_key: Some("unused-component-props"),
478 filter_flag: Some("--unused-component-props"),
479 mcp_issue_type: Some("unused-component-props"),
480 suppress_token: Some("unused-component-prop"),
481 suppress_file_level: false,
482 lsp: true,
483 docs_category: "framework",
484 },
485 IssueKindMeta {
486 kind: Some(IssueKind::AbsentComponentProp),
487 code: "absent-component-prop",
488 aliases: &["absent-component-props"],
489 label: "Absent Optional Component Props",
490 config_key: Some("absent-component-props"),
491 filter_flag: Some("--absent-component-props"),
492 mcp_issue_type: Some("absent-component-props"),
493 suppress_token: Some("absent-component-prop"),
494 suppress_file_level: false,
495 lsp: true,
496 docs_category: "framework",
497 },
498 IssueKindMeta {
499 kind: Some(IssueKind::UnusedComponentEmit),
500 code: "unused-component-emit",
501 aliases: &["unused-component-emits"],
502 label: "Unused Component Emits",
503 config_key: Some("unused-component-emits"),
504 filter_flag: Some("--unused-component-emits"),
505 mcp_issue_type: Some("unused-component-emits"),
506 suppress_token: Some("unused-component-emit"),
507 suppress_file_level: false,
508 lsp: true,
509 docs_category: "framework",
510 },
511 IssueKindMeta {
512 kind: Some(IssueKind::UnusedComponentInput),
513 code: "unused-component-input",
514 aliases: &["unused-component-inputs"],
515 label: "Unused Component Inputs",
516 config_key: Some("unused-component-inputs"),
517 filter_flag: Some("--unused-component-inputs"),
518 mcp_issue_type: Some("unused-component-inputs"),
519 suppress_token: Some("unused-component-input"),
520 suppress_file_level: false,
521 lsp: true,
522 docs_category: "framework",
523 },
524 IssueKindMeta {
525 kind: Some(IssueKind::UnusedComponentOutput),
526 code: "unused-component-output",
527 aliases: &["unused-component-outputs"],
528 label: "Unused Component Outputs",
529 config_key: Some("unused-component-outputs"),
530 filter_flag: Some("--unused-component-outputs"),
531 mcp_issue_type: Some("unused-component-outputs"),
532 suppress_token: Some("unused-component-output"),
533 suppress_file_level: false,
534 lsp: true,
535 docs_category: "framework",
536 },
537 IssueKindMeta {
538 kind: Some(IssueKind::UnusedSvelteEvent),
539 code: "unused-svelte-event",
540 aliases: &["unused-svelte-events"],
541 label: "Unused Svelte Events",
542 config_key: Some("unused-svelte-events"),
543 filter_flag: Some("--unused-svelte-events"),
544 mcp_issue_type: Some("unused-svelte-events"),
545 suppress_token: Some("unused-svelte-event"),
546 suppress_file_level: false,
547 lsp: true,
548 docs_category: "framework",
549 },
550 IssueKindMeta {
551 kind: Some(IssueKind::UnusedServerAction),
552 code: "unused-server-action",
553 aliases: &["unused-server-actions"],
554 label: "Unused Server Actions",
555 config_key: Some("unused-server-actions"),
556 filter_flag: Some("--unused-server-actions"),
557 mcp_issue_type: Some("unused-server-actions"),
558 suppress_token: Some("unused-server-action"),
559 suppress_file_level: false,
560 lsp: true,
561 docs_category: "framework",
562 },
563 IssueKindMeta {
564 kind: Some(IssueKind::UnusedLoadDataKey),
565 code: "unused-load-data-key",
566 aliases: &["unused-load-data-keys"],
567 label: "Unused Load Data Keys",
568 config_key: Some("unused-load-data-keys"),
569 filter_flag: Some("--unused-load-data-keys"),
570 mcp_issue_type: Some("unused-load-data-keys"),
571 suppress_token: Some("unused-load-data-key"),
572 suppress_file_level: false,
573 lsp: true,
574 docs_category: "framework",
575 },
576 IssueKindMeta {
577 kind: Some(IssueKind::UnusedEffectLayer),
578 code: "unused-effect-layer",
579 aliases: &["unused-effect-layers"],
580 label: "Unused Effect Layers",
581 config_key: Some("unused-effect-layers"),
582 filter_flag: Some("--unused-effect-layers"),
583 mcp_issue_type: Some("unused-effect-layers"),
584 suppress_token: Some("unused-effect-layer"),
585 suppress_file_level: false,
586 lsp: true,
587 docs_category: "framework",
588 },
589 IssueKindMeta {
590 kind: Some(IssueKind::UnusedTaggedError),
591 code: "unused-tagged-error",
592 aliases: &["unused-tagged-errors"],
593 label: "Unused Tagged Errors",
594 config_key: Some("unused-tagged-errors"),
595 filter_flag: Some("--unused-tagged-errors"),
596 mcp_issue_type: Some("unused-tagged-errors"),
597 suppress_token: Some("unused-tagged-error"),
598 suppress_file_level: false,
599 lsp: true,
600 docs_category: "framework",
601 },
602 IssueKindMeta {
603 kind: Some(IssueKind::UnproducedCatchTag),
604 code: "unproduced-catch-tag",
605 aliases: &["unproduced-catch-tags"],
606 label: "Unproduced Catch Tags",
607 config_key: Some("unproduced-catch-tags"),
608 filter_flag: Some("--unproduced-catch-tags"),
609 mcp_issue_type: Some("unproduced-catch-tags"),
610 suppress_token: Some("unproduced-catch-tag"),
611 suppress_file_level: false,
612 lsp: true,
613 docs_category: "framework",
614 },
615 IssueKindMeta {
616 kind: Some(IssueKind::RouteCollision),
617 code: "route-collision",
618 aliases: &["route-collisions"],
619 label: "Route Collisions",
620 config_key: Some("route-collision"),
621 filter_flag: None,
622 mcp_issue_type: None,
623 suppress_token: Some("route-collision"),
624 suppress_file_level: true,
625 lsp: true,
626 docs_category: "framework",
627 },
628 IssueKindMeta {
629 kind: Some(IssueKind::DynamicSegmentNameConflict),
630 code: "dynamic-segment-name-conflict",
631 aliases: &["dynamic-segment-name-conflicts"],
632 label: "Dynamic Segment Conflicts",
633 config_key: Some("dynamic-segment-name-conflict"),
634 filter_flag: None,
635 mcp_issue_type: None,
636 suppress_token: Some("dynamic-segment-name-conflict"),
637 suppress_file_level: true,
638 lsp: true,
639 docs_category: "framework",
640 },
641 IssueKindMeta {
642 kind: Some(IssueKind::StaleSuppression),
643 code: "stale-suppression",
644 aliases: &[],
645 label: "Stale Suppressions",
646 config_key: Some("stale-suppressions"),
647 filter_flag: Some("--stale-suppressions"),
648 mcp_issue_type: Some("stale-suppressions"),
649 suppress_token: None,
650 suppress_file_level: false,
651 lsp: true,
652 docs_category: "source",
653 },
654 IssueKindMeta {
655 kind: Some(IssueKind::StaleSuppression),
656 code: "missing-suppression-reason",
657 aliases: &["missing-suppression-reasons"],
658 label: "Missing Suppression Reasons",
659 config_key: Some("require-suppression-reason"),
660 filter_flag: Some("--stale-suppressions"),
661 mcp_issue_type: None,
662 suppress_token: None,
663 suppress_file_level: false,
664 lsp: false,
665 docs_category: "source",
666 },
667 IssueKindMeta {
668 kind: Some(IssueKind::PnpmCatalogEntry),
669 code: "unused-catalog-entry",
670 aliases: &["catalog", "unused-catalog-entries"],
671 label: "Unused Catalog Entries",
672 config_key: Some("unused-catalog-entries"),
673 filter_flag: Some("--unused-catalog-entries"),
674 mcp_issue_type: Some("unused-catalog-entries"),
675 suppress_token: None,
676 suppress_file_level: false,
677 lsp: true,
678 docs_category: "dependency",
679 },
680 IssueKindMeta {
681 kind: Some(IssueKind::EmptyCatalogGroup),
682 code: "empty-catalog-group",
683 aliases: &["empty-catalog", "empty-catalog-groups"],
684 label: "Empty Catalog Groups",
685 config_key: Some("empty-catalog-groups"),
686 filter_flag: Some("--empty-catalog-groups"),
687 mcp_issue_type: Some("empty-catalog-groups"),
688 suppress_token: None,
689 suppress_file_level: false,
690 lsp: true,
691 docs_category: "dependency",
692 },
693 IssueKindMeta {
694 kind: Some(IssueKind::UnresolvedCatalogReference),
695 code: "unresolved-catalog-reference",
696 aliases: &["unresolved-catalog", "unresolved-catalog-references"],
697 label: "Unresolved Catalog References",
698 config_key: Some("unresolved-catalog-references"),
699 filter_flag: Some("--unresolved-catalog-references"),
700 mcp_issue_type: Some("unresolved-catalog-references"),
701 suppress_token: None,
702 suppress_file_level: false,
703 lsp: true,
704 docs_category: "dependency",
705 },
706 IssueKindMeta {
707 kind: Some(IssueKind::UnusedDependencyOverride),
708 code: "unused-dependency-override",
709 aliases: &[
710 "unused-dependency-overrides",
711 "unused-override",
712 "unused-overrides",
713 ],
714 label: "Unused Dependency Overrides",
715 config_key: Some("unused-dependency-overrides"),
716 filter_flag: Some("--unused-dependency-overrides"),
717 mcp_issue_type: Some("unused-dependency-overrides"),
718 suppress_token: None,
719 suppress_file_level: false,
720 lsp: true,
721 docs_category: "dependency",
722 },
723 IssueKindMeta {
724 kind: Some(IssueKind::MisconfiguredDependencyOverride),
725 code: "misconfigured-dependency-override",
726 aliases: &[
727 "misconfigured-dependency-overrides",
728 "misconfigured-override",
729 "misconfigured-overrides",
730 ],
731 label: "Misconfigured Dependency Overrides",
732 config_key: Some("misconfigured-dependency-overrides"),
733 filter_flag: Some("--misconfigured-dependency-overrides"),
734 mcp_issue_type: Some("misconfigured-dependency-overrides"),
735 suppress_token: None,
736 suppress_file_level: false,
737 lsp: true,
738 docs_category: "dependency",
739 },
740 IssueKindMeta {
741 kind: Some(IssueKind::SecuritySink),
742 code: "security-sink",
743 aliases: &[],
744 label: "Security Sink Candidates",
745 config_key: Some("security-sink"),
746 filter_flag: None,
747 mcp_issue_type: None,
748 suppress_token: Some("security-sink"),
749 suppress_file_level: false,
750 lsp: true,
751 docs_category: "security",
752 },
753 IssueKindMeta {
754 kind: Some(IssueKind::SecurityClientServerLeak),
755 code: "security-client-server-leak",
756 aliases: &[],
757 label: "Security Client-Server Leaks",
758 config_key: Some("security-client-server-leak"),
759 filter_flag: None,
760 mcp_issue_type: None,
761 suppress_token: Some("security-client-server-leak"),
762 suppress_file_level: true,
763 lsp: true,
764 docs_category: "security",
765 },
766 IssueKindMeta {
767 kind: Some(IssueKind::CoverageGaps),
768 code: "coverage-gaps",
769 aliases: &[],
770 label: "Coverage Gaps",
771 config_key: Some("coverage-gaps"),
772 filter_flag: None,
773 mcp_issue_type: None,
774 suppress_token: Some("coverage-gaps"),
775 suppress_file_level: true,
776 lsp: false,
777 docs_category: "health",
778 },
779 IssueKindMeta {
780 kind: Some(IssueKind::FeatureFlag),
781 code: "feature-flag",
782 aliases: &[],
783 label: "Feature Flags",
784 config_key: Some("feature-flags"),
785 filter_flag: None,
786 mcp_issue_type: None,
787 suppress_token: Some("feature-flag"),
788 suppress_file_level: false,
789 lsp: false,
790 docs_category: "flags",
791 },
792 IssueKindMeta {
793 kind: Some(IssueKind::Complexity),
794 code: "complexity",
795 aliases: &[],
796 label: "Complexity",
797 config_key: None,
798 filter_flag: None,
799 mcp_issue_type: None,
800 suppress_token: Some("complexity"),
801 suppress_file_level: false,
802 lsp: false,
803 docs_category: "health",
804 },
805 IssueKindMeta {
806 kind: Some(IssueKind::PropDrilling),
807 code: "prop-drilling",
808 aliases: &[],
809 label: "Prop Drilling",
810 config_key: Some("prop-drilling"),
811 filter_flag: None,
812 mcp_issue_type: None,
813 suppress_token: Some("prop-drilling"),
814 suppress_file_level: false,
815 lsp: true,
816 docs_category: "source",
817 },
818 IssueKindMeta {
819 kind: Some(IssueKind::ThinWrapper),
820 code: "thin-wrapper",
821 aliases: &["thin-wrappers"],
822 label: "Thin Wrappers",
823 config_key: Some("thin-wrapper"),
824 filter_flag: None,
825 mcp_issue_type: None,
826 suppress_token: Some("thin-wrapper"),
827 suppress_file_level: false,
828 lsp: true,
829 docs_category: "source",
830 },
831 IssueKindMeta {
832 kind: Some(IssueKind::DuplicatePropShape),
833 code: "duplicate-prop-shape",
834 aliases: &["duplicate-prop-shapes"],
835 label: "Duplicate Prop Shapes",
836 config_key: Some("duplicate-prop-shape"),
837 filter_flag: None,
838 mcp_issue_type: None,
839 suppress_token: Some("duplicate-prop-shape"),
840 suppress_file_level: false,
841 lsp: true,
842 docs_category: "source",
843 },
844 IssueKindMeta {
845 kind: Some(IssueKind::CssTokenDrift),
846 code: "css-token-drift",
847 aliases: &[],
848 label: "CSS Token Drift",
849 config_key: Some("css-token-drift"),
850 filter_flag: None,
851 mcp_issue_type: None,
852 suppress_token: Some("css-token-drift"),
853 suppress_file_level: false,
854 lsp: false,
855 docs_category: "health",
856 },
857 IssueKindMeta {
858 kind: Some(IssueKind::CssDuplicateBlock),
859 code: "css-duplicate-block",
860 aliases: &[],
861 label: "CSS Duplicate Block",
862 config_key: Some("css-duplicate-block"),
863 filter_flag: None,
864 mcp_issue_type: None,
865 suppress_token: Some("css-duplicate-block"),
866 suppress_file_level: false,
867 lsp: false,
868 docs_category: "health",
869 },
870 IssueKindMeta {
871 kind: Some(IssueKind::CssSelectorComplexity),
872 code: "css-selector-complexity",
873 aliases: &[],
874 label: "CSS Selector Complexity",
875 config_key: Some("css-selector-complexity"),
876 filter_flag: None,
877 mcp_issue_type: None,
878 suppress_token: Some("css-selector-complexity"),
879 suppress_file_level: false,
880 lsp: false,
881 docs_category: "health",
882 },
883 IssueKindMeta {
884 kind: Some(IssueKind::CssDeadSurface),
885 code: "css-dead-surface",
886 aliases: &[],
887 label: "CSS Dead Surface",
888 config_key: Some("css-dead-surface"),
889 filter_flag: None,
890 mcp_issue_type: None,
891 suppress_token: Some("css-dead-surface"),
892 suppress_file_level: false,
893 lsp: false,
894 docs_category: "health",
895 },
896 IssueKindMeta {
897 kind: Some(IssueKind::CssBrokenReference),
898 code: "css-broken-reference",
899 aliases: &[],
900 label: "CSS Broken Reference",
901 config_key: Some("css-broken-reference"),
902 filter_flag: None,
903 mcp_issue_type: None,
904 suppress_token: Some("css-broken-reference"),
905 suppress_file_level: false,
906 lsp: false,
907 docs_category: "health",
908 },
909 IssueKindMeta {
910 kind: Some(IssueKind::WeakenedTests),
911 code: "weakened-tests",
912 aliases: &[],
913 label: "Weakened Tests",
914 config_key: Some("weakened-tests"),
915 filter_flag: None,
916 mcp_issue_type: None,
917 suppress_token: Some("weakened-tests"),
918 suppress_file_level: false,
919 lsp: false,
920 docs_category: "audit",
921 },
922 IssueKindMeta {
923 kind: Some(IssueKind::AddedSuppression),
924 code: "added-suppression",
925 aliases: &[],
926 label: "Added Suppressions",
927 config_key: Some("added-suppression"),
928 filter_flag: None,
929 mcp_issue_type: None,
930 suppress_token: Some("added-suppression"),
931 suppress_file_level: false,
932 lsp: false,
933 docs_category: "audit",
934 },
935 IssueKindMeta {
936 kind: Some(IssueKind::TestOnlyBranch),
937 code: "test-only-branch",
938 aliases: &[],
939 label: "Test-Only Branches",
940 config_key: Some("test-only-branch"),
941 filter_flag: None,
942 mcp_issue_type: None,
943 suppress_token: Some("test-only-branch"),
944 suppress_file_level: false,
945 lsp: false,
946 docs_category: "audit",
947 },
948 IssueKindMeta {
949 kind: Some(IssueKind::DivergedCloneSibling),
950 code: "diverged-clone-sibling",
951 aliases: &[],
952 label: "Diverged Clone Siblings",
953 config_key: Some("diverged-clone-sibling"),
954 filter_flag: None,
955 mcp_issue_type: None,
956 suppress_token: Some("diverged-clone-sibling"),
957 suppress_file_level: false,
958 lsp: false,
959 docs_category: "audit",
960 },
961 IssueKindMeta {
962 kind: Some(IssueKind::AddedDependency),
963 code: "added-dependency",
964 aliases: &[],
965 label: "Added Dependencies",
966 config_key: Some("added-dependency"),
967 filter_flag: None,
968 mcp_issue_type: None,
969 suppress_token: None,
972 suppress_file_level: false,
973 lsp: false,
974 docs_category: "audit",
975 },
976];
977
978#[derive(Debug, Clone, Copy, PartialEq, Eq)]
980#[non_exhaustive]
981pub struct IssueResultMeta {
982 pub code: &'static str,
984 pub sarif_description: &'static str,
986 pub meta_description: &'static str,
988 pub meta_docs_path: &'static str,
990 pub meta_name: &'static str,
992 pub summary_label: &'static str,
994 pub docs_anchor: &'static str,
996 pub result_key: &'static str,
998 pub counts_in_total: bool,
1001}
1002
1003#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1005pub struct TsAliasMeta {
1006 pub name: &'static str,
1008 pub parent: &'static str,
1010}
1011
1012#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1014pub struct IssueTsAliasMeta {
1015 pub code: &'static str,
1017 pub alias: TsAliasMeta,
1019}
1020
1021pub const ISSUE_TS_ALIAS_META: &[IssueTsAliasMeta] = &[
1023 IssueTsAliasMeta {
1024 code: "unused-file",
1025 alias: TsAliasMeta {
1026 name: "UnusedFile",
1027 parent: "UnusedFileFinding",
1028 },
1029 },
1030 IssueTsAliasMeta {
1031 code: "unused-export",
1032 alias: TsAliasMeta {
1033 name: "UnusedExport",
1034 parent: "UnusedExportFinding",
1035 },
1036 },
1037 IssueTsAliasMeta {
1038 code: "private-type-leak",
1039 alias: TsAliasMeta {
1040 name: "PrivateTypeLeak",
1041 parent: "PrivateTypeLeakFinding",
1042 },
1043 },
1044 IssueTsAliasMeta {
1045 code: "deprecated-export-in-use",
1046 alias: TsAliasMeta {
1047 name: "DeprecatedExportInUse",
1048 parent: "DeprecatedExportInUseFinding",
1049 },
1050 },
1051 IssueTsAliasMeta {
1052 code: "unused-dependency",
1053 alias: TsAliasMeta {
1054 name: "UnusedDependency",
1055 parent: "UnusedDependencyFinding",
1056 },
1057 },
1058 IssueTsAliasMeta {
1059 code: "unused-dev-dependency",
1060 alias: TsAliasMeta {
1061 name: "UnusedDependency",
1062 parent: "UnusedDevDependencyFinding",
1063 },
1064 },
1065 IssueTsAliasMeta {
1066 code: "unused-optional-dependency",
1067 alias: TsAliasMeta {
1068 name: "UnusedDependency",
1069 parent: "UnusedOptionalDependencyFinding",
1070 },
1071 },
1072 IssueTsAliasMeta {
1073 code: "unused-enum-member",
1074 alias: TsAliasMeta {
1075 name: "UnusedMember",
1076 parent: "UnusedEnumMemberFinding",
1077 },
1078 },
1079 IssueTsAliasMeta {
1080 code: "unused-class-member",
1081 alias: TsAliasMeta {
1082 name: "UnusedMember",
1083 parent: "UnusedClassMemberFinding",
1084 },
1085 },
1086 IssueTsAliasMeta {
1087 code: "unused-store-member",
1088 alias: TsAliasMeta {
1089 name: "UnusedMember",
1090 parent: "UnusedStoreMemberFinding",
1091 },
1092 },
1093 IssueTsAliasMeta {
1094 code: "unresolved-import",
1095 alias: TsAliasMeta {
1096 name: "UnresolvedImport",
1097 parent: "UnresolvedImportFinding",
1098 },
1099 },
1100 IssueTsAliasMeta {
1101 code: "unlisted-dependency",
1102 alias: TsAliasMeta {
1103 name: "UnlistedDependency",
1104 parent: "UnlistedDependencyFinding",
1105 },
1106 },
1107 IssueTsAliasMeta {
1108 code: "duplicate-export",
1109 alias: TsAliasMeta {
1110 name: "DuplicateExport",
1111 parent: "DuplicateExportFinding",
1112 },
1113 },
1114 IssueTsAliasMeta {
1115 code: "type-only-dependency",
1116 alias: TsAliasMeta {
1117 name: "TypeOnlyDependency",
1118 parent: "TypeOnlyDependencyFinding",
1119 },
1120 },
1121 IssueTsAliasMeta {
1122 code: "test-only-dependency",
1123 alias: TsAliasMeta {
1124 name: "TestOnlyDependency",
1125 parent: "TestOnlyDependencyFinding",
1126 },
1127 },
1128 IssueTsAliasMeta {
1129 code: "dev-dependency-in-production",
1130 alias: TsAliasMeta {
1131 name: "DevDependencyInProduction",
1132 parent: "DevDependencyInProductionFinding",
1133 },
1134 },
1135 IssueTsAliasMeta {
1136 code: "circular-dependency",
1137 alias: TsAliasMeta {
1138 name: "CircularDependency",
1139 parent: "CircularDependencyFinding",
1140 },
1141 },
1142 IssueTsAliasMeta {
1143 code: "re-export-cycle",
1144 alias: TsAliasMeta {
1145 name: "ReExportCycle",
1146 parent: "ReExportCycleFinding",
1147 },
1148 },
1149 IssueTsAliasMeta {
1150 code: "package-cycle",
1151 alias: TsAliasMeta {
1152 name: "PackageCycle",
1153 parent: "PackageCycleFinding",
1154 },
1155 },
1156 IssueTsAliasMeta {
1157 code: "boundary-violation",
1158 alias: TsAliasMeta {
1159 name: "BoundaryViolation",
1160 parent: "BoundaryViolationFinding",
1161 },
1162 },
1163 IssueTsAliasMeta {
1164 code: "unused-catalog-entry",
1165 alias: TsAliasMeta {
1166 name: "UnusedCatalogEntry",
1167 parent: "UnusedCatalogEntryFinding",
1168 },
1169 },
1170 IssueTsAliasMeta {
1171 code: "empty-catalog-group",
1172 alias: TsAliasMeta {
1173 name: "EmptyCatalogGroup",
1174 parent: "EmptyCatalogGroupFinding",
1175 },
1176 },
1177 IssueTsAliasMeta {
1178 code: "unresolved-catalog-reference",
1179 alias: TsAliasMeta {
1180 name: "UnresolvedCatalogReference",
1181 parent: "UnresolvedCatalogReferenceFinding",
1182 },
1183 },
1184 IssueTsAliasMeta {
1185 code: "unused-dependency-override",
1186 alias: TsAliasMeta {
1187 name: "UnusedDependencyOverride",
1188 parent: "UnusedDependencyOverrideFinding",
1189 },
1190 },
1191 IssueTsAliasMeta {
1192 code: "misconfigured-dependency-override",
1193 alias: TsAliasMeta {
1194 name: "MisconfiguredDependencyOverride",
1195 parent: "MisconfiguredDependencyOverrideFinding",
1196 },
1197 },
1198];
1199
1200pub const ISSUE_RESULT_META: &[IssueResultMeta] = &[
1202 IssueResultMeta {
1203 code: "unused-file",
1204 sarif_description: "File is not reachable from any entry point",
1205 meta_description: "Source files that are not imported by any other module and are not entry points. Detection uses graph reachability from configured entry points.",
1206 meta_docs_path: "explanations/dead-code#unused-files",
1207 meta_name: "Unused Files",
1208 summary_label: "Unused files",
1209 docs_anchor: "unused-files",
1210 result_key: "unused_files",
1211 counts_in_total: true,
1212 },
1213 IssueResultMeta {
1214 code: "unused-export",
1215 sarif_description: "Export is never imported",
1216 meta_description: "Named exports that are never imported by any other module in the project, including direct exports and re-exports through barrel files.",
1217 meta_docs_path: "explanations/dead-code#unused-exports",
1218 meta_name: "Unused Exports",
1219 summary_label: "Unused exports",
1220 docs_anchor: "unused-exports",
1221 result_key: "unused_exports",
1222 counts_in_total: true,
1223 },
1224 IssueResultMeta {
1225 code: "unused-type",
1226 sarif_description: "Type export is never imported",
1227 meta_description: "Type-only exports that are never imported. These do not generate runtime code but add maintenance burden.",
1228 meta_docs_path: "explanations/dead-code#unused-types",
1229 meta_name: "Unused Type Exports",
1230 summary_label: "Unused types",
1231 docs_anchor: "unused-types",
1232 result_key: "unused_types",
1233 counts_in_total: true,
1234 },
1235 IssueResultMeta {
1236 code: "private-type-leak",
1237 sarif_description: "Exported signature references a private type",
1238 meta_description: "Exported values or types whose public TypeScript signature references a same-file type declaration that is not exported.",
1239 meta_docs_path: "explanations/dead-code#private-type-leaks",
1240 meta_name: "Private Type Leaks",
1241 summary_label: "Private type leaks",
1242 docs_anchor: "private-type-leaks",
1243 result_key: "private_type_leaks",
1244 counts_in_total: true,
1245 },
1246 IssueResultMeta {
1247 code: "deprecated-export-in-use",
1248 sarif_description: "Export marked @deprecated is still referenced",
1249 meta_description: "Exports whose leading JSDoc carries @deprecated and that still have at least one consumer in a reachable file. Each finding lists the consumer count and a capped consumer sample.",
1250 meta_docs_path: "explanations/dead-code#deprecated-exports-in-use",
1251 meta_name: "Deprecated Exports in Use",
1252 summary_label: "Deprecated exports in use",
1253 docs_anchor: "deprecated-exports-in-use",
1254 result_key: "deprecated_exports_in_use",
1255 counts_in_total: true,
1256 },
1257 IssueResultMeta {
1258 code: "unused-dependency",
1259 sarif_description: "Dependency listed but never imported",
1260 meta_description: "Packages listed in dependencies that are never imported or required by any source file.",
1261 meta_docs_path: "explanations/dead-code#unused-dependencies",
1262 meta_name: "Unused Dependencies",
1263 summary_label: "Unused dependencies",
1264 docs_anchor: "unused-dependencies",
1265 result_key: "unused_dependencies",
1266 counts_in_total: true,
1267 },
1268 IssueResultMeta {
1269 code: "unused-dev-dependency",
1270 sarif_description: "Dev dependency listed but never imported",
1271 meta_description: "Packages listed in devDependencies that are never imported by test files, config files, or scripts.",
1272 meta_docs_path: "explanations/dead-code#unused-devdependencies",
1273 meta_name: "Unused Dev Dependencies",
1274 summary_label: "Unused devDependencies",
1275 docs_anchor: "unused-dependencies",
1276 result_key: "unused_dev_dependencies",
1277 counts_in_total: true,
1278 },
1279 IssueResultMeta {
1280 code: "unused-optional-dependency",
1281 sarif_description: "Optional dependency listed but never imported",
1282 meta_description: "Packages listed in optionalDependencies that are never imported.",
1283 meta_docs_path: "explanations/dead-code#unused-optionaldependencies",
1284 meta_name: "Unused Optional Dependencies",
1285 summary_label: "Unused optionalDependencies",
1286 docs_anchor: "unused-dependencies",
1287 result_key: "unused_optional_dependencies",
1288 counts_in_total: true,
1289 },
1290 IssueResultMeta {
1291 code: "unused-enum-member",
1292 sarif_description: "Enum member is never referenced",
1293 meta_description: "Enum members that are never referenced in the codebase.",
1294 meta_docs_path: "explanations/dead-code#unused-enum-members",
1295 meta_name: "Unused Enum Members",
1296 summary_label: "Unused enum members",
1297 docs_anchor: "unused-enum-members",
1298 result_key: "unused_enum_members",
1299 counts_in_total: true,
1300 },
1301 IssueResultMeta {
1302 code: "unused-class-member",
1303 sarif_description: "Class member is never referenced",
1304 meta_description: "Class methods and properties that are never referenced outside the class.",
1305 meta_docs_path: "explanations/dead-code#unused-class-members",
1306 meta_name: "Unused Class Members",
1307 summary_label: "Unused class members",
1308 docs_anchor: "unused-class-members",
1309 result_key: "unused_class_members",
1310 counts_in_total: true,
1311 },
1312 IssueResultMeta {
1313 code: "unused-store-member",
1314 sarif_description: "Store member is never accessed by any consumer",
1315 meta_description: "Pinia store members declared but never accessed by any consumer project-wide.",
1316 meta_docs_path: "explanations/dead-code#unused-store-members",
1317 meta_name: "Unused Store Members",
1318 summary_label: "Unused store members",
1319 docs_anchor: "unused-store-members",
1320 result_key: "unused_store_members",
1321 counts_in_total: true,
1322 },
1323 IssueResultMeta {
1324 code: "unresolved-import",
1325 sarif_description: "Import could not be resolved",
1326 meta_description: "Import specifiers that could not be resolved to a file on disk.",
1327 meta_docs_path: "explanations/dead-code#unresolved-imports",
1328 meta_name: "Unresolved Imports",
1329 summary_label: "Unresolved imports",
1330 docs_anchor: "unresolved-imports",
1331 result_key: "unresolved_imports",
1332 counts_in_total: true,
1333 },
1334 IssueResultMeta {
1335 code: "unlisted-dependency",
1336 sarif_description: "Dependency used but not in package.json",
1337 meta_description: "Packages imported in source code but not listed in package.json.",
1338 meta_docs_path: "explanations/dead-code#unlisted-dependencies",
1339 meta_name: "Unlisted Dependencies",
1340 summary_label: "Unlisted dependencies",
1341 docs_anchor: "unlisted-dependencies",
1342 result_key: "unlisted_dependencies",
1343 counts_in_total: true,
1344 },
1345 IssueResultMeta {
1346 code: "duplicate-export",
1347 sarif_description: "Export name appears in multiple modules",
1348 meta_description: "The same export name is defined in multiple modules.",
1349 meta_docs_path: "explanations/dead-code#duplicate-exports",
1350 meta_name: "Duplicate Exports",
1351 summary_label: "Duplicate exports",
1352 docs_anchor: "duplicate-exports",
1353 result_key: "duplicate_exports",
1354 counts_in_total: true,
1355 },
1356 IssueResultMeta {
1357 code: "type-only-dependency",
1358 sarif_description: "Production dependency only used via type-only imports",
1359 meta_description: "Production dependencies that are only imported via type-only imports.",
1360 meta_docs_path: "explanations/dead-code#type-only-dependencies",
1361 meta_name: "Type-only Dependencies",
1362 summary_label: "Type-only dependencies",
1363 docs_anchor: "type-only-dependencies",
1364 result_key: "type_only_dependencies",
1365 counts_in_total: true,
1366 },
1367 IssueResultMeta {
1368 code: "test-only-dependency",
1369 sarif_description: "Production dependency only imported by test files",
1370 meta_description: "Production dependencies that are only imported from test files.",
1371 meta_docs_path: "explanations/dead-code#test-only-dependencies",
1372 meta_name: "Test-only Dependencies",
1373 summary_label: "Test-only dependencies",
1374 docs_anchor: "test-only-dependencies",
1375 result_key: "test_only_dependencies",
1376 counts_in_total: true,
1377 },
1378 IssueResultMeta {
1379 code: "dev-dependency-in-production",
1380 sarif_description: "devDependency imported by production code with a runtime import",
1381 meta_description: "devDependencies imported by production source code via a runtime/value import; they should be promoted to dependencies.",
1382 meta_docs_path: "explanations/dead-code#dev-dependencies-in-production",
1383 meta_name: "Dev Dependencies Used in Production",
1384 summary_label: "Dev dependencies used in production",
1385 docs_anchor: "dev-dependencies-in-production",
1386 result_key: "dev_dependencies_in_production",
1387 counts_in_total: true,
1388 },
1389 IssueResultMeta {
1390 code: "circular-dependency",
1391 sarif_description: "Circular dependency chain detected",
1392 meta_description: "A cycle in the module import graph.",
1393 meta_docs_path: "explanations/dead-code#circular-dependencies",
1394 meta_name: "Circular Dependencies",
1395 summary_label: "Circular dependencies",
1396 docs_anchor: "circular-dependencies",
1397 result_key: "circular_dependencies",
1398 counts_in_total: true,
1399 },
1400 IssueResultMeta {
1401 code: "re-export-cycle",
1402 sarif_description: "Two or more barrel files re-export from each other in a loop",
1403 meta_description: "A barrel file re-exports from another barrel that ultimately re-exports back.",
1404 meta_docs_path: "explanations/dead-code#re-export-cycles",
1405 meta_name: "Re-Export Cycles",
1406 summary_label: "Re-export cycles",
1407 docs_anchor: "re-export-cycles",
1408 result_key: "re_export_cycles",
1409 counts_in_total: true,
1410 },
1411 IssueResultMeta {
1412 code: "package-cycle",
1413 sarif_description: "Two or more workspace packages import each other in a loop",
1414 meta_description: "Workspace packages whose resolved imports form a cycle, even when no file-level cycle exists.",
1415 meta_docs_path: "explanations/dead-code#package-cycles",
1416 meta_name: "Package Cycles",
1417 summary_label: "Package cycles",
1418 docs_anchor: "package-cycles",
1419 result_key: "package_cycles",
1420 counts_in_total: true,
1421 },
1422 IssueResultMeta {
1423 code: "boundary-violation",
1424 sarif_description: "Import crosses a configured architecture boundary",
1425 meta_description: "A module imports from a zone that its configured boundary rules do not allow.",
1426 meta_docs_path: "explanations/dead-code#boundary-violations",
1427 meta_name: "Boundary Violations",
1428 summary_label: "Boundary violations",
1429 docs_anchor: "boundary-violations",
1430 result_key: "boundary_violations",
1431 counts_in_total: true,
1432 },
1433 IssueResultMeta {
1434 code: "boundary-coverage",
1435 sarif_description: "Source file matches no configured architecture boundary zone",
1436 meta_description: "A reachable source file is not assigned to any configured boundary zone while boundary coverage is required.",
1437 meta_docs_path: "explanations/dead-code#boundary-violations",
1438 meta_name: "Boundary Coverage",
1439 summary_label: "Boundary coverage",
1440 docs_anchor: "boundary-violations",
1441 result_key: "boundary_coverage_violations",
1442 counts_in_total: true,
1443 },
1444 IssueResultMeta {
1445 code: "boundary-call-violation",
1446 sarif_description: "Zoned file calls a callee its zone forbids",
1447 meta_description: "A file classified into a boundary zone calls a callee matching one of the zone's forbidden call patterns.",
1448 meta_docs_path: "explanations/dead-code#boundary-violations",
1449 meta_name: "Boundary Call Violation",
1450 summary_label: "Boundary calls",
1451 docs_anchor: "boundary-violations",
1452 result_key: "boundary_call_violations",
1453 counts_in_total: true,
1454 },
1455 IssueResultMeta {
1456 code: "policy-violation",
1457 sarif_description: "Usage violates a configured rule-pack policy",
1458 meta_description: "A call, import, export, catalogue-derived effect, or graph-resolved GDP proof producer violates a configured rule pack rule.",
1459 meta_docs_path: "explanations/dead-code#policy-violations",
1460 meta_name: "Policy Violation",
1461 summary_label: "Policy violations",
1462 docs_anchor: "policy-violations",
1463 result_key: "policy_violations",
1464 counts_in_total: true,
1465 },
1466 IssueResultMeta {
1467 code: "invalid-client-export",
1468 sarif_description: "\"use client\" file exports a server-only / route-config name",
1469 meta_description: "A file carrying the use client directive also exports a Next.js server-only or route-segment config name.",
1470 meta_docs_path: "explanations/dead-code#invalid-client-exports",
1471 meta_name: "Invalid client export",
1472 summary_label: "Invalid client exports",
1473 docs_anchor: "invalid-client-exports",
1474 result_key: "invalid_client_exports",
1475 counts_in_total: true,
1476 },
1477 IssueResultMeta {
1478 code: "mixed-client-server-barrel",
1479 sarif_description: "Barrel re-exports both a \"use client\" module and a server-only module",
1480 meta_description: "A barrel file forwards a name from a use client module alongside a name from a server-only module.",
1481 meta_docs_path: "explanations/dead-code#mixed-client-server-barrels",
1482 meta_name: "Mixed client/server barrel",
1483 summary_label: "Mixed client/server barrels",
1484 docs_anchor: "mixed-client-server-barrels",
1485 result_key: "mixed_client_server_barrels",
1486 counts_in_total: true,
1487 },
1488 IssueResultMeta {
1489 code: "misplaced-directive",
1490 sarif_description: "\"use client\" / \"use server\" directive is not in the leading position and is ignored",
1491 meta_description: "A use client or use server directive string appears after a non-directive statement and is ignored.",
1492 meta_docs_path: "explanations/dead-code#misplaced-directives",
1493 meta_name: "Misplaced directive",
1494 summary_label: "Misplaced directives",
1495 docs_anchor: "misplaced-directives",
1496 result_key: "misplaced_directives",
1497 counts_in_total: true,
1498 },
1499 IssueResultMeta {
1500 code: "unprovided-inject",
1501 sarif_description: "inject() / getContext() reads a key that no provide() / setContext() supplies",
1502 meta_description: "A Vue inject or Svelte getContext reads a dependency-injection key that no matching provider supplies.",
1503 meta_docs_path: "explanations/dead-code#unprovided-injects",
1504 meta_name: "Unprovided injects",
1505 summary_label: "Unprovided injects",
1506 docs_anchor: "unprovided-injects",
1507 result_key: "unprovided_injects",
1508 counts_in_total: true,
1509 },
1510 IssueResultMeta {
1511 code: "unrendered-component",
1512 sarif_description: "A Vue / Svelte component is reachable through a barrel but rendered nowhere",
1513 meta_description: "A Vue or Svelte single-file component is reachable through the graph but rendered nowhere in the project.",
1514 meta_docs_path: "explanations/dead-code#unrendered-components",
1515 meta_name: "Unrendered components",
1516 summary_label: "Unrendered components",
1517 docs_anchor: "unrendered-components",
1518 result_key: "unrendered_components",
1519 counts_in_total: true,
1520 },
1521 IssueResultMeta {
1522 code: "unused-component-prop",
1523 sarif_description: "A Vue, Svelte, or React component prop is referenced nowhere in its own component",
1524 meta_description: "A declared Vue, Svelte, React, or Preact component prop is referenced nowhere inside its own component.",
1525 meta_docs_path: "explanations/dead-code#unused-component-props",
1526 meta_name: "Unused component props",
1527 summary_label: "Unused component props",
1528 docs_anchor: "unused-component-props",
1529 result_key: "unused_component_props",
1530 counts_in_total: true,
1531 },
1532 IssueResultMeta {
1533 code: "absent-component-prop",
1534 sarif_description: "Known reachable callers do not supply this used optional input; review defaults and API intent manually.",
1535 meta_description: "Known reachable callers do not supply this used optional input; review defaults and API intent manually.",
1536 meta_docs_path: "explanations/dead-code#absent-component-props",
1537 meta_name: "Absent optional component props",
1538 summary_label: "Absent optional component props",
1539 docs_anchor: "absent-component-props",
1540 result_key: "absent_component_props",
1541 counts_in_total: true,
1542 },
1543 IssueResultMeta {
1544 code: "unused-component-emit",
1545 sarif_description: "A Vue <script setup> defineEmits event is emitted nowhere in its own component",
1546 meta_description: "A Vue script setup defineEmits event is emitted nowhere in its own component.",
1547 meta_docs_path: "explanations/dead-code#unused-component-emits",
1548 meta_name: "Unused component emits",
1549 summary_label: "Unused component emits",
1550 docs_anchor: "unused-component-emits",
1551 result_key: "unused_component_emits",
1552 counts_in_total: true,
1553 },
1554 IssueResultMeta {
1555 code: "unused-component-input",
1556 sarif_description: "An Angular @Input() / signal input() / model() input is read nowhere in its own component",
1557 meta_description: "An Angular input is read nowhere in its own component.",
1558 meta_docs_path: "explanations/dead-code#unused-component-inputs",
1559 meta_name: "Unused component inputs",
1560 summary_label: "Unused component inputs",
1561 docs_anchor: "unused-component-inputs",
1562 result_key: "unused_component_inputs",
1563 counts_in_total: true,
1564 },
1565 IssueResultMeta {
1566 code: "unused-component-output",
1567 sarif_description: "An Angular @Output() / signal output() output is emitted nowhere in its own component",
1568 meta_description: "An Angular output is emitted nowhere in its own component.",
1569 meta_docs_path: "explanations/dead-code#unused-component-outputs",
1570 meta_name: "Unused component outputs",
1571 summary_label: "Unused component outputs",
1572 docs_anchor: "unused-component-outputs",
1573 result_key: "unused_component_outputs",
1574 counts_in_total: true,
1575 },
1576 IssueResultMeta {
1577 code: "unused-svelte-event",
1578 sarif_description: "A Svelte component dispatches a createEventDispatcher event whose name is listened to nowhere in the project",
1579 meta_description: "A Svelte component dispatches a custom event whose name is listened to nowhere in the analyzed project.",
1580 meta_docs_path: "explanations/dead-code#unused-svelte-events",
1581 meta_name: "Unused Svelte events",
1582 summary_label: "Unused Svelte events",
1583 docs_anchor: "unused-svelte-events",
1584 result_key: "unused_svelte_events",
1585 counts_in_total: true,
1586 },
1587 IssueResultMeta {
1588 code: "unused-server-action",
1589 sarif_description: "A Next.js Server Action exported from a \"use server\" file is referenced by no code in the project",
1590 meta_description: "A Next.js Server Action exported from a use server file is referenced by no code in the project.",
1591 meta_docs_path: "explanations/dead-code#unused-server-actions",
1592 meta_name: "Unused server actions",
1593 summary_label: "Unused server actions",
1594 docs_anchor: "unused-server-actions",
1595 result_key: "unused_server_actions",
1596 counts_in_total: true,
1597 },
1598 IssueResultMeta {
1599 code: "unused-load-data-key",
1600 sarif_description: "A SvelteKit load() return-object key is read by no consumer",
1601 meta_description: "A SvelteKit load return-object key is read by no route or project-wide consumer.",
1602 meta_docs_path: "explanations/dead-code#unused-load-data-keys",
1603 meta_name: "Unused load data keys",
1604 summary_label: "Unused load data keys",
1605 docs_anchor: "unused-load-data-keys",
1606 result_key: "unused_load_data_keys",
1607 counts_in_total: true,
1608 },
1609 IssueResultMeta {
1610 code: "unused-effect-layer",
1611 sarif_description: "An Effect layer is composed only into layers that nothing uses",
1612 meta_description: "An Effect layer is composed only into other layers that nothing in the project uses.",
1613 meta_docs_path: "explanations/dead-code#unused-effect-layers",
1614 meta_name: "Unused Effect layers",
1615 summary_label: "Unused Effect layers",
1616 docs_anchor: "unused-effect-layers",
1617 result_key: "unused_effect_layers",
1618 counts_in_total: true,
1619 },
1620 IssueResultMeta {
1621 code: "unused-tagged-error",
1622 sarif_description: "An Effect tagged error class is referenced only as a type or a catchTag string, and no code constructs or fails with it",
1623 meta_description: "An Effect tagged error class is referenced only as a type or a catchTag string. No code constructs or fails with it.",
1624 meta_docs_path: "explanations/dead-code#unused-tagged-errors",
1625 meta_name: "Unused tagged errors",
1626 summary_label: "Unused tagged errors",
1627 docs_anchor: "unused-tagged-errors",
1628 result_key: "unused_tagged_errors",
1629 counts_in_total: true,
1630 },
1631 IssueResultMeta {
1632 code: "unproduced-catch-tag",
1633 sarif_description: "A catchTag handler names a project error tag that no code produces",
1634 meta_description: "A catchTag or catchTags handler names a project error tag that no code in the project produces.",
1635 meta_docs_path: "explanations/dead-code#unproduced-catch-tags",
1636 meta_name: "Unproduced catch tags",
1637 summary_label: "Unproduced catch tags",
1638 docs_anchor: "unproduced-catch-tags",
1639 result_key: "unproduced_catch_tags",
1640 counts_in_total: true,
1641 },
1642 IssueResultMeta {
1643 code: "route-collision",
1644 sarif_description: "Two or more Next.js App Router route files resolve to the same URL",
1645 meta_description: "Two or more Next.js App Router route files resolve to the same URL within one app root.",
1646 meta_docs_path: "explanations/dead-code#route-collisions",
1647 meta_name: "Route collision",
1648 summary_label: "Route collisions",
1649 docs_anchor: "route-collisions",
1650 result_key: "route_collisions",
1651 counts_in_total: true,
1652 },
1653 IssueResultMeta {
1654 code: "dynamic-segment-name-conflict",
1655 sarif_description: "Sibling Next.js dynamic route segments use different slug names at the same position",
1656 meta_description: "Sibling Next.js dynamic route segments use different slug names at the same position.",
1657 meta_docs_path: "explanations/dead-code#dynamic-segment-name-conflicts",
1658 meta_name: "Dynamic segment name conflict",
1659 summary_label: "Dynamic segment conflicts",
1660 docs_anchor: "dynamic-segment-name-conflicts",
1661 result_key: "dynamic_segment_name_conflicts",
1662 counts_in_total: true,
1663 },
1664 IssueResultMeta {
1665 code: "stale-suppression",
1666 sarif_description: "Suppression comment or tag no longer matches any issue",
1667 meta_description: "A fallow suppression comment or tag no longer matches any active issue.",
1668 meta_docs_path: "explanations/dead-code#stale-suppressions",
1669 meta_name: "Stale Suppressions",
1670 summary_label: "Stale suppressions",
1671 docs_anchor: "stale-suppressions",
1672 result_key: "stale_suppressions",
1673 counts_in_total: true,
1674 },
1675 IssueResultMeta {
1676 code: "unused-catalog-entry",
1677 sarif_description: "Catalog entry not referenced by any workspace package",
1678 meta_description: "A package manager catalog entry is not referenced by any workspace package.json.",
1679 meta_docs_path: "explanations/dead-code#unused-catalog-entries",
1680 meta_name: "Unused catalog entry",
1681 summary_label: "Unused catalog entries",
1682 docs_anchor: "unused-catalog-entries",
1683 result_key: "unused_catalog_entries",
1684 counts_in_total: true,
1685 },
1686 IssueResultMeta {
1687 code: "empty-catalog-group",
1688 sarif_description: "Named catalog group has no entries",
1689 meta_description: "A named package manager catalog group has no package entries.",
1690 meta_docs_path: "explanations/dead-code#empty-catalog-groups",
1691 meta_name: "Empty catalog group",
1692 summary_label: "Empty catalog groups",
1693 docs_anchor: "empty-catalog-groups",
1694 result_key: "empty_catalog_groups",
1695 counts_in_total: true,
1696 },
1697 IssueResultMeta {
1698 code: "unresolved-catalog-reference",
1699 sarif_description: "package.json references a catalog that does not declare the package",
1700 meta_description: "A workspace package.json uses a catalog protocol reference that no catalog declares.",
1701 meta_docs_path: "explanations/dead-code#unresolved-catalog-references",
1702 meta_name: "Unresolved catalog reference",
1703 summary_label: "Unresolved catalog references",
1704 docs_anchor: "unresolved-catalog-references",
1705 result_key: "unresolved_catalog_references",
1706 counts_in_total: true,
1707 },
1708 IssueResultMeta {
1709 code: "unused-dependency-override",
1710 sarif_description: "Package-manager override target is not declared or resolved",
1711 meta_description: "A dependency override targets a package not declared by any workspace package and not present in the active readable lockfile.",
1712 meta_docs_path: "explanations/dead-code#unused-dependency-overrides",
1713 meta_name: "Unused dependency override",
1714 summary_label: "Unused dependency overrides",
1715 docs_anchor: "unused-dependency-overrides",
1716 result_key: "unused_dependency_overrides",
1717 counts_in_total: true,
1718 },
1719 IssueResultMeta {
1720 code: "misconfigured-dependency-override",
1721 sarif_description: "Package-manager override has an unparsable key or value",
1722 meta_description: "A dependency override key or value does not parse according to its package manager's grammar.",
1723 meta_docs_path: "explanations/dead-code#misconfigured-dependency-overrides",
1724 meta_name: "Misconfigured dependency override",
1725 summary_label: "Misconfigured dependency overrides",
1726 docs_anchor: "misconfigured-dependency-overrides",
1727 result_key: "misconfigured_dependency_overrides",
1728 counts_in_total: true,
1729 },
1730 IssueResultMeta {
1731 code: "prop-drilling",
1732 sarif_description: "A React/Preact prop is forwarded unchanged through 3+ pass-through components to a distant consumer",
1733 meta_description: "A React or Preact prop is forwarded unchanged through multiple pass-through components to a distant consumer.",
1734 meta_docs_path: "explanations/dead-code#prop-drilling",
1735 meta_name: "Prop drilling",
1736 summary_label: "Prop drilling",
1737 docs_anchor: "prop-drilling",
1738 result_key: "prop_drilling_chains",
1739 counts_in_total: true,
1740 },
1741 IssueResultMeta {
1742 code: "thin-wrapper",
1743 sarif_description: "A React/Preact component whose whole body is a single spread-forwarded child render (a candidate for inlining)",
1744 meta_description: "A React or Preact component is structural indirection around a single spread-forwarded child render.",
1745 meta_docs_path: "explanations/dead-code#thin-wrapper",
1746 meta_name: "Thin wrapper",
1747 summary_label: "Thin wrappers",
1748 docs_anchor: "thin-wrapper",
1749 result_key: "thin_wrappers",
1750 counts_in_total: false,
1751 },
1752 IssueResultMeta {
1753 code: "duplicate-prop-shape",
1754 sarif_description: "Three or more React/Preact components across two or more files declare an identical prop-name set (a missing shared Props type)",
1755 meta_description: "Multiple React or Preact components declare an identical significant prop-name set.",
1756 meta_docs_path: "explanations/dead-code#duplicate-prop-shape",
1757 meta_name: "Duplicate prop shape",
1758 summary_label: "Duplicate prop shapes",
1759 docs_anchor: "duplicate-prop-shape",
1760 result_key: "duplicate_prop_shapes",
1761 counts_in_total: false,
1762 },
1763];
1764
1765pub static KNOWN_ISSUE_KIND_NAMES: LazyLock<Vec<&'static str>> =
1767 LazyLock::new(known_issue_kind_names_from_meta);
1768
1769pub static DEAD_CODE_FILTER_FLAGS: LazyLock<Vec<&'static str>> =
1771 LazyLock::new(dead_code_filter_flags_from_meta);
1772
1773pub static MCP_ISSUE_TYPE_FLAGS: LazyLock<Vec<(&'static str, &'static str)>> =
1775 LazyLock::new(mcp_issue_type_flags_from_meta);
1776
1777pub static CODECLIMATE_RESULT_CODES: LazyLock<Vec<&'static str>> =
1779 LazyLock::new(codeclimate_result_codes_from_meta);
1780
1781fn known_issue_kind_names_from_meta() -> Vec<&'static str> {
1782 let mut names = Vec::new();
1783 for meta in ISSUE_KIND_META.iter().filter(|meta| meta.kind.is_some()) {
1784 push_unique(&mut names, meta.code);
1785 for alias in meta.aliases {
1786 push_unique(&mut names, *alias);
1787 }
1788 }
1789 names
1790}
1791
1792fn dead_code_filter_flags_from_meta() -> Vec<&'static str> {
1793 let mut flags = Vec::new();
1794 for meta in ISSUE_KIND_META {
1795 if let Some(flag) = meta.filter_flag {
1796 push_unique(&mut flags, flag);
1797 }
1798 }
1799 flags
1800}
1801
1802fn mcp_issue_type_flags_from_meta() -> Vec<(&'static str, &'static str)> {
1803 ISSUE_KIND_META
1804 .iter()
1805 .filter_map(|meta| meta.mcp_pair())
1806 .collect()
1807}
1808
1809fn codeclimate_result_codes_from_meta() -> Vec<&'static str> {
1810 ISSUE_RESULT_META
1811 .iter()
1812 .filter(|meta| meta.counts_in_total)
1813 .map(|meta| meta.code)
1814 .collect()
1815}
1816
1817fn push_unique<T: Copy + PartialEq>(items: &mut Vec<T>, item: T) {
1818 if !items.contains(&item) {
1819 items.push(item);
1820 }
1821}
1822
1823#[must_use]
1825pub fn issue_meta_by_code(code: &str) -> Option<&'static IssueKindMeta> {
1826 ISSUE_KIND_META.iter().find(|meta| meta.code == code)
1827}
1828
1829#[must_use]
1831pub fn issue_meta_for_token(token: &str) -> Option<&'static IssueKindMeta> {
1832 ISSUE_KIND_META
1833 .iter()
1834 .find(|meta| meta.code == token || meta.aliases.contains(&token))
1835}
1836
1837#[must_use]
1840pub fn issue_meta_for_contract_token(token: &str) -> Option<&'static IssueKindMeta> {
1841 let normalized = token.trim().trim_start_matches("--").replace('_', "-");
1842 ISSUE_KIND_META
1843 .iter()
1844 .find(|meta| issue_meta_matches_contract_token(meta, &normalized))
1845}
1846
1847#[must_use]
1849pub fn issue_meta_matches_contract_token(meta: &IssueKindMeta, token: &str) -> bool {
1850 let normalized = token.trim().trim_start_matches("--").replace('_', "-");
1851 meta.code == normalized
1852 || meta.aliases.contains(&normalized.as_str())
1853 || meta.config_key == Some(normalized.as_str())
1854 || meta.mcp_issue_type == Some(normalized.as_str())
1855 || meta.suppress_token == Some(normalized.as_str())
1856 || meta.filter_flag.map(|flag| flag.trim_start_matches("--")) == Some(normalized.as_str())
1857}
1858
1859#[must_use]
1861pub fn issue_meta_by_kind(kind: IssueKind) -> Option<&'static IssueKindMeta> {
1862 ISSUE_KIND_META.iter().find(|meta| meta.kind == Some(kind))
1863}
1864
1865#[must_use]
1867pub fn issue_result_meta_by_code(code: &str) -> Option<&'static IssueResultMeta> {
1868 ISSUE_RESULT_META.iter().find(|meta| meta.code == code)
1869}
1870
1871#[must_use]
1873pub fn issue_sarif_rule_ids(code: &str) -> Vec<String> {
1874 let mut ids = vec![format!("fallow/{code}")];
1875 if code == "stale-suppression" {
1876 ids.push("fallow/missing-suppression-reason".to_string());
1877 }
1878 ids
1879}
1880
1881#[must_use]
1883pub fn issue_sarif_rule_description(rule_id: &str) -> Option<&'static str> {
1884 let code = rule_id.strip_prefix("fallow/")?;
1885 if code == "missing-suppression-reason" {
1886 return Some("Suppression comment omits a required reason");
1887 }
1888 issue_result_meta_by_code(code).map(|meta| meta.sarif_description)
1889}
1890
1891#[must_use]
1893pub fn issue_codeclimate_check_names(code: &str) -> Vec<String> {
1894 if !CODECLIMATE_RESULT_CODES.contains(&code) {
1895 return Vec::new();
1896 }
1897 issue_sarif_rule_ids(code)
1898}
1899
1900#[must_use]
1903pub fn issue_docs_anchor(code: &str) -> Option<&'static str> {
1904 issue_result_meta_by_code(code).map(|meta| meta.docs_anchor)
1905}
1906
1907#[must_use]
1909pub fn issue_ts_alias(code: &str) -> Option<TsAliasMeta> {
1910 ISSUE_TS_ALIAS_META
1911 .iter()
1912 .find(|meta| meta.code == code)
1913 .map(|meta| meta.alias)
1914}
1915
1916pub fn diagnostic_issue_metas() -> impl Iterator<Item = &'static IssueKindMeta> {
1918 ISSUE_KIND_META.iter().filter(|meta| meta.lsp)
1919}
1920
1921pub fn result_issue_metas() -> impl Iterator<Item = &'static IssueResultMeta> {
1923 ISSUE_RESULT_META.iter()
1924}
1925
1926pub fn counted_result_issue_metas() -> impl Iterator<Item = &'static IssueResultMeta> {
1928 result_issue_metas().filter(|meta| meta.counts_in_total)
1929}
1930
1931pub const FIXABLE_ISSUE_CODES: &[&str] = &[
1935 "unused-export",
1936 "unused-enum-member",
1937 "unused-dependency",
1938 "unused-dev-dependency",
1939 "unused-optional-dependency",
1940 "unused-catalog-entry",
1941];
1942
1943#[must_use]
1945pub fn issue_is_fixable(code: &str) -> bool {
1946 FIXABLE_ISSUE_CODES.contains(&code)
1947}
1948
1949#[cfg(test)]
1950mod tests {
1951 use std::collections::BTreeSet;
1952
1953 use crate::results::TOTAL_ISSUE_RESULT_KEYS;
1954
1955 use super::*;
1956
1957 #[test]
1958 fn fixable_codes_are_canonical_issue_codes() {
1959 for code in FIXABLE_ISSUE_CODES {
1960 assert!(
1961 issue_meta_by_code(code).is_some(),
1962 "fixable code {code} is not a canonical ISSUE_KIND_META code"
1963 );
1964 }
1965 assert!(issue_is_fixable("unused-export"));
1966 assert!(!issue_is_fixable("unused-file"));
1967 }
1968
1969 #[test]
1970 fn known_names_round_trip_through_metadata() {
1971 for name in KNOWN_ISSUE_KIND_NAMES.iter() {
1972 let meta = issue_meta_for_token(name)
1973 .unwrap_or_else(|| panic!("known issue name {name} missing metadata row"));
1974 assert!(
1975 meta.kind.is_some(),
1976 "known issue name {name} maps to non-IssueKind metadata"
1977 );
1978 }
1979 }
1980
1981 #[test]
1982 fn issue_kind_variants_have_metadata() {
1983 for &kind in IssueKind::ALL {
1984 assert!(
1985 issue_meta_by_kind(kind).is_some(),
1986 "IssueKind {kind:?} has no metadata row"
1987 );
1988 }
1989 }
1990
1991 #[test]
1992 fn dead_code_filter_flags_match_metadata() {
1993 let from_constants: BTreeSet<&str> = DEAD_CODE_FILTER_FLAGS.iter().copied().collect();
1994 let from_meta: BTreeSet<&str> = ISSUE_KIND_META
1995 .iter()
1996 .filter_map(|meta| meta.filter_flag)
1997 .collect();
1998 assert_eq!(from_constants, from_meta);
1999 }
2000
2001 #[test]
2002 fn mcp_issue_type_flags_match_metadata() {
2003 let from_constants: BTreeSet<(&str, &str)> = MCP_ISSUE_TYPE_FLAGS.iter().copied().collect();
2004 let from_meta: BTreeSet<(&str, &str)> = ISSUE_KIND_META
2005 .iter()
2006 .filter_map(|meta| meta.mcp_pair())
2007 .collect();
2008 assert_eq!(from_constants, from_meta);
2009 }
2010
2011 #[test]
2012 fn lsp_exposes_only_actual_diagnostic_codes() {
2013 let codes: BTreeSet<&str> = diagnostic_issue_metas().map(|meta| meta.code).collect();
2014 assert!(codes.contains("boundary-violation"));
2015 assert!(!codes.contains("boundary-coverage"));
2016 assert!(!codes.contains("boundary-call-violation"));
2017 }
2018
2019 #[test]
2020 fn issue_codes_are_unique() {
2021 let mut seen = BTreeSet::new();
2022 for meta in ISSUE_KIND_META {
2023 assert!(seen.insert(meta.code), "duplicate issue code {}", meta.code);
2024 }
2025 }
2026
2027 #[test]
2028 fn contract_tokens_resolve_through_metadata() {
2029 for meta in ISSUE_KIND_META {
2030 assert_eq!(
2031 issue_meta_for_contract_token(meta.code).map(|resolved| resolved.code),
2032 Some(meta.code),
2033 "canonical issue code {} should resolve through contract token lookup",
2034 meta.code
2035 );
2036 for token in meta.aliases {
2037 assert!(
2038 issue_meta_matches_contract_token(meta, token),
2039 "alias {token} should match {}",
2040 meta.code
2041 );
2042 }
2043 for token in [
2044 meta.config_key,
2045 meta.mcp_issue_type,
2046 meta.suppress_token,
2047 meta.filter_flag,
2048 ]
2049 .into_iter()
2050 .flatten()
2051 {
2052 assert!(
2053 issue_meta_for_contract_token(token).is_some(),
2054 "contract token {token} should resolve through metadata"
2055 );
2056 assert!(
2057 issue_meta_matches_contract_token(meta, token),
2058 "contract token {token} should match {}",
2059 meta.code
2060 );
2061 }
2062 }
2063 }
2064
2065 #[test]
2066 fn sarif_rule_descriptions_cover_result_metadata() {
2067 for meta in result_issue_metas() {
2068 for rule_id in issue_sarif_rule_ids(meta.code) {
2069 assert!(
2070 issue_sarif_rule_description(&rule_id).is_some(),
2071 "SARIF rule {rule_id} for {} needs a central description",
2072 meta.code
2073 );
2074 }
2075 }
2076 }
2077
2078 #[test]
2079 fn result_meta_codes_have_issue_metadata() {
2080 for meta in ISSUE_RESULT_META {
2081 assert!(
2082 issue_meta_by_code(meta.code).is_some(),
2083 "result metadata code {} has no issue metadata row",
2084 meta.code
2085 );
2086 }
2087 }
2088
2089 #[test]
2090 fn result_meta_codes_have_docs_anchors() {
2091 for meta in ISSUE_RESULT_META {
2092 let issue = issue_meta_by_code(meta.code)
2093 .unwrap_or_else(|| panic!("result metadata code {} has no issue row", meta.code));
2094 assert_eq!(
2095 issue.docs_anchor(),
2096 Some(meta.docs_anchor),
2097 "result metadata code {} has mismatched docs anchor",
2098 meta.code
2099 );
2100 }
2101 }
2102
2103 #[test]
2104 fn result_meta_codes_have_summary_labels() {
2105 for meta in ISSUE_RESULT_META {
2106 assert!(
2107 !meta.summary_label.is_empty(),
2108 "result metadata code {} has no summary label",
2109 meta.code
2110 );
2111 }
2112 }
2113
2114 #[test]
2115 fn result_meta_codes_have_meta_names() {
2116 for meta in ISSUE_RESULT_META {
2117 assert!(
2118 !meta.meta_name.is_empty(),
2119 "result metadata code {} has no meta name",
2120 meta.code
2121 );
2122 }
2123 }
2124
2125 #[test]
2126 fn result_meta_codes_have_meta_docs_paths() {
2127 for meta in ISSUE_RESULT_META {
2128 assert!(
2129 meta.meta_docs_path.starts_with("explanations/dead-code#"),
2130 "result metadata code {} has invalid meta docs path",
2131 meta.code
2132 );
2133 }
2134 }
2135
2136 #[test]
2137 fn result_meta_codes_have_meta_descriptions() {
2138 for meta in ISSUE_RESULT_META {
2139 assert!(
2140 !meta.meta_description.is_empty(),
2141 "result metadata code {} has no meta description",
2142 meta.code
2143 );
2144 }
2145 }
2146
2147 #[test]
2148 fn ci_format_ids_are_prefixed_and_known() {
2149 let result_codes: BTreeSet<&str> = result_issue_metas().map(|meta| meta.code).collect();
2150 let codeclimate_codes: BTreeSet<&str> = CODECLIMATE_RESULT_CODES.iter().copied().collect();
2151 assert!(codeclimate_codes.is_subset(&result_codes));
2152
2153 for meta in result_issue_metas() {
2154 let issue = issue_meta_by_code(meta.code)
2155 .unwrap_or_else(|| panic!("result metadata code {} has no issue row", meta.code));
2156 assert!(issue.has_result_contract());
2157 let sarif_ids = issue.sarif_rule_ids();
2158 assert!(sarif_ids.contains(&format!("fallow/{}", meta.code)));
2159 for rule_id in sarif_ids {
2160 assert!(
2161 rule_id.starts_with("fallow/"),
2162 "result metadata code {} has unprefixed SARIF rule id {rule_id}",
2163 meta.code
2164 );
2165 }
2166 for check_name in issue.codeclimate_check_names() {
2167 assert!(
2168 check_name.starts_with("fallow/"),
2169 "result metadata code {} has unprefixed CodeClimate check name {check_name}",
2170 meta.code
2171 );
2172 }
2173 }
2174 }
2175
2176 #[test]
2177 fn ts_alias_policy_is_explicit() {
2178 let aliases: BTreeSet<(&str, &str)> = ISSUE_TS_ALIAS_META
2179 .iter()
2180 .map(|meta| (meta.alias.name, meta.alias.parent))
2181 .collect();
2182
2183 assert_eq!(
2184 BTreeSet::from([
2185 ("BoundaryViolation", "BoundaryViolationFinding"),
2186 ("CircularDependency", "CircularDependencyFinding"),
2187 ("DeprecatedExportInUse", "DeprecatedExportInUseFinding"),
2188 (
2189 "DevDependencyInProduction",
2190 "DevDependencyInProductionFinding",
2191 ),
2192 ("DuplicateExport", "DuplicateExportFinding"),
2193 ("EmptyCatalogGroup", "EmptyCatalogGroupFinding"),
2194 (
2195 "MisconfiguredDependencyOverride",
2196 "MisconfiguredDependencyOverrideFinding",
2197 ),
2198 ("PrivateTypeLeak", "PrivateTypeLeakFinding"),
2199 ("PackageCycle", "PackageCycleFinding"),
2200 ("ReExportCycle", "ReExportCycleFinding"),
2201 ("TestOnlyDependency", "TestOnlyDependencyFinding"),
2202 ("TypeOnlyDependency", "TypeOnlyDependencyFinding"),
2203 (
2204 "UnresolvedCatalogReference",
2205 "UnresolvedCatalogReferenceFinding",
2206 ),
2207 ("UnresolvedImport", "UnresolvedImportFinding"),
2208 ("UnlistedDependency", "UnlistedDependencyFinding"),
2209 ("UnusedCatalogEntry", "UnusedCatalogEntryFinding"),
2210 ("UnusedDependency", "UnusedDependencyFinding"),
2211 ("UnusedDependency", "UnusedDevDependencyFinding"),
2212 ("UnusedDependency", "UnusedOptionalDependencyFinding"),
2213 (
2214 "UnusedDependencyOverride",
2215 "UnusedDependencyOverrideFinding",
2216 ),
2217 ("UnusedExport", "UnusedExportFinding"),
2218 ("UnusedFile", "UnusedFileFinding"),
2219 ("UnusedMember", "UnusedClassMemberFinding"),
2220 ("UnusedMember", "UnusedEnumMemberFinding"),
2221 ("UnusedMember", "UnusedStoreMemberFinding"),
2222 ]),
2223 aliases
2224 );
2225 }
2226
2227 #[test]
2228 fn ts_alias_registry_rows_match_result_metadata() {
2229 let result_codes: BTreeSet<&str> = result_issue_metas().map(|meta| meta.code).collect();
2230 let mut seen_codes = BTreeSet::new();
2231 for meta in ISSUE_TS_ALIAS_META {
2232 assert!(
2233 seen_codes.insert(meta.code),
2234 "duplicate TypeScript alias row for {}",
2235 meta.code
2236 );
2237 assert!(
2238 result_codes.contains(meta.code),
2239 "TypeScript alias row {} has no result metadata",
2240 meta.code
2241 );
2242 assert!(
2243 meta.alias.parent.ends_with("Finding"),
2244 "TypeScript alias row {} must point at a generated Finding wrapper",
2245 meta.code
2246 );
2247 assert_eq!(
2248 Some(meta.alias),
2249 issue_meta_by_code(meta.code).and_then(|issue| issue.ts_alias()),
2250 "IssueKindMeta helper must resolve TypeScript alias row {} through the registry",
2251 meta.code
2252 );
2253 }
2254 }
2255
2256 #[test]
2257 fn result_keys_are_unique() {
2258 let mut seen = BTreeSet::new();
2259 for meta in ISSUE_RESULT_META {
2260 assert!(
2261 seen.insert(meta.result_key),
2262 "duplicate result key {}",
2263 meta.result_key
2264 );
2265 }
2266 }
2267
2268 #[test]
2269 fn counted_result_keys_match_total_issue_fields() {
2270 let from_total: BTreeSet<&str> = TOTAL_ISSUE_RESULT_KEYS.iter().copied().collect();
2271 let from_meta: BTreeSet<&str> = counted_result_issue_metas()
2272 .map(|meta| meta.result_key)
2273 .collect();
2274 assert_eq!(from_total, from_meta);
2275 }
2276
2277 #[test]
2278 fn advisory_result_keys_are_explicitly_excluded_from_total() {
2279 let expected = BTreeSet::from(["duplicate_prop_shapes", "thin_wrappers"]);
2280 let from_meta: BTreeSet<&str> = result_issue_metas()
2281 .filter(|meta| !meta.counts_in_total)
2282 .map(|meta| meta.result_key)
2283 .collect();
2284 assert_eq!(expected, from_meta);
2285 }
2286}