1use crate::suppress::IssueKind;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct IssueKindMeta {
12 pub kind: Option<IssueKind>,
14 pub code: &'static str,
16 pub aliases: &'static [&'static str],
18 pub label: &'static str,
20 pub config_key: Option<&'static str>,
22 pub filter_flag: Option<&'static str>,
24 pub mcp_issue_type: Option<&'static str>,
26 pub suppress_token: Option<&'static str>,
28 pub suppress_file_level: bool,
30 pub lsp: bool,
33 pub docs_category: &'static str,
35}
36
37impl IssueKindMeta {
38 #[must_use]
40 pub const fn mcp_pair(self) -> Option<(&'static str, &'static str)> {
41 match (self.mcp_issue_type, self.filter_flag) {
42 (Some(issue_type), Some(flag)) => Some((issue_type, flag)),
43 _ => None,
44 }
45 }
46
47 #[must_use]
49 pub fn has_result_contract(self) -> bool {
50 issue_result_meta_by_code(self.code).is_some()
51 }
52
53 #[must_use]
55 pub fn sarif_rule_ids(self) -> Vec<String> {
56 issue_sarif_rule_ids(self.code)
57 }
58
59 #[must_use]
61 pub fn sarif_enabled(self) -> bool {
62 self.has_result_contract()
63 }
64
65 #[must_use]
67 pub fn codeclimate_check_names(self) -> Vec<String> {
68 issue_codeclimate_check_names(self.code)
69 }
70
71 #[must_use]
73 pub fn codeclimate_enabled(self) -> bool {
74 !self.codeclimate_check_names().is_empty()
75 }
76
77 #[must_use]
79 pub fn docs_anchor(self) -> Option<&'static str> {
80 issue_docs_anchor(self.code)
81 }
82
83 #[must_use]
85 pub fn ts_alias(self) -> Option<TsAliasMeta> {
86 issue_ts_alias(self.code)
87 }
88}
89
90pub const ISSUE_KIND_META: &[IssueKindMeta] = &[
92 IssueKindMeta {
93 kind: Some(IssueKind::CodeDuplication),
94 code: "code-duplication",
95 aliases: &[],
96 label: "Code Duplication",
97 config_key: None,
98 filter_flag: None,
99 mcp_issue_type: None,
100 suppress_token: Some("code-duplication"),
101 suppress_file_level: false,
102 lsp: true,
103 docs_category: "dupes",
104 },
105 IssueKindMeta {
106 kind: Some(IssueKind::UnusedFile),
107 code: "unused-file",
108 aliases: &[],
109 label: "Unused Files",
110 config_key: Some("unused-files"),
111 filter_flag: Some("--unused-files"),
112 mcp_issue_type: Some("unused-files"),
113 suppress_token: Some("unused-file"),
114 suppress_file_level: true,
115 lsp: true,
116 docs_category: "source",
117 },
118 IssueKindMeta {
119 kind: Some(IssueKind::UnusedExport),
120 code: "unused-export",
121 aliases: &[],
122 label: "Unused Exports",
123 config_key: Some("unused-exports"),
124 filter_flag: Some("--unused-exports"),
125 mcp_issue_type: Some("unused-exports"),
126 suppress_token: Some("unused-export"),
127 suppress_file_level: false,
128 lsp: true,
129 docs_category: "source",
130 },
131 IssueKindMeta {
132 kind: Some(IssueKind::UnusedType),
133 code: "unused-type",
134 aliases: &[],
135 label: "Unused Types",
136 config_key: Some("unused-types"),
137 filter_flag: Some("--unused-types"),
138 mcp_issue_type: Some("unused-types"),
139 suppress_token: Some("unused-type"),
140 suppress_file_level: false,
141 lsp: true,
142 docs_category: "source",
143 },
144 IssueKindMeta {
145 kind: Some(IssueKind::PrivateTypeLeak),
146 code: "private-type-leak",
147 aliases: &[],
148 label: "Private Type Leaks",
149 config_key: Some("private-type-leaks"),
150 filter_flag: Some("--private-type-leaks"),
151 mcp_issue_type: Some("private-type-leaks"),
152 suppress_token: Some("private-type-leak"),
153 suppress_file_level: false,
154 lsp: true,
155 docs_category: "source",
156 },
157 IssueKindMeta {
158 kind: Some(IssueKind::UnusedDependency),
159 code: "unused-dependency",
160 aliases: &[],
161 label: "Unused Dependencies",
162 config_key: Some("unused-dependencies"),
163 filter_flag: Some("--unused-deps"),
164 mcp_issue_type: Some("unused-deps"),
165 suppress_token: None,
166 suppress_file_level: false,
167 lsp: true,
168 docs_category: "dependency",
169 },
170 IssueKindMeta {
171 kind: Some(IssueKind::UnusedDevDependency),
172 code: "unused-dev-dependency",
173 aliases: &[],
174 label: "Unused Dev Dependencies",
175 config_key: Some("unused-dev-dependencies"),
176 filter_flag: Some("--unused-deps"),
177 mcp_issue_type: None,
178 suppress_token: None,
179 suppress_file_level: false,
180 lsp: true,
181 docs_category: "dependency",
182 },
183 IssueKindMeta {
184 kind: None,
185 code: "unused-optional-dependency",
186 aliases: &[],
187 label: "Unused Optional Dependencies",
188 config_key: Some("unused-optional-dependencies"),
189 filter_flag: Some("--unused-deps"),
190 mcp_issue_type: None,
191 suppress_token: None,
192 suppress_file_level: false,
193 lsp: true,
194 docs_category: "dependency",
195 },
196 IssueKindMeta {
197 kind: Some(IssueKind::UnusedEnumMember),
198 code: "unused-enum-member",
199 aliases: &[],
200 label: "Unused Enum Members",
201 config_key: Some("unused-enum-members"),
202 filter_flag: Some("--unused-enum-members"),
203 mcp_issue_type: Some("unused-enum-members"),
204 suppress_token: Some("unused-enum-member"),
205 suppress_file_level: false,
206 lsp: true,
207 docs_category: "source",
208 },
209 IssueKindMeta {
210 kind: Some(IssueKind::UnusedClassMember),
211 code: "unused-class-member",
212 aliases: &[],
213 label: "Unused Class Members",
214 config_key: Some("unused-class-members"),
215 filter_flag: Some("--unused-class-members"),
216 mcp_issue_type: Some("unused-class-members"),
217 suppress_token: Some("unused-class-member"),
218 suppress_file_level: false,
219 lsp: true,
220 docs_category: "source",
221 },
222 IssueKindMeta {
223 kind: Some(IssueKind::UnusedStoreMember),
224 code: "unused-store-member",
225 aliases: &["unused-store-members"],
226 label: "Unused Store Members",
227 config_key: Some("unused-store-members"),
228 filter_flag: Some("--unused-store-members"),
229 mcp_issue_type: Some("unused-store-members"),
230 suppress_token: Some("unused-store-member"),
231 suppress_file_level: false,
232 lsp: true,
233 docs_category: "framework",
234 },
235 IssueKindMeta {
236 kind: Some(IssueKind::UnresolvedImport),
237 code: "unresolved-import",
238 aliases: &[],
239 label: "Unresolved Imports",
240 config_key: Some("unresolved-imports"),
241 filter_flag: Some("--unresolved-imports"),
242 mcp_issue_type: Some("unresolved-imports"),
243 suppress_token: Some("unresolved-import"),
244 suppress_file_level: false,
245 lsp: true,
246 docs_category: "dependency",
247 },
248 IssueKindMeta {
249 kind: Some(IssueKind::UnlistedDependency),
250 code: "unlisted-dependency",
251 aliases: &[],
252 label: "Unlisted Dependencies",
253 config_key: Some("unlisted-dependencies"),
254 filter_flag: Some("--unlisted-deps"),
255 mcp_issue_type: Some("unlisted-deps"),
256 suppress_token: None,
257 suppress_file_level: false,
258 lsp: true,
259 docs_category: "dependency",
260 },
261 IssueKindMeta {
262 kind: Some(IssueKind::DuplicateExport),
263 code: "duplicate-export",
264 aliases: &[],
265 label: "Duplicate Exports",
266 config_key: Some("duplicate-exports"),
267 filter_flag: Some("--duplicate-exports"),
268 mcp_issue_type: Some("duplicate-exports"),
269 suppress_token: Some("duplicate-export"),
270 suppress_file_level: true,
271 lsp: true,
272 docs_category: "source",
273 },
274 IssueKindMeta {
275 kind: Some(IssueKind::TypeOnlyDependency),
276 code: "type-only-dependency",
277 aliases: &[],
278 label: "Type-Only Dependencies",
279 config_key: Some("type-only-dependencies"),
280 filter_flag: Some("--unused-deps"),
281 mcp_issue_type: None,
282 suppress_token: None,
283 suppress_file_level: false,
284 lsp: true,
285 docs_category: "dependency",
286 },
287 IssueKindMeta {
288 kind: Some(IssueKind::TestOnlyDependency),
289 code: "test-only-dependency",
290 aliases: &[],
291 label: "Test-Only Dependencies",
292 config_key: Some("test-only-dependencies"),
293 filter_flag: Some("--unused-deps"),
294 mcp_issue_type: None,
295 suppress_token: None,
296 suppress_file_level: false,
297 lsp: true,
298 docs_category: "dependency",
299 },
300 IssueKindMeta {
301 kind: Some(IssueKind::CircularDependency),
302 code: "circular-dependency",
303 aliases: &["circular-dependencies"],
304 label: "Circular Dependencies",
305 config_key: Some("circular-dependencies"),
306 filter_flag: Some("--circular-deps"),
307 mcp_issue_type: Some("circular-deps"),
308 suppress_token: Some("circular-dependency"),
309 suppress_file_level: false,
310 lsp: true,
311 docs_category: "architecture",
312 },
313 IssueKindMeta {
314 kind: Some(IssueKind::ReExportCycle),
315 code: "re-export-cycle",
316 aliases: &["re-export-cycles", "reexport-cycle", "reexport-cycles"],
317 label: "Re-Export Cycles",
318 config_key: Some("re-export-cycle"),
319 filter_flag: Some("--re-export-cycles"),
320 mcp_issue_type: Some("re-export-cycles"),
321 suppress_token: Some("re-export-cycle"),
322 suppress_file_level: true,
323 lsp: true,
324 docs_category: "architecture",
325 },
326 IssueKindMeta {
327 kind: Some(IssueKind::BoundaryViolation),
328 code: "boundary-violation",
329 aliases: &[],
330 label: "Boundary Violations",
331 config_key: Some("boundary-violation"),
332 filter_flag: Some("--boundary-violations"),
333 mcp_issue_type: Some("boundary-violations"),
334 suppress_token: Some("boundary-violation"),
335 suppress_file_level: false,
336 lsp: true,
337 docs_category: "architecture",
338 },
339 IssueKindMeta {
340 kind: None,
341 code: "boundary-coverage",
342 aliases: &["boundary-coverage-violations"],
343 label: "Boundary Coverage",
344 config_key: Some("boundary-violation"),
345 filter_flag: Some("--boundary-violations"),
346 mcp_issue_type: None,
347 suppress_token: Some("boundary-violation"),
348 suppress_file_level: true,
349 lsp: false,
350 docs_category: "architecture",
351 },
352 IssueKindMeta {
353 kind: Some(IssueKind::BoundaryViolation),
354 code: "boundary-call-violation",
355 aliases: &["boundary-call-violations"],
356 label: "Boundary Call Violations",
357 config_key: Some("boundary-violation"),
358 filter_flag: Some("--boundary-violations"),
359 mcp_issue_type: None,
360 suppress_token: Some("boundary-call-violation"),
361 suppress_file_level: false,
362 lsp: false,
363 docs_category: "architecture",
364 },
365 IssueKindMeta {
366 kind: Some(IssueKind::PolicyViolation),
367 code: "policy-violation",
368 aliases: &["policy-violations"],
369 label: "Policy Violations",
370 config_key: Some("policy-violation"),
371 filter_flag: Some("--policy-violations"),
372 mcp_issue_type: Some("policy-violations"),
373 suppress_token: Some("policy-violation"),
374 suppress_file_level: false,
375 lsp: true,
376 docs_category: "architecture",
377 },
378 IssueKindMeta {
379 kind: Some(IssueKind::InvalidClientExport),
380 code: "invalid-client-export",
381 aliases: &["invalid-client-exports"],
382 label: "Invalid Client Exports",
383 config_key: Some("invalid-client-export"),
384 filter_flag: None,
385 mcp_issue_type: None,
386 suppress_token: Some("invalid-client-export"),
387 suppress_file_level: false,
388 lsp: true,
389 docs_category: "framework",
390 },
391 IssueKindMeta {
392 kind: Some(IssueKind::MixedClientServerBarrel),
393 code: "mixed-client-server-barrel",
394 aliases: &["mixed-client-server-barrels"],
395 label: "Mixed Client/Server Barrels",
396 config_key: Some("mixed-client-server-barrel"),
397 filter_flag: None,
398 mcp_issue_type: None,
399 suppress_token: Some("mixed-client-server-barrel"),
400 suppress_file_level: false,
401 lsp: true,
402 docs_category: "framework",
403 },
404 IssueKindMeta {
405 kind: Some(IssueKind::MisplacedDirective),
406 code: "misplaced-directive",
407 aliases: &["misplaced-directives"],
408 label: "Misplaced Directives",
409 config_key: Some("misplaced-directive"),
410 filter_flag: None,
411 mcp_issue_type: None,
412 suppress_token: Some("misplaced-directive"),
413 suppress_file_level: false,
414 lsp: true,
415 docs_category: "framework",
416 },
417 IssueKindMeta {
418 kind: Some(IssueKind::UnprovidedInject),
419 code: "unprovided-inject",
420 aliases: &["unprovided-injects"],
421 label: "Unprovided Injects",
422 config_key: Some("unprovided-injects"),
423 filter_flag: Some("--unprovided-injects"),
424 mcp_issue_type: Some("unprovided-injects"),
425 suppress_token: Some("unprovided-inject"),
426 suppress_file_level: false,
427 lsp: true,
428 docs_category: "framework",
429 },
430 IssueKindMeta {
431 kind: Some(IssueKind::UnrenderedComponent),
432 code: "unrendered-component",
433 aliases: &["unrendered-components"],
434 label: "Unrendered Components",
435 config_key: Some("unrendered-components"),
436 filter_flag: Some("--unrendered-components"),
437 mcp_issue_type: Some("unrendered-components"),
438 suppress_token: Some("unrendered-component"),
439 suppress_file_level: false,
440 lsp: true,
441 docs_category: "framework",
442 },
443 IssueKindMeta {
444 kind: Some(IssueKind::UnusedComponentProp),
445 code: "unused-component-prop",
446 aliases: &["unused-component-props"],
447 label: "Unused Component Props",
448 config_key: Some("unused-component-props"),
449 filter_flag: Some("--unused-component-props"),
450 mcp_issue_type: Some("unused-component-props"),
451 suppress_token: Some("unused-component-prop"),
452 suppress_file_level: false,
453 lsp: true,
454 docs_category: "framework",
455 },
456 IssueKindMeta {
457 kind: Some(IssueKind::UnusedComponentEmit),
458 code: "unused-component-emit",
459 aliases: &["unused-component-emits"],
460 label: "Unused Component Emits",
461 config_key: Some("unused-component-emits"),
462 filter_flag: Some("--unused-component-emits"),
463 mcp_issue_type: Some("unused-component-emits"),
464 suppress_token: Some("unused-component-emit"),
465 suppress_file_level: false,
466 lsp: true,
467 docs_category: "framework",
468 },
469 IssueKindMeta {
470 kind: Some(IssueKind::UnusedComponentInput),
471 code: "unused-component-input",
472 aliases: &["unused-component-inputs"],
473 label: "Unused Component Inputs",
474 config_key: Some("unused-component-inputs"),
475 filter_flag: Some("--unused-component-inputs"),
476 mcp_issue_type: Some("unused-component-inputs"),
477 suppress_token: Some("unused-component-input"),
478 suppress_file_level: false,
479 lsp: true,
480 docs_category: "framework",
481 },
482 IssueKindMeta {
483 kind: Some(IssueKind::UnusedComponentOutput),
484 code: "unused-component-output",
485 aliases: &["unused-component-outputs"],
486 label: "Unused Component Outputs",
487 config_key: Some("unused-component-outputs"),
488 filter_flag: Some("--unused-component-outputs"),
489 mcp_issue_type: Some("unused-component-outputs"),
490 suppress_token: Some("unused-component-output"),
491 suppress_file_level: false,
492 lsp: true,
493 docs_category: "framework",
494 },
495 IssueKindMeta {
496 kind: Some(IssueKind::UnusedSvelteEvent),
497 code: "unused-svelte-event",
498 aliases: &["unused-svelte-events"],
499 label: "Unused Svelte Events",
500 config_key: Some("unused-svelte-events"),
501 filter_flag: Some("--unused-svelte-events"),
502 mcp_issue_type: Some("unused-svelte-events"),
503 suppress_token: Some("unused-svelte-event"),
504 suppress_file_level: false,
505 lsp: true,
506 docs_category: "framework",
507 },
508 IssueKindMeta {
509 kind: Some(IssueKind::UnusedServerAction),
510 code: "unused-server-action",
511 aliases: &["unused-server-actions"],
512 label: "Unused Server Actions",
513 config_key: Some("unused-server-actions"),
514 filter_flag: Some("--unused-server-actions"),
515 mcp_issue_type: Some("unused-server-actions"),
516 suppress_token: Some("unused-server-action"),
517 suppress_file_level: false,
518 lsp: true,
519 docs_category: "framework",
520 },
521 IssueKindMeta {
522 kind: Some(IssueKind::UnusedLoadDataKey),
523 code: "unused-load-data-key",
524 aliases: &["unused-load-data-keys"],
525 label: "Unused Load Data Keys",
526 config_key: Some("unused-load-data-keys"),
527 filter_flag: Some("--unused-load-data-keys"),
528 mcp_issue_type: Some("unused-load-data-keys"),
529 suppress_token: Some("unused-load-data-key"),
530 suppress_file_level: false,
531 lsp: true,
532 docs_category: "framework",
533 },
534 IssueKindMeta {
535 kind: Some(IssueKind::RouteCollision),
536 code: "route-collision",
537 aliases: &["route-collisions"],
538 label: "Route Collisions",
539 config_key: Some("route-collision"),
540 filter_flag: None,
541 mcp_issue_type: None,
542 suppress_token: Some("route-collision"),
543 suppress_file_level: true,
544 lsp: true,
545 docs_category: "framework",
546 },
547 IssueKindMeta {
548 kind: Some(IssueKind::DynamicSegmentNameConflict),
549 code: "dynamic-segment-name-conflict",
550 aliases: &["dynamic-segment-name-conflicts"],
551 label: "Dynamic Segment Conflicts",
552 config_key: Some("dynamic-segment-name-conflict"),
553 filter_flag: None,
554 mcp_issue_type: None,
555 suppress_token: Some("dynamic-segment-name-conflict"),
556 suppress_file_level: true,
557 lsp: true,
558 docs_category: "framework",
559 },
560 IssueKindMeta {
561 kind: Some(IssueKind::StaleSuppression),
562 code: "stale-suppression",
563 aliases: &[],
564 label: "Stale Suppressions",
565 config_key: Some("stale-suppressions"),
566 filter_flag: Some("--stale-suppressions"),
567 mcp_issue_type: Some("stale-suppressions"),
568 suppress_token: None,
569 suppress_file_level: false,
570 lsp: true,
571 docs_category: "source",
572 },
573 IssueKindMeta {
574 kind: Some(IssueKind::PnpmCatalogEntry),
575 code: "unused-catalog-entry",
576 aliases: &["unused-catalog-entries"],
577 label: "Unused Catalog Entries",
578 config_key: Some("unused-catalog-entries"),
579 filter_flag: Some("--unused-catalog-entries"),
580 mcp_issue_type: Some("unused-catalog-entries"),
581 suppress_token: None,
582 suppress_file_level: false,
583 lsp: true,
584 docs_category: "dependency",
585 },
586 IssueKindMeta {
587 kind: Some(IssueKind::EmptyCatalogGroup),
588 code: "empty-catalog-group",
589 aliases: &["empty-catalog-groups"],
590 label: "Empty Catalog Groups",
591 config_key: Some("empty-catalog-groups"),
592 filter_flag: Some("--empty-catalog-groups"),
593 mcp_issue_type: Some("empty-catalog-groups"),
594 suppress_token: None,
595 suppress_file_level: false,
596 lsp: true,
597 docs_category: "dependency",
598 },
599 IssueKindMeta {
600 kind: Some(IssueKind::UnresolvedCatalogReference),
601 code: "unresolved-catalog-reference",
602 aliases: &["unresolved-catalog-references"],
603 label: "Unresolved Catalog References",
604 config_key: Some("unresolved-catalog-references"),
605 filter_flag: Some("--unresolved-catalog-references"),
606 mcp_issue_type: Some("unresolved-catalog-references"),
607 suppress_token: None,
608 suppress_file_level: false,
609 lsp: true,
610 docs_category: "dependency",
611 },
612 IssueKindMeta {
613 kind: Some(IssueKind::UnusedDependencyOverride),
614 code: "unused-dependency-override",
615 aliases: &["unused-dependency-overrides"],
616 label: "Unused Dependency Overrides",
617 config_key: Some("unused-dependency-overrides"),
618 filter_flag: Some("--unused-dependency-overrides"),
619 mcp_issue_type: Some("unused-dependency-overrides"),
620 suppress_token: None,
621 suppress_file_level: false,
622 lsp: true,
623 docs_category: "dependency",
624 },
625 IssueKindMeta {
626 kind: Some(IssueKind::MisconfiguredDependencyOverride),
627 code: "misconfigured-dependency-override",
628 aliases: &["misconfigured-dependency-overrides"],
629 label: "Misconfigured Dependency Overrides",
630 config_key: Some("misconfigured-dependency-overrides"),
631 filter_flag: Some("--misconfigured-dependency-overrides"),
632 mcp_issue_type: Some("misconfigured-dependency-overrides"),
633 suppress_token: None,
634 suppress_file_level: false,
635 lsp: true,
636 docs_category: "dependency",
637 },
638 IssueKindMeta {
639 kind: Some(IssueKind::SecuritySink),
640 code: "security-sink",
641 aliases: &[],
642 label: "Security Sink Candidates",
643 config_key: Some("security-sink"),
644 filter_flag: None,
645 mcp_issue_type: None,
646 suppress_token: Some("security-sink"),
647 suppress_file_level: false,
648 lsp: true,
649 docs_category: "security",
650 },
651 IssueKindMeta {
652 kind: Some(IssueKind::SecurityClientServerLeak),
653 code: "security-client-server-leak",
654 aliases: &[],
655 label: "Security Client-Server Leaks",
656 config_key: Some("security-client-server-leak"),
657 filter_flag: None,
658 mcp_issue_type: None,
659 suppress_token: Some("security-client-server-leak"),
660 suppress_file_level: true,
661 lsp: true,
662 docs_category: "security",
663 },
664 IssueKindMeta {
665 kind: Some(IssueKind::CoverageGaps),
666 code: "coverage-gaps",
667 aliases: &[],
668 label: "Coverage Gaps",
669 config_key: Some("coverage-gaps"),
670 filter_flag: None,
671 mcp_issue_type: None,
672 suppress_token: Some("coverage-gaps"),
673 suppress_file_level: true,
674 lsp: false,
675 docs_category: "health",
676 },
677 IssueKindMeta {
678 kind: Some(IssueKind::FeatureFlag),
679 code: "feature-flag",
680 aliases: &[],
681 label: "Feature Flags",
682 config_key: Some("feature-flags"),
683 filter_flag: None,
684 mcp_issue_type: None,
685 suppress_token: Some("feature-flag"),
686 suppress_file_level: false,
687 lsp: false,
688 docs_category: "flags",
689 },
690 IssueKindMeta {
691 kind: Some(IssueKind::Complexity),
692 code: "complexity",
693 aliases: &[],
694 label: "Complexity",
695 config_key: None,
696 filter_flag: None,
697 mcp_issue_type: None,
698 suppress_token: Some("complexity"),
699 suppress_file_level: false,
700 lsp: false,
701 docs_category: "health",
702 },
703 IssueKindMeta {
704 kind: Some(IssueKind::PropDrilling),
705 code: "prop-drilling",
706 aliases: &[],
707 label: "Prop Drilling",
708 config_key: Some("prop-drilling"),
709 filter_flag: None,
710 mcp_issue_type: None,
711 suppress_token: Some("prop-drilling"),
712 suppress_file_level: false,
713 lsp: false,
714 docs_category: "source",
715 },
716 IssueKindMeta {
717 kind: Some(IssueKind::ThinWrapper),
718 code: "thin-wrapper",
719 aliases: &["thin-wrappers"],
720 label: "Thin Wrappers",
721 config_key: Some("thin-wrapper"),
722 filter_flag: None,
723 mcp_issue_type: None,
724 suppress_token: Some("thin-wrapper"),
725 suppress_file_level: false,
726 lsp: false,
727 docs_category: "source",
728 },
729 IssueKindMeta {
730 kind: Some(IssueKind::DuplicatePropShape),
731 code: "duplicate-prop-shape",
732 aliases: &["duplicate-prop-shapes"],
733 label: "Duplicate Prop Shapes",
734 config_key: Some("duplicate-prop-shape"),
735 filter_flag: None,
736 mcp_issue_type: None,
737 suppress_token: Some("duplicate-prop-shape"),
738 suppress_file_level: false,
739 lsp: false,
740 docs_category: "source",
741 },
742];
743
744#[derive(Debug, Clone, Copy, PartialEq, Eq)]
746#[non_exhaustive]
747pub struct IssueResultMeta {
748 pub code: &'static str,
750 pub meta_description: &'static str,
752 pub meta_docs_path: &'static str,
754 pub meta_name: &'static str,
756 pub summary_label: &'static str,
758 pub docs_anchor: &'static str,
760 pub result_key: &'static str,
762 pub counts_in_total: bool,
764}
765
766#[derive(Debug, Clone, Copy, PartialEq, Eq)]
768pub struct TsAliasMeta {
769 pub name: &'static str,
771 pub parent: &'static str,
773}
774
775pub const ISSUE_RESULT_META: &[IssueResultMeta] = &[
777 IssueResultMeta {
778 code: "unused-file",
779 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.",
780 meta_docs_path: "explanations/dead-code#unused-files",
781 meta_name: "Unused Files",
782 summary_label: "Unused files",
783 docs_anchor: "unused-files",
784 result_key: "unused_files",
785 counts_in_total: true,
786 },
787 IssueResultMeta {
788 code: "unused-export",
789 meta_description: "Named exports that are never imported by any other module in the project, including direct exports and re-exports through barrel files.",
790 meta_docs_path: "explanations/dead-code#unused-exports",
791 meta_name: "Unused Exports",
792 summary_label: "Unused exports",
793 docs_anchor: "unused-exports",
794 result_key: "unused_exports",
795 counts_in_total: true,
796 },
797 IssueResultMeta {
798 code: "unused-type",
799 meta_description: "Type-only exports that are never imported. These do not generate runtime code but add maintenance burden.",
800 meta_docs_path: "explanations/dead-code#unused-types",
801 meta_name: "Unused Type Exports",
802 summary_label: "Unused types",
803 docs_anchor: "unused-types",
804 result_key: "unused_types",
805 counts_in_total: true,
806 },
807 IssueResultMeta {
808 code: "private-type-leak",
809 meta_description: "Exported values or types whose public TypeScript signature references a same-file type declaration that is not exported.",
810 meta_docs_path: "explanations/dead-code#private-type-leaks",
811 meta_name: "Private Type Leaks",
812 summary_label: "Private type leaks",
813 docs_anchor: "private-type-leaks",
814 result_key: "private_type_leaks",
815 counts_in_total: true,
816 },
817 IssueResultMeta {
818 code: "unused-dependency",
819 meta_description: "Packages listed in dependencies that are never imported or required by any source file.",
820 meta_docs_path: "explanations/dead-code#unused-dependencies",
821 meta_name: "Unused Dependencies",
822 summary_label: "Unused dependencies",
823 docs_anchor: "unused-dependencies",
824 result_key: "unused_dependencies",
825 counts_in_total: true,
826 },
827 IssueResultMeta {
828 code: "unused-dev-dependency",
829 meta_description: "Packages listed in devDependencies that are never imported by test files, config files, or scripts.",
830 meta_docs_path: "explanations/dead-code#unused-devdependencies",
831 meta_name: "Unused Dev Dependencies",
832 summary_label: "Unused devDependencies",
833 docs_anchor: "unused-dependencies",
834 result_key: "unused_dev_dependencies",
835 counts_in_total: true,
836 },
837 IssueResultMeta {
838 code: "unused-optional-dependency",
839 meta_description: "Packages listed in optionalDependencies that are never imported.",
840 meta_docs_path: "explanations/dead-code#unused-optionaldependencies",
841 meta_name: "Unused Optional Dependencies",
842 summary_label: "Unused optionalDependencies",
843 docs_anchor: "unused-dependencies",
844 result_key: "unused_optional_dependencies",
845 counts_in_total: true,
846 },
847 IssueResultMeta {
848 code: "unused-enum-member",
849 meta_description: "Enum members that are never referenced in the codebase.",
850 meta_docs_path: "explanations/dead-code#unused-enum-members",
851 meta_name: "Unused Enum Members",
852 summary_label: "Unused enum members",
853 docs_anchor: "unused-enum-members",
854 result_key: "unused_enum_members",
855 counts_in_total: true,
856 },
857 IssueResultMeta {
858 code: "unused-class-member",
859 meta_description: "Class methods and properties that are never referenced outside the class.",
860 meta_docs_path: "explanations/dead-code#unused-class-members",
861 meta_name: "Unused Class Members",
862 summary_label: "Unused class members",
863 docs_anchor: "unused-class-members",
864 result_key: "unused_class_members",
865 counts_in_total: true,
866 },
867 IssueResultMeta {
868 code: "unused-store-member",
869 meta_description: "Pinia store members declared but never accessed by any consumer project-wide.",
870 meta_docs_path: "explanations/dead-code#unused-store-members",
871 meta_name: "Unused Store Members",
872 summary_label: "Unused store members",
873 docs_anchor: "unused-store-members",
874 result_key: "unused_store_members",
875 counts_in_total: true,
876 },
877 IssueResultMeta {
878 code: "unresolved-import",
879 meta_description: "Import specifiers that could not be resolved to a file on disk.",
880 meta_docs_path: "explanations/dead-code#unresolved-imports",
881 meta_name: "Unresolved Imports",
882 summary_label: "Unresolved imports",
883 docs_anchor: "unresolved-imports",
884 result_key: "unresolved_imports",
885 counts_in_total: true,
886 },
887 IssueResultMeta {
888 code: "unlisted-dependency",
889 meta_description: "Packages imported in source code but not listed in package.json.",
890 meta_docs_path: "explanations/dead-code#unlisted-dependencies",
891 meta_name: "Unlisted Dependencies",
892 summary_label: "Unlisted dependencies",
893 docs_anchor: "unlisted-dependencies",
894 result_key: "unlisted_dependencies",
895 counts_in_total: true,
896 },
897 IssueResultMeta {
898 code: "duplicate-export",
899 meta_description: "The same export name is defined in multiple modules.",
900 meta_docs_path: "explanations/dead-code#duplicate-exports",
901 meta_name: "Duplicate Exports",
902 summary_label: "Duplicate exports",
903 docs_anchor: "duplicate-exports",
904 result_key: "duplicate_exports",
905 counts_in_total: true,
906 },
907 IssueResultMeta {
908 code: "type-only-dependency",
909 meta_description: "Production dependencies that are only imported via type-only imports.",
910 meta_docs_path: "explanations/dead-code#type-only-dependencies",
911 meta_name: "Type-only Dependencies",
912 summary_label: "Type-only dependencies",
913 docs_anchor: "type-only-dependencies",
914 result_key: "type_only_dependencies",
915 counts_in_total: true,
916 },
917 IssueResultMeta {
918 code: "test-only-dependency",
919 meta_description: "Production dependencies that are only imported from test files.",
920 meta_docs_path: "explanations/dead-code#test-only-dependencies",
921 meta_name: "Test-only Dependencies",
922 summary_label: "Test-only dependencies",
923 docs_anchor: "test-only-dependencies",
924 result_key: "test_only_dependencies",
925 counts_in_total: true,
926 },
927 IssueResultMeta {
928 code: "circular-dependency",
929 meta_description: "A cycle in the module import graph.",
930 meta_docs_path: "explanations/dead-code#circular-dependencies",
931 meta_name: "Circular Dependencies",
932 summary_label: "Circular dependencies",
933 docs_anchor: "circular-dependencies",
934 result_key: "circular_dependencies",
935 counts_in_total: true,
936 },
937 IssueResultMeta {
938 code: "re-export-cycle",
939 meta_description: "A barrel file re-exports from another barrel that ultimately re-exports back.",
940 meta_docs_path: "explanations/dead-code#re-export-cycles",
941 meta_name: "Re-Export Cycles",
942 summary_label: "Re-export cycles",
943 docs_anchor: "re-export-cycles",
944 result_key: "re_export_cycles",
945 counts_in_total: true,
946 },
947 IssueResultMeta {
948 code: "boundary-violation",
949 meta_description: "A module imports from a zone that its configured boundary rules do not allow.",
950 meta_docs_path: "explanations/dead-code#boundary-violations",
951 meta_name: "Boundary Violations",
952 summary_label: "Boundary violations",
953 docs_anchor: "boundary-violations",
954 result_key: "boundary_violations",
955 counts_in_total: true,
956 },
957 IssueResultMeta {
958 code: "boundary-coverage",
959 meta_description: "A reachable source file is not assigned to any configured boundary zone while boundary coverage is required.",
960 meta_docs_path: "explanations/dead-code#boundary-violations",
961 meta_name: "Boundary Coverage",
962 summary_label: "Boundary coverage",
963 docs_anchor: "boundary-violations",
964 result_key: "boundary_coverage_violations",
965 counts_in_total: true,
966 },
967 IssueResultMeta {
968 code: "boundary-call-violation",
969 meta_description: "A file classified into a boundary zone calls a callee matching one of the zone's forbidden call patterns.",
970 meta_docs_path: "explanations/dead-code#boundary-violations",
971 meta_name: "Boundary Call Violation",
972 summary_label: "Boundary calls",
973 docs_anchor: "boundary-violations",
974 result_key: "boundary_call_violations",
975 counts_in_total: true,
976 },
977 IssueResultMeta {
978 code: "policy-violation",
979 meta_description: "A call site, import, or catalogue-derived effect matched a configured rule pack rule.",
980 meta_docs_path: "explanations/dead-code#policy-violations",
981 meta_name: "Policy Violation",
982 summary_label: "Policy violations",
983 docs_anchor: "policy-violations",
984 result_key: "policy_violations",
985 counts_in_total: true,
986 },
987 IssueResultMeta {
988 code: "invalid-client-export",
989 meta_description: "A file carrying the use client directive also exports a Next.js server-only or route-segment config name.",
990 meta_docs_path: "explanations/dead-code#invalid-client-exports",
991 meta_name: "Invalid client export",
992 summary_label: "Invalid client exports",
993 docs_anchor: "invalid-client-exports",
994 result_key: "invalid_client_exports",
995 counts_in_total: true,
996 },
997 IssueResultMeta {
998 code: "mixed-client-server-barrel",
999 meta_description: "A barrel file forwards a name from a use client module alongside a name from a server-only module.",
1000 meta_docs_path: "explanations/dead-code#mixed-client-server-barrels",
1001 meta_name: "Mixed client/server barrel",
1002 summary_label: "Mixed client/server barrels",
1003 docs_anchor: "mixed-client-server-barrels",
1004 result_key: "mixed_client_server_barrels",
1005 counts_in_total: true,
1006 },
1007 IssueResultMeta {
1008 code: "misplaced-directive",
1009 meta_description: "A use client or use server directive string appears after a non-directive statement and is ignored.",
1010 meta_docs_path: "explanations/dead-code#misplaced-directives",
1011 meta_name: "Misplaced directive",
1012 summary_label: "Misplaced directives",
1013 docs_anchor: "misplaced-directives",
1014 result_key: "misplaced_directives",
1015 counts_in_total: true,
1016 },
1017 IssueResultMeta {
1018 code: "unprovided-inject",
1019 meta_description: "A Vue inject or Svelte getContext reads a dependency-injection key that no matching provider supplies.",
1020 meta_docs_path: "explanations/dead-code#unprovided-injects",
1021 meta_name: "Unprovided injects",
1022 summary_label: "Unprovided injects",
1023 docs_anchor: "unprovided-inject",
1024 result_key: "unprovided_injects",
1025 counts_in_total: true,
1026 },
1027 IssueResultMeta {
1028 code: "unrendered-component",
1029 meta_description: "A Vue or Svelte single-file component is reachable through the graph but rendered nowhere in the project.",
1030 meta_docs_path: "explanations/dead-code#unrendered-components",
1031 meta_name: "Unrendered components",
1032 summary_label: "Unrendered components",
1033 docs_anchor: "unrendered-component",
1034 result_key: "unrendered_components",
1035 counts_in_total: true,
1036 },
1037 IssueResultMeta {
1038 code: "unused-component-prop",
1039 meta_description: "A declared Vue, Svelte, React, or Preact component prop is referenced nowhere inside its own component.",
1040 meta_docs_path: "explanations/dead-code#unused-component-props",
1041 meta_name: "Unused component props",
1042 summary_label: "Unused component props",
1043 docs_anchor: "unused-component-prop",
1044 result_key: "unused_component_props",
1045 counts_in_total: true,
1046 },
1047 IssueResultMeta {
1048 code: "unused-component-emit",
1049 meta_description: "A Vue script setup defineEmits event is emitted nowhere in its own component.",
1050 meta_docs_path: "explanations/dead-code#unused-component-emits",
1051 meta_name: "Unused component emits",
1052 summary_label: "Unused component emits",
1053 docs_anchor: "unused-component-emit",
1054 result_key: "unused_component_emits",
1055 counts_in_total: true,
1056 },
1057 IssueResultMeta {
1058 code: "unused-component-input",
1059 meta_description: "An Angular input is read nowhere in its own component.",
1060 meta_docs_path: "explanations/dead-code#unused-component-inputs",
1061 meta_name: "Unused component inputs",
1062 summary_label: "Unused component inputs",
1063 docs_anchor: "unused-component-input",
1064 result_key: "unused_component_inputs",
1065 counts_in_total: true,
1066 },
1067 IssueResultMeta {
1068 code: "unused-component-output",
1069 meta_description: "An Angular output is emitted nowhere in its own component.",
1070 meta_docs_path: "explanations/dead-code#unused-component-outputs",
1071 meta_name: "Unused component outputs",
1072 summary_label: "Unused component outputs",
1073 docs_anchor: "unused-component-output",
1074 result_key: "unused_component_outputs",
1075 counts_in_total: true,
1076 },
1077 IssueResultMeta {
1078 code: "unused-svelte-event",
1079 meta_description: "A Svelte component dispatches a custom event whose name is listened to nowhere in the analyzed project.",
1080 meta_docs_path: "explanations/dead-code#unused-svelte-events",
1081 meta_name: "Unused Svelte events",
1082 summary_label: "Unused Svelte events",
1083 docs_anchor: "unused-svelte-event",
1084 result_key: "unused_svelte_events",
1085 counts_in_total: true,
1086 },
1087 IssueResultMeta {
1088 code: "unused-server-action",
1089 meta_description: "A Next.js Server Action exported from a use server file is referenced by no code in the project.",
1090 meta_docs_path: "explanations/dead-code#unused-server-actions",
1091 meta_name: "Unused server actions",
1092 summary_label: "Unused server actions",
1093 docs_anchor: "unused-server-action",
1094 result_key: "unused_server_actions",
1095 counts_in_total: true,
1096 },
1097 IssueResultMeta {
1098 code: "unused-load-data-key",
1099 meta_description: "A SvelteKit load return-object key is read by no route or project-wide consumer.",
1100 meta_docs_path: "explanations/dead-code#unused-load-data-keys",
1101 meta_name: "Unused load data keys",
1102 summary_label: "Unused load data keys",
1103 docs_anchor: "unused-load-data-key",
1104 result_key: "unused_load_data_keys",
1105 counts_in_total: true,
1106 },
1107 IssueResultMeta {
1108 code: "route-collision",
1109 meta_description: "Two or more Next.js App Router route files resolve to the same URL within one app root.",
1110 meta_docs_path: "explanations/dead-code#route-collisions",
1111 meta_name: "Route collision",
1112 summary_label: "Route collisions",
1113 docs_anchor: "route-collisions",
1114 result_key: "route_collisions",
1115 counts_in_total: true,
1116 },
1117 IssueResultMeta {
1118 code: "dynamic-segment-name-conflict",
1119 meta_description: "Sibling Next.js dynamic route segments use different slug names at the same position.",
1120 meta_docs_path: "explanations/dead-code#dynamic-segment-name-conflicts",
1121 meta_name: "Dynamic segment name conflict",
1122 summary_label: "Dynamic segment conflicts",
1123 docs_anchor: "dynamic-segment-name-conflicts",
1124 result_key: "dynamic_segment_name_conflicts",
1125 counts_in_total: true,
1126 },
1127 IssueResultMeta {
1128 code: "stale-suppression",
1129 meta_description: "A fallow suppression comment or tag no longer matches any active issue.",
1130 meta_docs_path: "explanations/dead-code#stale-suppressions",
1131 meta_name: "Stale Suppressions",
1132 summary_label: "Stale suppressions",
1133 docs_anchor: "stale-suppressions",
1134 result_key: "stale_suppressions",
1135 counts_in_total: true,
1136 },
1137 IssueResultMeta {
1138 code: "unused-catalog-entry",
1139 meta_description: "A package manager catalog entry is not referenced by any workspace package.json.",
1140 meta_docs_path: "explanations/dead-code#unused-catalog-entries",
1141 meta_name: "Unused catalog entry",
1142 summary_label: "Unused catalog entries",
1143 docs_anchor: "unused-catalog-entries",
1144 result_key: "unused_catalog_entries",
1145 counts_in_total: true,
1146 },
1147 IssueResultMeta {
1148 code: "empty-catalog-group",
1149 meta_description: "A named package manager catalog group has no package entries.",
1150 meta_docs_path: "explanations/dead-code#empty-catalog-groups",
1151 meta_name: "Empty catalog group",
1152 summary_label: "Empty catalog groups",
1153 docs_anchor: "empty-catalog-groups",
1154 result_key: "empty_catalog_groups",
1155 counts_in_total: true,
1156 },
1157 IssueResultMeta {
1158 code: "unresolved-catalog-reference",
1159 meta_description: "A workspace package.json uses a catalog protocol reference that no catalog declares.",
1160 meta_docs_path: "explanations/dead-code#unresolved-catalog-references",
1161 meta_name: "Unresolved catalog reference",
1162 summary_label: "Unresolved catalog references",
1163 docs_anchor: "unresolved-catalog-references",
1164 result_key: "unresolved_catalog_references",
1165 counts_in_total: true,
1166 },
1167 IssueResultMeta {
1168 code: "unused-dependency-override",
1169 meta_description: "A pnpm dependency override targets a package not declared by any workspace package and not present in the lockfile.",
1170 meta_docs_path: "explanations/dead-code#unused-dependency-overrides",
1171 meta_name: "Unused pnpm dependency override",
1172 summary_label: "Unused dependency overrides",
1173 docs_anchor: "unused-dependency-overrides",
1174 result_key: "unused_dependency_overrides",
1175 counts_in_total: true,
1176 },
1177 IssueResultMeta {
1178 code: "misconfigured-dependency-override",
1179 meta_description: "A pnpm dependency override key or value does not parse as a valid override spec.",
1180 meta_docs_path: "explanations/dead-code#misconfigured-dependency-overrides",
1181 meta_name: "Misconfigured pnpm dependency override",
1182 summary_label: "Misconfigured dependency overrides",
1183 docs_anchor: "misconfigured-dependency-overrides",
1184 result_key: "misconfigured_dependency_overrides",
1185 counts_in_total: true,
1186 },
1187 IssueResultMeta {
1188 code: "prop-drilling",
1189 meta_description: "A React or Preact prop is forwarded unchanged through multiple pass-through components to a distant consumer.",
1190 meta_docs_path: "explanations/dead-code#prop-drilling",
1191 meta_name: "Prop drilling",
1192 summary_label: "Prop drilling",
1193 docs_anchor: "prop-drilling",
1194 result_key: "prop_drilling_chains",
1195 counts_in_total: false,
1196 },
1197 IssueResultMeta {
1198 code: "thin-wrapper",
1199 meta_description: "A React or Preact component is structural indirection around a single spread-forwarded child render.",
1200 meta_docs_path: "explanations/dead-code#thin-wrapper",
1201 meta_name: "Thin wrapper",
1202 summary_label: "Thin wrappers",
1203 docs_anchor: "thin-wrapper",
1204 result_key: "thin_wrappers",
1205 counts_in_total: false,
1206 },
1207 IssueResultMeta {
1208 code: "duplicate-prop-shape",
1209 meta_description: "Multiple React or Preact components declare an identical significant prop-name set.",
1210 meta_docs_path: "explanations/dead-code#duplicate-prop-shape",
1211 meta_name: "Duplicate prop shape",
1212 summary_label: "Duplicate prop shapes",
1213 docs_anchor: "duplicate-prop-shape",
1214 result_key: "duplicate_prop_shapes",
1215 counts_in_total: false,
1216 },
1217];
1218
1219pub const KNOWN_ISSUE_KIND_NAMES: &[&str] = &[
1221 "unused-file",
1222 "unused-export",
1223 "unused-type",
1224 "private-type-leak",
1225 "unused-dependency",
1226 "unused-dev-dependency",
1227 "unused-enum-member",
1228 "unused-class-member",
1229 "unresolved-import",
1230 "unlisted-dependency",
1231 "duplicate-export",
1232 "code-duplication",
1233 "circular-dependency",
1234 "circular-dependencies",
1235 "re-export-cycle",
1236 "re-export-cycles",
1237 "reexport-cycle",
1238 "reexport-cycles",
1239 "type-only-dependency",
1240 "test-only-dependency",
1241 "boundary-violation",
1242 "boundary-call-violation",
1243 "boundary-call-violations",
1244 "coverage-gaps",
1245 "feature-flag",
1246 "complexity",
1247 "stale-suppression",
1248 "unused-catalog-entry",
1249 "unused-catalog-entries",
1250 "empty-catalog-group",
1251 "empty-catalog-groups",
1252 "unresolved-catalog-reference",
1253 "unresolved-catalog-references",
1254 "unused-dependency-override",
1255 "unused-dependency-overrides",
1256 "misconfigured-dependency-override",
1257 "misconfigured-dependency-overrides",
1258 "security-client-server-leak",
1259 "security-sink",
1260 "policy-violation",
1261 "policy-violations",
1262 "invalid-client-export",
1263 "invalid-client-exports",
1264 "mixed-client-server-barrel",
1265 "mixed-client-server-barrels",
1266 "misplaced-directive",
1267 "misplaced-directives",
1268 "unused-store-member",
1269 "unused-store-members",
1270 "unprovided-inject",
1271 "unprovided-injects",
1272 "route-collision",
1273 "route-collisions",
1274 "dynamic-segment-name-conflict",
1275 "dynamic-segment-name-conflicts",
1276 "unrendered-component",
1277 "unrendered-components",
1278 "unused-component-prop",
1279 "unused-component-props",
1280 "unused-component-emit",
1281 "unused-component-emits",
1282 "unused-component-input",
1283 "unused-component-inputs",
1284 "unused-component-output",
1285 "unused-component-outputs",
1286 "unused-server-action",
1287 "unused-server-actions",
1288 "unused-load-data-key",
1289 "unused-load-data-keys",
1290 "prop-drilling",
1291 "thin-wrapper",
1292 "thin-wrappers",
1293 "duplicate-prop-shape",
1294 "duplicate-prop-shapes",
1295 "unused-svelte-event",
1296 "unused-svelte-events",
1297];
1298
1299pub const DEAD_CODE_FILTER_FLAGS: &[&str] = &[
1301 "--unused-files",
1302 "--unused-exports",
1303 "--unused-types",
1304 "--private-type-leaks",
1305 "--unused-deps",
1306 "--unused-enum-members",
1307 "--unused-class-members",
1308 "--unused-store-members",
1309 "--unprovided-injects",
1310 "--unrendered-components",
1311 "--unused-component-props",
1312 "--unused-component-emits",
1313 "--unused-component-inputs",
1314 "--unused-component-outputs",
1315 "--unused-svelte-events",
1316 "--unused-server-actions",
1317 "--unused-load-data-keys",
1318 "--unresolved-imports",
1319 "--unlisted-deps",
1320 "--duplicate-exports",
1321 "--circular-deps",
1322 "--re-export-cycles",
1323 "--boundary-violations",
1324 "--policy-violations",
1325 "--stale-suppressions",
1326 "--unused-catalog-entries",
1327 "--empty-catalog-groups",
1328 "--unresolved-catalog-references",
1329 "--unused-dependency-overrides",
1330 "--misconfigured-dependency-overrides",
1331];
1332
1333pub const MCP_ISSUE_TYPE_FLAGS: &[(&str, &str)] = &[
1335 ("unused-files", "--unused-files"),
1336 ("unused-exports", "--unused-exports"),
1337 ("unused-types", "--unused-types"),
1338 ("private-type-leaks", "--private-type-leaks"),
1339 ("unused-deps", "--unused-deps"),
1340 ("unused-enum-members", "--unused-enum-members"),
1341 ("unused-class-members", "--unused-class-members"),
1342 ("unused-store-members", "--unused-store-members"),
1343 ("unprovided-injects", "--unprovided-injects"),
1344 ("unrendered-components", "--unrendered-components"),
1345 ("unused-component-props", "--unused-component-props"),
1346 ("unused-component-emits", "--unused-component-emits"),
1347 ("unused-component-inputs", "--unused-component-inputs"),
1348 ("unused-component-outputs", "--unused-component-outputs"),
1349 ("unused-svelte-events", "--unused-svelte-events"),
1350 ("unused-server-actions", "--unused-server-actions"),
1351 ("unused-load-data-keys", "--unused-load-data-keys"),
1352 ("unresolved-imports", "--unresolved-imports"),
1353 ("unlisted-deps", "--unlisted-deps"),
1354 ("duplicate-exports", "--duplicate-exports"),
1355 ("circular-deps", "--circular-deps"),
1356 ("re-export-cycles", "--re-export-cycles"),
1357 ("boundary-violations", "--boundary-violations"),
1358 ("policy-violations", "--policy-violations"),
1359 ("stale-suppressions", "--stale-suppressions"),
1360 ("unused-catalog-entries", "--unused-catalog-entries"),
1361 ("empty-catalog-groups", "--empty-catalog-groups"),
1362 (
1363 "unresolved-catalog-references",
1364 "--unresolved-catalog-references",
1365 ),
1366 (
1367 "unused-dependency-overrides",
1368 "--unused-dependency-overrides",
1369 ),
1370 (
1371 "misconfigured-dependency-overrides",
1372 "--misconfigured-dependency-overrides",
1373 ),
1374];
1375
1376pub const CODECLIMATE_RESULT_CODES: &[&str] = &[
1378 "unused-file",
1379 "unused-export",
1380 "unused-type",
1381 "private-type-leak",
1382 "unused-dependency",
1383 "unused-dev-dependency",
1384 "unused-optional-dependency",
1385 "unused-enum-member",
1386 "unused-class-member",
1387 "unused-store-member",
1388 "unresolved-import",
1389 "unlisted-dependency",
1390 "duplicate-export",
1391 "type-only-dependency",
1392 "test-only-dependency",
1393 "circular-dependency",
1394 "re-export-cycle",
1395 "boundary-violation",
1396 "boundary-coverage",
1397 "boundary-call-violation",
1398 "policy-violation",
1399 "invalid-client-export",
1400 "mixed-client-server-barrel",
1401 "misplaced-directive",
1402 "unprovided-inject",
1403 "unrendered-component",
1404 "unused-component-prop",
1405 "unused-component-emit",
1406 "unused-component-input",
1407 "unused-component-output",
1408 "unused-svelte-event",
1409 "unused-server-action",
1410 "unused-load-data-key",
1411 "route-collision",
1412 "dynamic-segment-name-conflict",
1413 "stale-suppression",
1414 "unused-catalog-entry",
1415 "empty-catalog-group",
1416 "unresolved-catalog-reference",
1417 "unused-dependency-override",
1418 "misconfigured-dependency-override",
1419];
1420
1421#[must_use]
1423pub fn issue_meta_by_code(code: &str) -> Option<&'static IssueKindMeta> {
1424 ISSUE_KIND_META.iter().find(|meta| meta.code == code)
1425}
1426
1427#[must_use]
1429pub fn issue_meta_for_token(token: &str) -> Option<&'static IssueKindMeta> {
1430 ISSUE_KIND_META
1431 .iter()
1432 .find(|meta| meta.code == token || meta.aliases.contains(&token))
1433}
1434
1435#[must_use]
1437pub fn issue_meta_by_kind(kind: IssueKind) -> Option<&'static IssueKindMeta> {
1438 ISSUE_KIND_META.iter().find(|meta| meta.kind == Some(kind))
1439}
1440
1441#[must_use]
1443pub fn issue_result_meta_by_code(code: &str) -> Option<&'static IssueResultMeta> {
1444 ISSUE_RESULT_META.iter().find(|meta| meta.code == code)
1445}
1446
1447#[must_use]
1449pub fn issue_sarif_rule_ids(code: &str) -> Vec<String> {
1450 let mut ids = vec![format!("fallow/{code}")];
1451 if code == "stale-suppression" {
1452 ids.push("fallow/missing-suppression-reason".to_string());
1453 }
1454 ids
1455}
1456
1457#[must_use]
1459pub fn issue_codeclimate_check_names(code: &str) -> Vec<String> {
1460 if !CODECLIMATE_RESULT_CODES.contains(&code) {
1461 return Vec::new();
1462 }
1463 issue_sarif_rule_ids(code)
1464}
1465
1466#[must_use]
1469pub fn issue_docs_anchor(code: &str) -> Option<&'static str> {
1470 issue_result_meta_by_code(code).map(|meta| meta.docs_anchor)
1471}
1472
1473#[must_use]
1475pub fn issue_ts_alias(code: &str) -> Option<TsAliasMeta> {
1476 let alias = match code {
1477 "unused-file" => TsAliasMeta {
1478 name: "UnusedFile",
1479 parent: "UnusedFileFinding",
1480 },
1481 "unused-export" => TsAliasMeta {
1482 name: "UnusedExport",
1483 parent: "UnusedExportFinding",
1484 },
1485 "private-type-leak" => TsAliasMeta {
1486 name: "PrivateTypeLeak",
1487 parent: "PrivateTypeLeakFinding",
1488 },
1489 "unused-dependency" => TsAliasMeta {
1490 name: "UnusedDependency",
1491 parent: "UnusedDependencyFinding",
1492 },
1493 "unused-dev-dependency" => TsAliasMeta {
1494 name: "UnusedDependency",
1495 parent: "UnusedDevDependencyFinding",
1496 },
1497 "unused-optional-dependency" => TsAliasMeta {
1498 name: "UnusedDependency",
1499 parent: "UnusedOptionalDependencyFinding",
1500 },
1501 "unused-enum-member" => TsAliasMeta {
1502 name: "UnusedMember",
1503 parent: "UnusedEnumMemberFinding",
1504 },
1505 "unused-class-member" => TsAliasMeta {
1506 name: "UnusedMember",
1507 parent: "UnusedClassMemberFinding",
1508 },
1509 "unused-store-member" => TsAliasMeta {
1510 name: "UnusedMember",
1511 parent: "UnusedStoreMemberFinding",
1512 },
1513 "unresolved-import" => TsAliasMeta {
1514 name: "UnresolvedImport",
1515 parent: "UnresolvedImportFinding",
1516 },
1517 "unlisted-dependency" => TsAliasMeta {
1518 name: "UnlistedDependency",
1519 parent: "UnlistedDependencyFinding",
1520 },
1521 "duplicate-export" => TsAliasMeta {
1522 name: "DuplicateExport",
1523 parent: "DuplicateExportFinding",
1524 },
1525 "type-only-dependency" => TsAliasMeta {
1526 name: "TypeOnlyDependency",
1527 parent: "TypeOnlyDependencyFinding",
1528 },
1529 "test-only-dependency" => TsAliasMeta {
1530 name: "TestOnlyDependency",
1531 parent: "TestOnlyDependencyFinding",
1532 },
1533 "circular-dependency" => TsAliasMeta {
1534 name: "CircularDependency",
1535 parent: "CircularDependencyFinding",
1536 },
1537 "re-export-cycle" => TsAliasMeta {
1538 name: "ReExportCycle",
1539 parent: "ReExportCycleFinding",
1540 },
1541 "boundary-violation" => TsAliasMeta {
1542 name: "BoundaryViolation",
1543 parent: "BoundaryViolationFinding",
1544 },
1545 "unused-catalog-entry" => TsAliasMeta {
1546 name: "UnusedCatalogEntry",
1547 parent: "UnusedCatalogEntryFinding",
1548 },
1549 "empty-catalog-group" => TsAliasMeta {
1550 name: "EmptyCatalogGroup",
1551 parent: "EmptyCatalogGroupFinding",
1552 },
1553 "unresolved-catalog-reference" => TsAliasMeta {
1554 name: "UnresolvedCatalogReference",
1555 parent: "UnresolvedCatalogReferenceFinding",
1556 },
1557 "unused-dependency-override" => TsAliasMeta {
1558 name: "UnusedDependencyOverride",
1559 parent: "UnusedDependencyOverrideFinding",
1560 },
1561 "misconfigured-dependency-override" => TsAliasMeta {
1562 name: "MisconfiguredDependencyOverride",
1563 parent: "MisconfiguredDependencyOverrideFinding",
1564 },
1565 _ => return None,
1566 };
1567 Some(alias)
1568}
1569
1570pub fn diagnostic_issue_metas() -> impl Iterator<Item = &'static IssueKindMeta> {
1572 ISSUE_KIND_META.iter().filter(|meta| meta.lsp)
1573}
1574
1575pub fn result_issue_metas() -> impl Iterator<Item = &'static IssueResultMeta> {
1577 ISSUE_RESULT_META.iter()
1578}
1579
1580pub fn counted_result_issue_metas() -> impl Iterator<Item = &'static IssueResultMeta> {
1582 result_issue_metas().filter(|meta| meta.counts_in_total)
1583}
1584
1585#[cfg(test)]
1586mod tests {
1587 use std::collections::BTreeSet;
1588
1589 use crate::results::TOTAL_ISSUE_RESULT_KEYS;
1590
1591 use super::*;
1592
1593 #[test]
1594 fn known_names_round_trip_through_metadata() {
1595 for name in KNOWN_ISSUE_KIND_NAMES {
1596 let meta = issue_meta_for_token(name)
1597 .unwrap_or_else(|| panic!("known issue name {name} missing metadata row"));
1598 assert!(
1599 meta.kind.is_some(),
1600 "known issue name {name} maps to non-IssueKind metadata"
1601 );
1602 }
1603 }
1604
1605 #[test]
1606 fn issue_kind_variants_have_metadata() {
1607 for &kind in IssueKind::ALL {
1608 assert!(
1609 issue_meta_by_kind(kind).is_some(),
1610 "IssueKind {kind:?} has no metadata row"
1611 );
1612 }
1613 }
1614
1615 #[test]
1616 fn dead_code_filter_flags_match_metadata() {
1617 let from_constants: BTreeSet<&str> = DEAD_CODE_FILTER_FLAGS.iter().copied().collect();
1618 let from_meta: BTreeSet<&str> = ISSUE_KIND_META
1619 .iter()
1620 .filter_map(|meta| meta.filter_flag)
1621 .collect();
1622 assert_eq!(from_constants, from_meta);
1623 }
1624
1625 #[test]
1626 fn mcp_issue_type_flags_match_metadata() {
1627 let from_constants: BTreeSet<(&str, &str)> = MCP_ISSUE_TYPE_FLAGS.iter().copied().collect();
1628 let from_meta: BTreeSet<(&str, &str)> = ISSUE_KIND_META
1629 .iter()
1630 .filter_map(|meta| meta.mcp_pair())
1631 .collect();
1632 assert_eq!(from_constants, from_meta);
1633 }
1634
1635 #[test]
1636 fn lsp_exposes_only_actual_diagnostic_codes() {
1637 let codes: BTreeSet<&str> = diagnostic_issue_metas().map(|meta| meta.code).collect();
1638 assert!(codes.contains("boundary-violation"));
1639 assert!(!codes.contains("boundary-coverage"));
1640 assert!(!codes.contains("boundary-call-violation"));
1641 }
1642
1643 #[test]
1644 fn issue_codes_are_unique() {
1645 let mut seen = BTreeSet::new();
1646 for meta in ISSUE_KIND_META {
1647 assert!(seen.insert(meta.code), "duplicate issue code {}", meta.code);
1648 }
1649 }
1650
1651 #[test]
1652 fn result_meta_codes_have_issue_metadata() {
1653 for meta in ISSUE_RESULT_META {
1654 assert!(
1655 issue_meta_by_code(meta.code).is_some(),
1656 "result metadata code {} has no issue metadata row",
1657 meta.code
1658 );
1659 }
1660 }
1661
1662 #[test]
1663 fn result_meta_codes_have_docs_anchors() {
1664 for meta in ISSUE_RESULT_META {
1665 let issue = issue_meta_by_code(meta.code)
1666 .unwrap_or_else(|| panic!("result metadata code {} has no issue row", meta.code));
1667 assert_eq!(
1668 issue.docs_anchor(),
1669 Some(meta.docs_anchor),
1670 "result metadata code {} has mismatched docs anchor",
1671 meta.code
1672 );
1673 }
1674 }
1675
1676 #[test]
1677 fn result_meta_codes_have_summary_labels() {
1678 for meta in ISSUE_RESULT_META {
1679 assert!(
1680 !meta.summary_label.is_empty(),
1681 "result metadata code {} has no summary label",
1682 meta.code
1683 );
1684 }
1685 }
1686
1687 #[test]
1688 fn result_meta_codes_have_meta_names() {
1689 for meta in ISSUE_RESULT_META {
1690 assert!(
1691 !meta.meta_name.is_empty(),
1692 "result metadata code {} has no meta name",
1693 meta.code
1694 );
1695 }
1696 }
1697
1698 #[test]
1699 fn result_meta_codes_have_meta_docs_paths() {
1700 for meta in ISSUE_RESULT_META {
1701 assert!(
1702 meta.meta_docs_path.starts_with("explanations/dead-code#"),
1703 "result metadata code {} has invalid meta docs path",
1704 meta.code
1705 );
1706 }
1707 }
1708
1709 #[test]
1710 fn result_meta_codes_have_meta_descriptions() {
1711 for meta in ISSUE_RESULT_META {
1712 assert!(
1713 !meta.meta_description.is_empty(),
1714 "result metadata code {} has no meta description",
1715 meta.code
1716 );
1717 }
1718 }
1719
1720 #[test]
1721 fn ci_format_ids_are_prefixed_and_known() {
1722 let result_codes: BTreeSet<&str> = result_issue_metas().map(|meta| meta.code).collect();
1723 let codeclimate_codes: BTreeSet<&str> = CODECLIMATE_RESULT_CODES.iter().copied().collect();
1724 assert!(codeclimate_codes.is_subset(&result_codes));
1725
1726 for meta in result_issue_metas() {
1727 let issue = issue_meta_by_code(meta.code)
1728 .unwrap_or_else(|| panic!("result metadata code {} has no issue row", meta.code));
1729 assert!(issue.sarif_enabled());
1730 let sarif_ids = issue.sarif_rule_ids();
1731 assert!(sarif_ids.contains(&format!("fallow/{}", meta.code)));
1732 for rule_id in sarif_ids {
1733 assert!(
1734 rule_id.starts_with("fallow/"),
1735 "result metadata code {} has unprefixed SARIF rule id {rule_id}",
1736 meta.code
1737 );
1738 }
1739 for check_name in issue.codeclimate_check_names() {
1740 assert!(
1741 check_name.starts_with("fallow/"),
1742 "result metadata code {} has unprefixed CodeClimate check name {check_name}",
1743 meta.code
1744 );
1745 }
1746 }
1747 }
1748
1749 #[test]
1750 fn ts_alias_policy_is_explicit() {
1751 let aliases: BTreeSet<(&str, &str)> = result_issue_metas()
1752 .filter_map(|meta| {
1753 issue_meta_by_code(meta.code)
1754 .and_then(|issue| issue.ts_alias())
1755 .map(|alias| (alias.name, alias.parent))
1756 })
1757 .collect();
1758
1759 assert_eq!(
1760 BTreeSet::from([
1761 ("BoundaryViolation", "BoundaryViolationFinding"),
1762 ("CircularDependency", "CircularDependencyFinding"),
1763 ("DuplicateExport", "DuplicateExportFinding"),
1764 ("EmptyCatalogGroup", "EmptyCatalogGroupFinding"),
1765 (
1766 "MisconfiguredDependencyOverride",
1767 "MisconfiguredDependencyOverrideFinding",
1768 ),
1769 ("PrivateTypeLeak", "PrivateTypeLeakFinding"),
1770 ("ReExportCycle", "ReExportCycleFinding"),
1771 ("TestOnlyDependency", "TestOnlyDependencyFinding"),
1772 ("TypeOnlyDependency", "TypeOnlyDependencyFinding"),
1773 (
1774 "UnresolvedCatalogReference",
1775 "UnresolvedCatalogReferenceFinding",
1776 ),
1777 ("UnresolvedImport", "UnresolvedImportFinding"),
1778 ("UnlistedDependency", "UnlistedDependencyFinding"),
1779 ("UnusedCatalogEntry", "UnusedCatalogEntryFinding"),
1780 ("UnusedDependency", "UnusedDependencyFinding"),
1781 ("UnusedDependency", "UnusedDevDependencyFinding"),
1782 ("UnusedDependency", "UnusedOptionalDependencyFinding"),
1783 (
1784 "UnusedDependencyOverride",
1785 "UnusedDependencyOverrideFinding",
1786 ),
1787 ("UnusedExport", "UnusedExportFinding"),
1788 ("UnusedFile", "UnusedFileFinding"),
1789 ("UnusedMember", "UnusedClassMemberFinding"),
1790 ("UnusedMember", "UnusedEnumMemberFinding"),
1791 ("UnusedMember", "UnusedStoreMemberFinding"),
1792 ]),
1793 aliases
1794 );
1795 }
1796
1797 #[test]
1798 fn result_keys_are_unique() {
1799 let mut seen = BTreeSet::new();
1800 for meta in ISSUE_RESULT_META {
1801 assert!(
1802 seen.insert(meta.result_key),
1803 "duplicate result key {}",
1804 meta.result_key
1805 );
1806 }
1807 }
1808
1809 #[test]
1810 fn counted_result_keys_match_total_issue_fields() {
1811 let from_total: BTreeSet<&str> = TOTAL_ISSUE_RESULT_KEYS.iter().copied().collect();
1812 let from_meta: BTreeSet<&str> = counted_result_issue_metas()
1813 .map(|meta| meta.result_key)
1814 .collect();
1815 assert_eq!(from_total, from_meta);
1816 }
1817
1818 #[test]
1819 fn advisory_result_keys_are_explicitly_excluded_from_total() {
1820 let expected = BTreeSet::from([
1821 "duplicate_prop_shapes",
1822 "prop_drilling_chains",
1823 "thin_wrappers",
1824 ]);
1825 let from_meta: BTreeSet<&str> = result_issue_metas()
1826 .filter(|meta| !meta.counts_in_total)
1827 .map(|meta| meta.result_key)
1828 .collect();
1829 assert_eq!(expected, from_meta);
1830 }
1831}