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