1use std::collections::HashSet;
2use std::fmt;
3use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6
7mod psalm_levels;
8pub use psalm_levels::psalm_error_level;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
15pub enum Severity {
16 Info,
18 Warning,
20 Error,
22}
23
24impl fmt::Display for Severity {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Severity::Info => write!(f, "info"),
28 Severity::Warning => write!(f, "warning"),
29 Severity::Error => write!(f, "error"),
30 }
31 }
32}
33
34pub use mir_types::Location;
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45#[non_exhaustive]
46pub enum IssueKind {
47 NonStaticSelfCall { class: String, method: String },
53 InvalidScope {
54 in_class: bool,
56 },
57 UndefinedVariable { name: String },
60 UndefinedFunction { name: String },
63 UndefinedMethod { class: String, method: String },
66 UndefinedClass { name: String },
69 UndefinedProperty { class: String, property: String },
72 InaccessibleProperty { class: String, property: String },
77 UndefinedConstant { name: String },
80 InaccessibleClassConstant { class: String, constant: String },
83 PossiblyUndefinedVariable { name: String },
86 UndefinedTrait { name: String },
89 TraitConstantAccessedDirectly {
96 trait_name: String,
97 constant: String,
98 },
99 UndefinedTraitAliasMethod {
107 trait_name: Option<String>,
108 method: String,
109 },
110 ParentNotFound,
113 InvalidStringClass { actual: String },
116 NotAnInterface { name: String },
120
121 NullArgument { param: String, fn_name: String },
125 NullPropertyFetch { property: String },
128 NullMethodCall { method: String },
131 NullArrayAccess,
134 PossiblyNullArgument { param: String, fn_name: String },
137 PossiblyInvalidArgument {
140 param: String,
141 fn_name: String,
142 expected: String,
143 actual: String,
144 },
145 PossiblyNullPropertyFetch { property: String },
148 PossiblyNullMethodCall { method: String },
151 PossiblyNullArrayAccess,
154 NullableReturnStatement { expected: String, actual: String },
157
158 InvalidReturnType { expected: String, actual: String },
162 InvalidArgument {
165 param: String,
166 fn_name: String,
167 expected: String,
168 actual: String,
169 },
170 TooFewArguments {
173 fn_name: String,
174 expected: usize,
175 actual: usize,
176 },
177 TooManyArguments {
180 fn_name: String,
181 expected: usize,
182 actual: usize,
183 },
184 InvalidNamedArgument {
187 fn_name: String,
188 name: String,
189 overwrites: bool,
190 },
191 InvalidNamedArguments { fn_name: String },
194 InvalidPassByReference { fn_name: String, param: String },
197 InvalidPropertyFetch { ty: String },
200 InvalidArrayAccess { ty: String },
203 PossiblyInvalidArrayAccess { ty: String },
206 InvalidArrayAssignment { ty: String },
209 InvalidPropertyAssignment {
212 property: String,
213 expected: String,
214 actual: String,
215 },
216 InvalidCast { from: String, to: String },
219 InvalidStaticInvocation { class: String, method: String },
222 InvalidOperand {
226 op: String,
227 left: String,
228 right: String,
229 },
230 PossiblyInvalidOperand {
233 op: String,
234 left: String,
235 right: String,
236 },
237 PossiblyNullOperand { op: String, ty: String },
240 DivisionByZero { op: String },
244 RawObjectIteration { ty: String },
247 PossiblyRawObjectIteration { ty: String },
250 MismatchingDocblockReturnType { declared: String, inferred: String },
253 MismatchingDocblockParamType {
256 param: String,
257 declared: String,
258 inferred: String,
259 },
260 TypeCheckMismatch {
266 var: String,
267 expected: String,
268 actual: String,
269 },
270
271 Trace { variable: String, type_info: String },
274
275 InvalidArrayOffset { expected: String, actual: String },
279 NonExistentArrayOffset { key: String },
283 PossiblyInvalidArrayOffset { expected: String, actual: String },
286 DuplicateArrayKey { key: String },
291
292 RedundantCondition {
296 always_true: bool,
297 unreachable: Option<String>,
299 },
300 RedundantCast { from: String, to: String },
303 UnnecessaryVarAnnotation { var: String },
306 TypeDoesNotContainType { left: String, right: String },
309 ParadoxicalCondition { value: String },
312 DocblockTypeContradiction { expr: String, declared: String },
317 ImpossibleIdenticalComparison {
323 op: String,
324 left: String,
325 right: String,
326 },
327 ImpossibleLooseComparison {
334 op: String,
335 left: String,
336 right: String,
337 },
338 UnevaluatedCode { reason: String },
345
346 UnusedVariable { name: String },
350 UnusedParam { name: String },
353 UnreachableCode,
356 UnhandledMatchCondition { detail: String },
359 UnusedMethod { class: String, method: String },
362 UnusedProperty { class: String, property: String },
365 UnusedFunction { name: String },
368 UnusedForeachValue { name: String },
371 UnusedClass { class: String },
374 UnusedSuppress { kind: String },
378
379 ArgumentTypeCoercion {
382 param: String,
383 fn_name: String,
384 expected: String,
385 actual: String,
386 },
387
388 PropertyTypeCoercion {
391 property: String,
392 expected: String,
393 actual: String,
394 },
395
396 ImpurePropertyAssignment { property: String },
399 ImpureMethodCall { method: String },
401 ImpureGlobalVariable { variable: String },
403 ImpureStaticVariable { variable: String },
405 ImpureStaticPropertyAssignment { class: String, property: String },
409 ImpureStaticPropertyAccess { class: String, property: String },
414 ImpureByRefAssignment { variable: String },
419 ImpureFunctionCall { fn_name: String },
423 ImmutablePropertyModification { receiver: String, property: String },
429
430 ReadonlyPropertyAssignment { class: String, property: String },
434 ReadonlyPropertyAlreadyInitialized { class: String, property: String },
441
442 UnimplementedAbstractMethod { class: String, method: String },
446 UnimplementedInterfaceMethod {
449 class: String,
450 interface: String,
451 method: String,
452 },
453 MethodSignatureMismatch {
456 class: String,
457 method: String,
458 detail: String,
459 },
460 OverriddenMethodAccess { class: String, method: String },
463 OverriddenPropertyAccess { class: String, property: String },
466 PropertyTypeRedeclarationMismatch {
469 class: String,
470 property: String,
471 expected: String,
472 actual: String,
473 },
474 ReadonlyPropertyRedeclarationMismatch {
477 parent_class: String,
478 class: String,
479 property: String,
480 parent_readonly: bool,
483 },
484 StaticPropertyRedeclarationMismatch {
487 parent_class: String,
488 class: String,
489 property: String,
490 parent_static: bool,
493 },
494 ReadonlyClassExtendsMismatch {
500 parent: String,
501 child: String,
502 parent_readonly: bool,
505 },
506 InvalidReadonlyPropertyDeclaration { class: String, property: String },
512 BackedEnumCaseTypeMismatch {
515 enum_name: String,
516 case_name: String,
517 expected: String,
518 actual: String,
519 },
520 DirectConstructorCall { class: String },
523 InvalidExtendClass { parent: String, child: String },
526 FinalMethodOverridden {
529 class: String,
530 method: String,
531 parent: String,
532 },
533 AbstractInstantiation { class: String },
536 AbstractMethodCall { class: String, method: String },
539 InterfaceInstantiation { class: String },
542 InvalidOverride {
546 class: String,
547 method: String,
548 detail: String,
549 },
550
551 TaintedInput { sink: String },
555 TaintedHtml,
558 TaintedSql,
561 TaintedShell,
564 TaintedLlmPrompt,
568 TaintedHeader,
571 TaintedCookie,
574
575 InvalidTemplateParam {
579 name: String,
580 expected_bound: String,
581 actual: String,
582 },
583 ShadowedTemplateParam { name: String },
586 IfThisIsMismatch {
591 class: String,
592 method: String,
593 expected: String,
594 actual: String,
595 },
596
597 DeprecatedCall {
601 name: String,
602 message: Option<Arc<str>>,
603 },
604 DeprecatedProperty {
607 class: String,
608 property: String,
609 message: Option<Arc<str>>,
610 },
611 DeprecatedConstant {
614 class: String,
615 constant: String,
616 message: Option<Arc<str>>,
617 },
618 DeprecatedInterface {
621 name: String,
622 message: Option<Arc<str>>,
623 },
624 DeprecatedTrait {
627 name: String,
628 message: Option<Arc<str>>,
629 },
630 DeprecatedMethodCall {
633 class: String,
634 method: String,
635 message: Option<Arc<str>>,
636 },
637 DeprecatedMethod {
640 class: String,
641 method: String,
642 message: Option<Arc<str>>,
643 },
644 DeprecatedClass {
647 name: String,
648 message: Option<Arc<str>>,
649 },
650 InternalMethod { class: String, method: String },
653 MissingReturnType { fn_name: String },
656 MissingClosureReturnType,
659 MissingParamType { fn_name: String, param: String },
662 MissingPropertyType { class: String, property: String },
665 InvalidThrow { ty: String },
668 InvalidCatch { ty: String },
671 UnreachableCatch { ty: String, shadowed_by: String },
676 MissingThrowsDocblock { class: String },
679 UndefinedThrowsDocblock { name: String },
684 ImplicitToStringCast { class: String },
687 ImplicitFloatToIntCast { from: String },
690 ParseError { message: String },
693 InvalidDocblock { message: String },
696 InvalidDocblockType { message: String },
703 MixedArgument { param: String, fn_name: String },
706 MixedAssignment { var: String },
709 MixedMethodCall { method: String },
712 UnsupportedReferenceUsage,
715 NoInterfaceProperties { property: String },
719 UndefinedDocblockClass { name: String },
723 MissingConstructor { class: String },
726 PropertyPossiblyUninitialized { class: String, property: String },
738 MixedFunctionCall,
741 MixedReturnStatement { declared: String },
744 MixedPropertyFetch { property: String },
747 MixedPropertyAssignment { property: String },
750 MixedArrayAccess,
753 MixedArrayOffset,
756 MixedClone,
759 InvalidClone { ty: String },
763 PossiblyInvalidClone { ty: String },
767 InvalidToString { class: String },
771 CircularInheritance { class: String },
774
775 InvalidTraitUse { trait_name: String, reason: String },
779 ForbiddenCode { message: String },
782
783 InvalidAttribute { message: String },
787 UndefinedAttributeClass { name: String },
790
791 WrongCaseFunction { used: String, canonical: String },
795 WrongCaseMethod {
798 class: String,
799 used: String,
800 canonical: String,
801 },
802 WrongCaseClass { used: String, canonical: String },
805 DuplicateClass { name: String },
808 DuplicateInterface { name: String },
811 DuplicateTrait { name: String },
814 DuplicateEnum { name: String },
817 DuplicateFunction { name: String },
820
821 PluginIssue { name: String, message: String },
825}
826
827fn append_deprecation_message(base: String, message: &Option<Arc<str>>) -> String {
828 match message.as_deref().filter(|m| !m.is_empty()) {
829 Some(msg) => format!("{base}: {msg}"),
830 None => base,
831 }
832}
833
834impl IssueKind {
835 pub fn is_constant_condition(&self) -> bool {
837 matches!(
838 self,
839 IssueKind::RedundantCondition { .. }
840 | IssueKind::ImpossibleIdenticalComparison { .. }
841 | IssueKind::ImpossibleLooseComparison { .. }
842 | IssueKind::TypeDoesNotContainType { .. }
843 | IssueKind::DocblockTypeContradiction { .. }
844 | IssueKind::ParadoxicalCondition { .. }
845 )
846 }
847
848 pub fn default_severity(&self) -> Severity {
850 match self {
851 IssueKind::NonStaticSelfCall { .. }
853 | IssueKind::DirectConstructorCall { .. }
854 | IssueKind::InvalidScope { .. }
855 | IssueKind::UndefinedVariable { .. }
856 | IssueKind::UndefinedFunction { .. }
857 | IssueKind::UndefinedMethod { .. }
858 | IssueKind::UndefinedClass { .. }
859 | IssueKind::NotAnInterface { .. }
860 | IssueKind::UndefinedConstant { .. }
861 | IssueKind::InaccessibleClassConstant { .. }
862 | IssueKind::InaccessibleProperty { .. }
863 | IssueKind::InvalidReturnType { .. }
864 | IssueKind::InvalidArgument { .. }
865 | IssueKind::TooFewArguments { .. }
866 | IssueKind::TooManyArguments { .. }
867 | IssueKind::InvalidNamedArgument { .. }
868 | IssueKind::InvalidNamedArguments { .. }
869 | IssueKind::InvalidPassByReference { .. }
870 | IssueKind::InvalidThrow { .. }
871 | IssueKind::InvalidCatch { .. }
872 | IssueKind::InvalidStaticInvocation { .. }
873 | IssueKind::UnimplementedAbstractMethod { .. }
874 | IssueKind::UnimplementedInterfaceMethod { .. }
875 | IssueKind::MethodSignatureMismatch { .. }
876 | IssueKind::InvalidExtendClass { .. }
877 | IssueKind::FinalMethodOverridden { .. }
878 | IssueKind::AbstractInstantiation { .. }
879 | IssueKind::AbstractMethodCall { .. }
880 | IssueKind::InterfaceInstantiation { .. }
881 | IssueKind::InvalidOverride { .. }
882 | IssueKind::InvalidTemplateParam { .. }
883 | IssueKind::ReadonlyPropertyAssignment { .. }
884 | IssueKind::ReadonlyPropertyAlreadyInitialized { .. }
885 | IssueKind::ParseError { .. }
886 | IssueKind::TaintedInput { .. }
887 | IssueKind::TaintedHtml
888 | IssueKind::TaintedSql
889 | IssueKind::TaintedShell
890 | IssueKind::TaintedLlmPrompt
891 | IssueKind::TaintedHeader
892 | IssueKind::TaintedCookie
893 | IssueKind::CircularInheritance { .. }
894 | IssueKind::InvalidTraitUse { .. }
895 | IssueKind::UndefinedTrait { .. }
896 | IssueKind::TraitConstantAccessedDirectly { .. }
897 | IssueKind::UndefinedTraitAliasMethod { .. }
898 | IssueKind::InvalidClone { .. }
899 | IssueKind::InvalidToString { .. }
900 | IssueKind::TypeCheckMismatch { .. }
901 | IssueKind::PropertyTypeRedeclarationMismatch { .. }
902 | IssueKind::ReadonlyPropertyRedeclarationMismatch { .. }
903 | IssueKind::StaticPropertyRedeclarationMismatch { .. }
904 | IssueKind::ReadonlyClassExtendsMismatch { .. }
905 | IssueKind::InvalidReadonlyPropertyDeclaration { .. }
906 | IssueKind::BackedEnumCaseTypeMismatch { .. }
907 | IssueKind::DivisionByZero { .. }
908 | IssueKind::ParentNotFound => Severity::Error,
909 IssueKind::Trace { .. } => Severity::Info,
910
911 IssueKind::NullArgument { .. }
913 | IssueKind::NullPropertyFetch { .. }
914 | IssueKind::NullMethodCall { .. }
915 | IssueKind::NullArrayAccess
916 | IssueKind::NullableReturnStatement { .. }
917 | IssueKind::InvalidPropertyFetch { .. }
918 | IssueKind::InvalidArrayAccess { .. }
919 | IssueKind::InvalidArrayAssignment { .. }
920 | IssueKind::InvalidPropertyAssignment { .. }
921 | IssueKind::InvalidArrayOffset { .. }
922 | IssueKind::NonExistentArrayOffset { .. }
923 | IssueKind::PossiblyInvalidArrayOffset { .. }
924 | IssueKind::UndefinedProperty { .. }
925 | IssueKind::InvalidOperand { .. }
926 | IssueKind::OverriddenMethodAccess { .. }
927 | IssueKind::OverriddenPropertyAccess { .. }
928 | IssueKind::ImplicitToStringCast { .. }
929 | IssueKind::ImplicitFloatToIntCast { .. }
930 | IssueKind::UnusedVariable { .. }
931 | IssueKind::UnusedForeachValue { .. }
932 | IssueKind::ImpurePropertyAssignment { .. }
933 | IssueKind::ImpureMethodCall { .. }
934 | IssueKind::ImpureGlobalVariable { .. }
935 | IssueKind::ImpureStaticVariable { .. }
936 | IssueKind::ImpureStaticPropertyAssignment { .. }
937 | IssueKind::ImpureStaticPropertyAccess { .. }
938 | IssueKind::ImpureFunctionCall { .. }
939 | IssueKind::ImpureByRefAssignment { .. }
940 | IssueKind::ImmutablePropertyModification { .. }
941 | IssueKind::UnsupportedReferenceUsage
942 | IssueKind::ParadoxicalCondition { .. }
943 | IssueKind::UnhandledMatchCondition { .. }
944 | IssueKind::InvalidStringClass { .. }
945 | IssueKind::ImpossibleIdenticalComparison { .. }
946 | IssueKind::ImpossibleLooseComparison { .. }
947 | IssueKind::DuplicateArrayKey { .. }
948 | IssueKind::ForbiddenCode { .. }
949 | IssueKind::UndefinedThrowsDocblock { .. }
950 | IssueKind::InvalidDocblockType { .. } => Severity::Warning,
951
952 IssueKind::PossiblyUndefinedVariable { .. } => Severity::Warning,
954
955 IssueKind::PossiblyNullArgument { .. }
957 | IssueKind::PossiblyInvalidArgument { .. }
958 | IssueKind::PossiblyNullPropertyFetch { .. }
959 | IssueKind::PossiblyNullMethodCall { .. }
960 | IssueKind::PossiblyNullArrayAccess
961 | IssueKind::PossiblyInvalidArrayAccess { .. }
962 | IssueKind::PossiblyInvalidClone { .. }
963 | IssueKind::PossiblyInvalidOperand { .. }
964 | IssueKind::PossiblyNullOperand { .. }
965 | IssueKind::PossiblyRawObjectIteration { .. } => Severity::Info,
966
967 IssueKind::RawObjectIteration { .. } => Severity::Warning,
968
969 IssueKind::RedundantCondition { .. }
971 | IssueKind::RedundantCast { .. }
972 | IssueKind::UnnecessaryVarAnnotation { .. }
973 | IssueKind::TypeDoesNotContainType { .. }
974 | IssueKind::DocblockTypeContradiction { .. }
975 | IssueKind::UnevaluatedCode { .. }
976 | IssueKind::IfThisIsMismatch { .. }
977 | IssueKind::UnusedParam { .. }
978 | IssueKind::UnreachableCode
979 | IssueKind::UnreachableCatch { .. }
980 | IssueKind::UnusedMethod { .. }
981 | IssueKind::UnusedProperty { .. }
982 | IssueKind::UnusedFunction { .. }
983 | IssueKind::UnusedClass { .. }
984 | IssueKind::UnusedSuppress { .. }
985 | IssueKind::ArgumentTypeCoercion { .. }
986 | IssueKind::PropertyTypeCoercion { .. }
987 | IssueKind::DeprecatedCall { .. }
988 | IssueKind::DeprecatedProperty { .. }
989 | IssueKind::DeprecatedConstant { .. }
990 | IssueKind::DeprecatedInterface { .. }
991 | IssueKind::DeprecatedTrait { .. }
992 | IssueKind::DeprecatedMethodCall { .. }
993 | IssueKind::DeprecatedMethod { .. }
994 | IssueKind::DeprecatedClass { .. }
995 | IssueKind::InternalMethod { .. }
996 | IssueKind::MissingReturnType { .. }
997 | IssueKind::MissingClosureReturnType
998 | IssueKind::MissingParamType { .. }
999 | IssueKind::MissingPropertyType { .. }
1000 | IssueKind::MismatchingDocblockReturnType { .. }
1001 | IssueKind::MismatchingDocblockParamType { .. }
1002 | IssueKind::InvalidDocblock { .. }
1003 | IssueKind::InvalidCast { .. }
1004 | IssueKind::MixedArgument { .. }
1005 | IssueKind::MixedAssignment { .. }
1006 | IssueKind::MixedMethodCall { .. }
1007 | IssueKind::NoInterfaceProperties { .. }
1008 | IssueKind::UndefinedDocblockClass { .. }
1009 | IssueKind::MissingConstructor { .. }
1010 | IssueKind::PropertyPossiblyUninitialized { .. }
1011 | IssueKind::MixedFunctionCall
1012 | IssueKind::MixedReturnStatement { .. }
1013 | IssueKind::MixedPropertyFetch { .. }
1014 | IssueKind::MixedPropertyAssignment { .. }
1015 | IssueKind::MixedArrayAccess
1016 | IssueKind::MixedArrayOffset
1017 | IssueKind::MixedClone
1018 | IssueKind::ShadowedTemplateParam { .. }
1019 | IssueKind::MissingThrowsDocblock { .. }
1020 | IssueKind::WrongCaseFunction { .. }
1021 | IssueKind::WrongCaseMethod { .. }
1022 | IssueKind::WrongCaseClass { .. }
1023 | IssueKind::InvalidAttribute { .. }
1024 | IssueKind::UndefinedAttributeClass { .. } => Severity::Info,
1025 IssueKind::DuplicateClass { .. }
1026 | IssueKind::DuplicateInterface { .. }
1027 | IssueKind::DuplicateTrait { .. }
1028 | IssueKind::DuplicateEnum { .. }
1029 | IssueKind::DuplicateFunction { .. } => Severity::Error,
1030 IssueKind::PluginIssue { .. } => Severity::Error,
1033 }
1034 }
1035
1036 pub fn code(&self) -> &'static str {
1066 match self {
1067 IssueKind::NonStaticSelfCall { .. } => "MIR0216",
1069 IssueKind::DirectConstructorCall { .. } => "MIR0217",
1070 IssueKind::InvalidScope { .. } => "MIR0001",
1071 IssueKind::UndefinedVariable { .. } => "MIR0002",
1072 IssueKind::UndefinedFunction { .. } => "MIR0003",
1073 IssueKind::UndefinedMethod { .. } => "MIR0004",
1074 IssueKind::UndefinedClass { .. } => "MIR0005",
1075 IssueKind::UndefinedProperty { .. } => "MIR0006",
1076 IssueKind::UndefinedConstant { .. } => "MIR0007",
1077 IssueKind::InaccessibleClassConstant { .. } => "MIR0011",
1078 IssueKind::InaccessibleProperty { .. } => "MIR0014",
1079 IssueKind::TraitConstantAccessedDirectly { .. } => "MIR0012",
1080 IssueKind::UndefinedTraitAliasMethod { .. } => "MIR0013",
1081 IssueKind::PossiblyUndefinedVariable { .. } => "MIR0008",
1082 IssueKind::UndefinedTrait { .. } => "MIR0009",
1083 IssueKind::ParentNotFound => "MIR0010",
1084
1085 IssueKind::NullArgument { .. } => "MIR0100",
1087 IssueKind::NullPropertyFetch { .. } => "MIR0101",
1088 IssueKind::NullMethodCall { .. } => "MIR0102",
1089 IssueKind::NullArrayAccess => "MIR0103",
1090 IssueKind::PossiblyNullArgument { .. } => "MIR0104",
1091 IssueKind::PossiblyInvalidArgument { .. } => "MIR0105",
1092 IssueKind::PossiblyNullPropertyFetch { .. } => "MIR0106",
1093 IssueKind::PossiblyNullMethodCall { .. } => "MIR0107",
1094 IssueKind::PossiblyNullArrayAccess => "MIR0108",
1095 IssueKind::NullableReturnStatement { .. } => "MIR0109",
1096
1097 IssueKind::InvalidReturnType { .. } => "MIR0200",
1099 IssueKind::InvalidArgument { .. } => "MIR0201",
1100 IssueKind::TooFewArguments { .. } => "MIR0202",
1101 IssueKind::TooManyArguments { .. } => "MIR0203",
1102 IssueKind::InvalidNamedArgument { .. } => "MIR0204",
1103 IssueKind::InvalidNamedArguments { .. } => "MIR0224",
1104 IssueKind::InvalidPassByReference { .. } => "MIR0205",
1105 IssueKind::InvalidPropertyFetch { .. } => "MIR0218",
1106 IssueKind::InvalidArrayAccess { .. } => "MIR0219",
1107 IssueKind::PossiblyInvalidArrayAccess { .. } => "MIR0227",
1108 IssueKind::InvalidArrayAssignment { .. } => "MIR0220",
1109 IssueKind::InvalidPropertyAssignment { .. } => "MIR0206",
1110 IssueKind::InvalidCast { .. } => "MIR0207",
1111 IssueKind::InvalidStaticInvocation { .. } => "MIR0215",
1112 IssueKind::InvalidOperand { .. } => "MIR0208",
1113 IssueKind::PossiblyInvalidOperand { .. } => "MIR0213",
1114 IssueKind::PossiblyNullOperand { .. } => "MIR0214",
1115 IssueKind::DivisionByZero { .. } => "MIR0229",
1116 IssueKind::RawObjectIteration { .. } => "MIR0222",
1117 IssueKind::PossiblyRawObjectIteration { .. } => "MIR0223",
1118 IssueKind::MismatchingDocblockReturnType { .. } => "MIR0209",
1119 IssueKind::MismatchingDocblockParamType { .. } => "MIR0210",
1120 IssueKind::InvalidStringClass { .. } => "MIR0211",
1121 IssueKind::NotAnInterface { .. } => "MIR0228",
1122 IssueKind::TypeCheckMismatch { .. } => "MIR0212",
1123 IssueKind::Trace { .. } => "MIR0221",
1124 IssueKind::ArgumentTypeCoercion { .. } => "MIR0225",
1125 IssueKind::PropertyTypeCoercion { .. } => "MIR0226",
1126
1127 IssueKind::InvalidArrayOffset { .. } => "MIR0300",
1129 IssueKind::NonExistentArrayOffset { .. } => "MIR0301",
1130 IssueKind::PossiblyInvalidArrayOffset { .. } => "MIR0302",
1131 IssueKind::DuplicateArrayKey { .. } => "MIR0303",
1132
1133 IssueKind::RedundantCondition { .. } => "MIR0400",
1135 IssueKind::RedundantCast { .. } => "MIR0401",
1136 IssueKind::UnnecessaryVarAnnotation { .. } => "MIR0402",
1137 IssueKind::TypeDoesNotContainType { .. } => "MIR0403",
1138 IssueKind::ParadoxicalCondition { .. } => "MIR0404",
1139 IssueKind::UnhandledMatchCondition { .. } => "MIR0405",
1140 IssueKind::DocblockTypeContradiction { .. } => "MIR0406",
1141 IssueKind::UnevaluatedCode { .. } => "MIR0407",
1142 IssueKind::ImpossibleIdenticalComparison { .. } => "MIR0408",
1143 IssueKind::ImpossibleLooseComparison { .. } => "MIR0409",
1144
1145 IssueKind::UnusedVariable { .. } => "MIR0500",
1147 IssueKind::UnusedParam { .. } => "MIR0501",
1148 IssueKind::UnreachableCode => "MIR0502",
1149 IssueKind::UnusedMethod { .. } => "MIR0503",
1150 IssueKind::UnusedProperty { .. } => "MIR0504",
1151 IssueKind::UnusedFunction { .. } => "MIR0505",
1152 IssueKind::UnusedForeachValue { .. } => "MIR0506",
1153 IssueKind::UnusedClass { .. } => "MIR0507",
1154 IssueKind::UnusedSuppress { .. } => "MIR0508",
1155
1156 IssueKind::ImpurePropertyAssignment { .. } => "MIR1700",
1158 IssueKind::ImpureMethodCall { .. } => "MIR1701",
1159 IssueKind::ImpureGlobalVariable { .. } => "MIR1702",
1160 IssueKind::ImpureStaticVariable { .. } => "MIR1703",
1161 IssueKind::ImpureStaticPropertyAssignment { .. } => "MIR1706",
1162 IssueKind::ImpureByRefAssignment { .. } => "MIR1707",
1163 IssueKind::ImpureStaticPropertyAccess { .. } => "MIR1708",
1164 IssueKind::ImpureFunctionCall { .. } => "MIR1704",
1165 IssueKind::ImmutablePropertyModification { .. } => "MIR1705",
1166 IssueKind::UnsupportedReferenceUsage => "MIR1506",
1167 IssueKind::NoInterfaceProperties { .. } => "MIR1504",
1168 IssueKind::UndefinedDocblockClass { .. } => "MIR1505",
1169 IssueKind::MissingConstructor { .. } => "MIR1507",
1170 IssueKind::PropertyPossiblyUninitialized { .. } => "MIR1510",
1171 IssueKind::MixedFunctionCall => "MIR1211",
1172 IssueKind::MixedReturnStatement { .. } => "MIR1212",
1173
1174 IssueKind::ReadonlyPropertyAssignment { .. } => "MIR0600",
1176 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => "MIR0601",
1177
1178 IssueKind::UnimplementedAbstractMethod { .. } => "MIR0700",
1180 IssueKind::UnimplementedInterfaceMethod { .. } => "MIR0701",
1181 IssueKind::MethodSignatureMismatch { .. } => "MIR0702",
1182 IssueKind::OverriddenMethodAccess { .. } => "MIR0703",
1183 IssueKind::OverriddenPropertyAccess { .. } => "MIR0710",
1184 IssueKind::PropertyTypeRedeclarationMismatch { .. } => "MIR0712",
1185 IssueKind::BackedEnumCaseTypeMismatch { .. } => "MIR0713",
1186 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => "MIR0714",
1187 IssueKind::StaticPropertyRedeclarationMismatch { .. } => "MIR0715",
1188 IssueKind::ReadonlyClassExtendsMismatch { .. } => "MIR0716",
1189 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => "MIR0717",
1190 IssueKind::InvalidExtendClass { .. } => "MIR0704",
1191 IssueKind::FinalMethodOverridden { .. } => "MIR0705",
1192 IssueKind::AbstractInstantiation { .. } => "MIR0706",
1193 IssueKind::AbstractMethodCall { .. } => "MIR0711",
1194 IssueKind::InterfaceInstantiation { .. } => "MIR0709",
1195 IssueKind::CircularInheritance { .. } => "MIR0707",
1196 IssueKind::InvalidOverride { .. } => "MIR0708",
1197
1198 IssueKind::TaintedInput { .. } => "MIR0800",
1200 IssueKind::TaintedHtml => "MIR0801",
1201 IssueKind::TaintedSql => "MIR0802",
1202 IssueKind::TaintedShell => "MIR0803",
1203 IssueKind::TaintedLlmPrompt => "MIR0804",
1204 IssueKind::TaintedHeader => "MIR0805",
1205 IssueKind::TaintedCookie => "MIR0806",
1206
1207 IssueKind::InvalidTemplateParam { .. } => "MIR0900",
1209 IssueKind::ShadowedTemplateParam { .. } => "MIR0901",
1210 IssueKind::IfThisIsMismatch { .. } => "MIR0902",
1211
1212 IssueKind::DeprecatedCall { .. } => "MIR1000",
1214 IssueKind::WrongCaseFunction { .. } => "MIR1009",
1215 IssueKind::WrongCaseMethod { .. } => "MIR1010",
1216 IssueKind::WrongCaseClass { .. } => "MIR1011",
1217 IssueKind::DeprecatedProperty { .. } => "MIR1005",
1218 IssueKind::DeprecatedInterface { .. } => "MIR1006",
1219 IssueKind::DeprecatedTrait { .. } => "MIR1007",
1220 IssueKind::DeprecatedConstant { .. } => "MIR1008",
1221 IssueKind::DeprecatedMethodCall { .. } => "MIR1001",
1222 IssueKind::DeprecatedMethod { .. } => "MIR1002",
1223 IssueKind::DeprecatedClass { .. } => "MIR1003",
1224 IssueKind::InternalMethod { .. } => "MIR1004",
1225
1226 IssueKind::MissingReturnType { .. } => "MIR1100",
1228 IssueKind::MissingParamType { .. } => "MIR1101",
1229 IssueKind::MissingPropertyType { .. } => "MIR1104",
1230 IssueKind::MissingClosureReturnType => "MIR1105",
1231 IssueKind::MissingThrowsDocblock { .. } => "MIR1102",
1232 IssueKind::InvalidDocblock { .. } => "MIR1103",
1233 IssueKind::UndefinedThrowsDocblock { .. } => "MIR1106",
1234 IssueKind::InvalidDocblockType { .. } => "MIR1107",
1235
1236 IssueKind::MixedArgument { .. } => "MIR1200",
1238 IssueKind::MixedAssignment { .. } => "MIR1201",
1239 IssueKind::MixedMethodCall { .. } => "MIR1202",
1240 IssueKind::MixedPropertyFetch { .. } => "MIR1203",
1241 IssueKind::MixedPropertyAssignment { .. } => "MIR1208",
1242 IssueKind::MixedArrayAccess => "MIR1209",
1243 IssueKind::MixedArrayOffset => "MIR1210",
1244 IssueKind::MixedClone => "MIR1204",
1245 IssueKind::InvalidClone { .. } => "MIR1205",
1246 IssueKind::PossiblyInvalidClone { .. } => "MIR1206",
1247 IssueKind::InvalidToString { .. } => "MIR1207",
1248
1249 IssueKind::InvalidTraitUse { .. } => "MIR1300",
1251 IssueKind::ForbiddenCode { .. } => "MIR1301",
1252
1253 IssueKind::ParseError { .. } => "MIR1400",
1255
1256 IssueKind::InvalidAttribute { .. } => "MIR1600",
1258 IssueKind::UndefinedAttributeClass { .. } => "MIR1601",
1259 IssueKind::DuplicateClass { .. } => "MIR1602",
1260 IssueKind::DuplicateInterface { .. } => "MIR1603",
1261 IssueKind::DuplicateTrait { .. } => "MIR1604",
1262 IssueKind::DuplicateEnum { .. } => "MIR1605",
1263 IssueKind::DuplicateFunction { .. } => "MIR1606",
1264
1265 IssueKind::InvalidThrow { .. } => "MIR1500",
1267 IssueKind::InvalidCatch { .. } => "MIR1503",
1268 IssueKind::UnreachableCatch { .. } => "MIR1508",
1269 IssueKind::PluginIssue { .. } => "MIR1509",
1270 IssueKind::ImplicitToStringCast { .. } => "MIR1501",
1271 IssueKind::ImplicitFloatToIntCast { .. } => "MIR1502",
1272 }
1273 }
1274
1275 pub fn default_severity_for_code(code: &str) -> Option<Severity> {
1281 match code {
1282 "MIR0001" | "MIR0002" | "MIR0003" | "MIR0004" | "MIR0005" | "MIR0007" | "MIR0009"
1284 | "MIR0010" | "MIR0011" | "MIR0012" | "MIR0013" | "MIR0014" | "MIR0200" | "MIR0201"
1285 | "MIR0202" | "MIR0203" | "MIR0204" | "MIR0205" | "MIR0212" | "MIR0215" | "MIR0216"
1286 | "MIR0217" | "MIR0224" | "MIR0600" | "MIR0601" | "MIR0700" | "MIR0701" | "MIR0702"
1287 | "MIR0704" | "MIR0705" | "MIR0706" | "MIR0707" | "MIR0708" | "MIR0709" | "MIR0711"
1288 | "MIR0712" | "MIR0713" | "MIR0714" | "MIR0715" | "MIR0716" | "MIR0717" | "MIR0228"
1289 | "MIR0229" | "MIR0800" | "MIR0801" | "MIR0802" | "MIR0803" | "MIR0804" | "MIR0805"
1290 | "MIR0806" | "MIR0900" | "MIR1205" | "MIR1207" | "MIR1300" | "MIR1400" | "MIR1500"
1291 | "MIR1503" | "MIR1602" | "MIR1603" | "MIR1604" | "MIR1605" | "MIR1606" => {
1292 Some(Severity::Error)
1293 }
1294
1295 "MIR0006" | "MIR0008" | "MIR0100" | "MIR0101" | "MIR0102" | "MIR0103" | "MIR0109"
1297 | "MIR0206" | "MIR0208" | "MIR0211" | "MIR0218" | "MIR0219" | "MIR0220" | "MIR0222"
1298 | "MIR0300" | "MIR0301" | "MIR0302" | "MIR0303" | "MIR0404" | "MIR0405" | "MIR0408"
1299 | "MIR0500" | "MIR0506" | "MIR0703" | "MIR0710" | "MIR1301" | "MIR1501" | "MIR1502"
1300 | "MIR1700" | "MIR1701" | "MIR1702" | "MIR1703" | "MIR1704" | "MIR1705" | "MIR1706"
1301 | "MIR1707" | "MIR1708" | "MIR1506" | "MIR1106" | "MIR1107" => Some(Severity::Warning),
1302
1303 "MIR0104" | "MIR0105" | "MIR0106" | "MIR0107" | "MIR0108" | "MIR0207" | "MIR0209"
1305 | "MIR0210" | "MIR0213" | "MIR0214" | "MIR0221" | "MIR0223" | "MIR0400" | "MIR0401"
1306 | "MIR0402" | "MIR0403" | "MIR0501" | "MIR0502" | "MIR0503" | "MIR0504" | "MIR0505"
1307 | "MIR0507" | "MIR0508" | "MIR0901" | "MIR1000" | "MIR1001" | "MIR1002" | "MIR1003"
1308 | "MIR1004" | "MIR1005" | "MIR1006" | "MIR1007" | "MIR1008" | "MIR1009" | "MIR1010"
1309 | "MIR1011" | "MIR1100" | "MIR1101" | "MIR1102" | "MIR1103" | "MIR1104" | "MIR1105"
1310 | "MIR1200" | "MIR1201" | "MIR1202" | "MIR1203" | "MIR1204" | "MIR1206" | "MIR1208"
1311 | "MIR1209" | "MIR1210" | "MIR1211" | "MIR1212" | "MIR1504" | "MIR1505" | "MIR1507"
1312 | "MIR1508" | "MIR1600" | "MIR1601" | "MIR0225" | "MIR0226" | "MIR0227" | "MIR0406"
1313 | "MIR0407" | "MIR0902" | "MIR1510" => Some(Severity::Info),
1314
1315 _ => None,
1316 }
1317 }
1318
1319 pub fn name(&self) -> &'static str {
1321 match self {
1322 IssueKind::NonStaticSelfCall { .. } => "NonStaticSelfCall",
1323 IssueKind::DirectConstructorCall { .. } => "DirectConstructorCall",
1324 IssueKind::InvalidScope { .. } => "InvalidScope",
1325 IssueKind::UndefinedVariable { .. } => "UndefinedVariable",
1326 IssueKind::UndefinedFunction { .. } => "UndefinedFunction",
1327 IssueKind::UndefinedMethod { .. } => "UndefinedMethod",
1328 IssueKind::UndefinedClass { .. } => "UndefinedClass",
1329 IssueKind::UndefinedProperty { .. } => "UndefinedProperty",
1330 IssueKind::UndefinedConstant { .. } => "UndefinedConstant",
1331 IssueKind::InaccessibleClassConstant { .. } => "InaccessibleClassConstant",
1332 IssueKind::InaccessibleProperty { .. } => "InaccessibleProperty",
1333 IssueKind::TraitConstantAccessedDirectly { .. } => "TraitConstantAccessedDirectly",
1334 IssueKind::UndefinedTraitAliasMethod { .. } => "UndefinedTraitAliasMethod",
1335 IssueKind::PossiblyUndefinedVariable { .. } => "PossiblyUndefinedVariable",
1336 IssueKind::UndefinedTrait { .. } => "UndefinedTrait",
1337 IssueKind::ParentNotFound => "ParentNotFound",
1338 IssueKind::InvalidStringClass { .. } => "InvalidStringClass",
1339 IssueKind::NotAnInterface { .. } => "NotAnInterface",
1340 IssueKind::NullArgument { .. } => "NullArgument",
1341 IssueKind::NullPropertyFetch { .. } => "NullPropertyFetch",
1342 IssueKind::NullMethodCall { .. } => "NullMethodCall",
1343 IssueKind::NullArrayAccess => "NullArrayAccess",
1344 IssueKind::PossiblyNullArgument { .. } => "PossiblyNullArgument",
1345 IssueKind::PossiblyInvalidArgument { .. } => "PossiblyInvalidArgument",
1346 IssueKind::PossiblyNullPropertyFetch { .. } => "PossiblyNullPropertyFetch",
1347 IssueKind::PossiblyNullMethodCall { .. } => "PossiblyNullMethodCall",
1348 IssueKind::PossiblyNullArrayAccess => "PossiblyNullArrayAccess",
1349 IssueKind::NullableReturnStatement { .. } => "NullableReturnStatement",
1350 IssueKind::InvalidReturnType { .. } => "InvalidReturnType",
1351 IssueKind::InvalidArgument { .. } => "InvalidArgument",
1352 IssueKind::TooFewArguments { .. } => "TooFewArguments",
1353 IssueKind::TooManyArguments { .. } => "TooManyArguments",
1354 IssueKind::InvalidNamedArgument { .. } => "InvalidNamedArgument",
1355 IssueKind::InvalidNamedArguments { .. } => "InvalidNamedArguments",
1356 IssueKind::InvalidPassByReference { .. } => "InvalidPassByReference",
1357 IssueKind::InvalidPropertyFetch { .. } => "InvalidPropertyFetch",
1358 IssueKind::InvalidArrayAccess { .. } => "InvalidArrayAccess",
1359 IssueKind::PossiblyInvalidArrayAccess { .. } => "PossiblyInvalidArrayAccess",
1360 IssueKind::InvalidArrayAssignment { .. } => "InvalidArrayAssignment",
1361 IssueKind::InvalidPropertyAssignment { .. } => "InvalidPropertyAssignment",
1362 IssueKind::InvalidCast { .. } => "InvalidCast",
1363 IssueKind::InvalidStaticInvocation { .. } => "InvalidStaticInvocation",
1364 IssueKind::InvalidOperand { .. } => "InvalidOperand",
1365 IssueKind::PossiblyInvalidOperand { .. } => "PossiblyInvalidOperand",
1366 IssueKind::PossiblyNullOperand { .. } => "PossiblyNullOperand",
1367 IssueKind::DivisionByZero { .. } => "DivisionByZero",
1368 IssueKind::RawObjectIteration { .. } => "RawObjectIteration",
1369 IssueKind::PossiblyRawObjectIteration { .. } => "PossiblyRawObjectIteration",
1370 IssueKind::MismatchingDocblockReturnType { .. } => "MismatchingDocblockReturnType",
1371 IssueKind::MismatchingDocblockParamType { .. } => "MismatchingDocblockParamType",
1372 IssueKind::TypeCheckMismatch { .. } => "TypeCheckMismatch",
1373 IssueKind::DocblockTypeContradiction { .. } => "DocblockTypeContradiction",
1374 IssueKind::ImpossibleIdenticalComparison { .. } => "ImpossibleIdenticalComparison",
1375 IssueKind::ImpossibleLooseComparison { .. } => "ImpossibleLooseComparison",
1376 IssueKind::UnevaluatedCode { .. } => "UnevaluatedCode",
1377 IssueKind::IfThisIsMismatch { .. } => "IfThisIsMismatch",
1378 IssueKind::Trace { .. } => "Trace",
1379 IssueKind::InvalidArrayOffset { .. } => "InvalidArrayOffset",
1380 IssueKind::NonExistentArrayOffset { .. } => "NonExistentArrayOffset",
1381 IssueKind::PossiblyInvalidArrayOffset { .. } => "PossiblyInvalidArrayOffset",
1382 IssueKind::DuplicateArrayKey { .. } => "DuplicateArrayKey",
1383 IssueKind::RedundantCondition { .. } => "RedundantCondition",
1384 IssueKind::RedundantCast { .. } => "RedundantCast",
1385 IssueKind::UnnecessaryVarAnnotation { .. } => "UnnecessaryVarAnnotation",
1386 IssueKind::TypeDoesNotContainType { .. } => "TypeDoesNotContainType",
1387 IssueKind::ParadoxicalCondition { .. } => "ParadoxicalCondition",
1388 IssueKind::UnhandledMatchCondition { .. } => "UnhandledMatchCondition",
1389 IssueKind::UnusedVariable { .. } => "UnusedVariable",
1390 IssueKind::UnusedParam { .. } => "UnusedParam",
1391 IssueKind::UnreachableCode => "UnreachableCode",
1392 IssueKind::UnusedMethod { .. } => "UnusedMethod",
1393 IssueKind::UnusedProperty { .. } => "UnusedProperty",
1394 IssueKind::UnusedFunction { .. } => "UnusedFunction",
1395 IssueKind::UnusedForeachValue { .. } => "UnusedForeachValue",
1396 IssueKind::UnusedClass { .. } => "UnusedClass",
1397 IssueKind::UnusedSuppress { .. } => "UnusedSuppress",
1398 IssueKind::ArgumentTypeCoercion { .. } => "ArgumentTypeCoercion",
1399 IssueKind::PropertyTypeCoercion { .. } => "PropertyTypeCoercion",
1400 IssueKind::ImpurePropertyAssignment { .. } => "ImpurePropertyAssignment",
1401 IssueKind::ImpureMethodCall { .. } => "ImpureMethodCall",
1402 IssueKind::ImpureGlobalVariable { .. } => "ImpureGlobalVariable",
1403 IssueKind::ImpureStaticVariable { .. } => "ImpureStaticVariable",
1404 IssueKind::ImpureByRefAssignment { .. } => "ImpureByRefAssignment",
1405 IssueKind::ImpureStaticPropertyAssignment { .. } => "ImpureStaticPropertyAssignment",
1406 IssueKind::ImpureStaticPropertyAccess { .. } => "ImpureStaticPropertyAccess",
1407 IssueKind::ImpureFunctionCall { .. } => "ImpureFunctionCall",
1408 IssueKind::ImmutablePropertyModification { .. } => "ImmutablePropertyModification",
1409 IssueKind::UnsupportedReferenceUsage => "UnsupportedReferenceUsage",
1410 IssueKind::NoInterfaceProperties { .. } => "NoInterfaceProperties",
1411 IssueKind::UndefinedDocblockClass { .. } => "UndefinedDocblockClass",
1412 IssueKind::MissingConstructor { .. } => "MissingConstructor",
1413 IssueKind::PropertyPossiblyUninitialized { .. } => "PropertyPossiblyUninitialized",
1414 IssueKind::MixedFunctionCall => "MixedFunctionCall",
1415 IssueKind::MixedReturnStatement { .. } => "MixedReturnStatement",
1416 IssueKind::UnimplementedAbstractMethod { .. } => "UnimplementedAbstractMethod",
1417 IssueKind::UnimplementedInterfaceMethod { .. } => "UnimplementedInterfaceMethod",
1418 IssueKind::MethodSignatureMismatch { .. } => "MethodSignatureMismatch",
1419 IssueKind::OverriddenMethodAccess { .. } => "OverriddenMethodAccess",
1420 IssueKind::OverriddenPropertyAccess { .. } => "OverriddenPropertyAccess",
1421 IssueKind::PropertyTypeRedeclarationMismatch { .. } => {
1422 "PropertyTypeRedeclarationMismatch"
1423 }
1424 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => {
1425 "ReadonlyPropertyRedeclarationMismatch"
1426 }
1427 IssueKind::StaticPropertyRedeclarationMismatch { .. } => {
1428 "StaticPropertyRedeclarationMismatch"
1429 }
1430 IssueKind::ReadonlyClassExtendsMismatch { .. } => "ReadonlyClassExtendsMismatch",
1431 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => {
1432 "InvalidReadonlyPropertyDeclaration"
1433 }
1434 IssueKind::BackedEnumCaseTypeMismatch { .. } => "BackedEnumCaseTypeMismatch",
1435 IssueKind::InvalidExtendClass { .. } => "InvalidExtendClass",
1436 IssueKind::FinalMethodOverridden { .. } => "FinalMethodOverridden",
1437 IssueKind::AbstractInstantiation { .. } => "AbstractInstantiation",
1438 IssueKind::AbstractMethodCall { .. } => "AbstractMethodCall",
1439 IssueKind::InterfaceInstantiation { .. } => "InterfaceInstantiation",
1440 IssueKind::InvalidOverride { .. } => "InvalidOverride",
1441 IssueKind::ReadonlyPropertyAssignment { .. } => "ReadonlyPropertyAssignment",
1442 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => {
1443 "ReadonlyPropertyAlreadyInitialized"
1444 }
1445 IssueKind::InvalidTemplateParam { .. } => "InvalidTemplateParam",
1446 IssueKind::ShadowedTemplateParam { .. } => "ShadowedTemplateParam",
1447 IssueKind::TaintedInput { .. } => "TaintedInput",
1448 IssueKind::TaintedHtml => "TaintedHtml",
1449 IssueKind::TaintedSql => "TaintedSql",
1450 IssueKind::TaintedShell => "TaintedShell",
1451 IssueKind::TaintedLlmPrompt => "TaintedLlmPrompt",
1452 IssueKind::TaintedHeader => "TaintedHeader",
1453 IssueKind::TaintedCookie => "TaintedCookie",
1454 IssueKind::DeprecatedCall { .. } => "DeprecatedCall",
1455 IssueKind::DeprecatedProperty { .. } => "DeprecatedProperty",
1456 IssueKind::DeprecatedConstant { .. } => "DeprecatedConstant",
1457 IssueKind::DeprecatedInterface { .. } => "DeprecatedInterface",
1458 IssueKind::DeprecatedTrait { .. } => "DeprecatedTrait",
1459 IssueKind::DeprecatedMethodCall { .. } => "DeprecatedMethodCall",
1460 IssueKind::DeprecatedMethod { .. } => "DeprecatedMethod",
1461 IssueKind::DeprecatedClass { .. } => "DeprecatedClass",
1462 IssueKind::InternalMethod { .. } => "InternalMethod",
1463 IssueKind::MissingReturnType { .. } => "MissingReturnType",
1464 IssueKind::MissingClosureReturnType => "MissingClosureReturnType",
1465 IssueKind::MissingParamType { .. } => "MissingParamType",
1466 IssueKind::MissingPropertyType { .. } => "MissingPropertyType",
1467 IssueKind::InvalidThrow { .. } => "InvalidThrow",
1468 IssueKind::InvalidCatch { .. } => "InvalidCatch",
1469 IssueKind::UnreachableCatch { .. } => "UnreachableCatch",
1470 IssueKind::MissingThrowsDocblock { .. } => "MissingThrowsDocblock",
1471 IssueKind::ImplicitToStringCast { .. } => "ImplicitToStringCast",
1472 IssueKind::ImplicitFloatToIntCast { .. } => "ImplicitFloatToIntCast",
1473 IssueKind::ParseError { .. } => "ParseError",
1474 IssueKind::InvalidDocblock { .. } => "InvalidDocblock",
1475 IssueKind::UndefinedThrowsDocblock { .. } => "UndefinedThrowsDocblock",
1476 IssueKind::InvalidDocblockType { .. } => "InvalidDocblockType",
1477 IssueKind::MixedArgument { .. } => "MixedArgument",
1478 IssueKind::MixedAssignment { .. } => "MixedAssignment",
1479 IssueKind::MixedMethodCall { .. } => "MixedMethodCall",
1480 IssueKind::MixedPropertyFetch { .. } => "MixedPropertyFetch",
1481 IssueKind::MixedPropertyAssignment { .. } => "MixedPropertyAssignment",
1482 IssueKind::MixedArrayAccess => "MixedArrayAccess",
1483 IssueKind::MixedArrayOffset => "MixedArrayOffset",
1484 IssueKind::MixedClone => "MixedClone",
1485 IssueKind::InvalidClone { .. } => "InvalidClone",
1486 IssueKind::PossiblyInvalidClone { .. } => "PossiblyInvalidClone",
1487 IssueKind::InvalidToString { .. } => "InvalidToString",
1488 IssueKind::CircularInheritance { .. } => "CircularInheritance",
1489 IssueKind::InvalidTraitUse { .. } => "InvalidTraitUse",
1490 IssueKind::ForbiddenCode { .. } => "ForbiddenCode",
1491 IssueKind::WrongCaseFunction { .. } => "WrongCaseFunction",
1492 IssueKind::WrongCaseMethod { .. } => "WrongCaseMethod",
1493 IssueKind::WrongCaseClass { .. } => "WrongCaseClass",
1494 IssueKind::InvalidAttribute { .. } => "InvalidAttribute",
1495 IssueKind::UndefinedAttributeClass { .. } => "UndefinedAttributeClass",
1496 IssueKind::DuplicateClass { .. } => "DuplicateClass",
1497 IssueKind::DuplicateInterface { .. } => "DuplicateInterface",
1498 IssueKind::DuplicateTrait { .. } => "DuplicateTrait",
1499 IssueKind::DuplicateEnum { .. } => "DuplicateEnum",
1500 IssueKind::DuplicateFunction { .. } => "DuplicateFunction",
1501 IssueKind::PluginIssue { .. } => "PluginIssue",
1502 }
1503 }
1504
1505 pub fn display_name(&self) -> &str {
1509 match self {
1510 IssueKind::PluginIssue { name, .. } => name,
1511 other => other.name(),
1512 }
1513 }
1514
1515 pub fn message(&self) -> String {
1517 match self {
1518 IssueKind::NonStaticSelfCall { class, method } => {
1519 format!("Non-static method {class}::{method}() cannot be called statically")
1520 }
1521 IssueKind::DirectConstructorCall { class } => {
1522 format!("Cannot call constructor of {class} directly")
1523 }
1524 IssueKind::InvalidScope { in_class } => {
1525 if *in_class {
1526 "$this cannot be used in a static method".to_string()
1527 } else {
1528 "$this cannot be used outside of a class".to_string()
1529 }
1530 }
1531 IssueKind::UndefinedVariable { name } => format!("Variable ${name} is not defined"),
1532 IssueKind::UndefinedFunction { name } => format!("Function {name}() is not defined"),
1533 IssueKind::UndefinedMethod { class, method } => {
1534 format!("Method {class}::{method}() does not exist")
1535 }
1536 IssueKind::UndefinedClass { name } => format!("Class {name} does not exist"),
1537 IssueKind::UndefinedProperty { class, property } => {
1538 format!("Property {class}::${property} does not exist")
1539 }
1540 IssueKind::UndefinedConstant { name } => format!("Constant {name} is not defined"),
1541 IssueKind::InaccessibleClassConstant { class, constant } => {
1542 format!("Cannot access constant {class}::{constant}")
1543 }
1544 IssueKind::InaccessibleProperty { class, property } => {
1545 format!("Cannot access property {class}::${property}")
1546 }
1547 IssueKind::PossiblyUndefinedVariable { name } => {
1548 format!("Variable ${name} might not be defined")
1549 }
1550 IssueKind::UndefinedTrait { name } => format!("Trait {name} does not exist"),
1551 IssueKind::TraitConstantAccessedDirectly {
1552 trait_name,
1553 constant,
1554 } => {
1555 format!("Cannot access trait constant {trait_name}::{constant} directly")
1556 }
1557 IssueKind::UndefinedTraitAliasMethod { trait_name, method } => match trait_name {
1558 Some(t) => {
1559 format!("An alias was defined for {t}::{method} but this method does not exist")
1560 }
1561 None => format!("An alias was defined for {method} but this method does not exist"),
1562 },
1563 IssueKind::ParentNotFound => {
1564 "Cannot use parent:: when current class has no parent".to_string()
1565 }
1566 IssueKind::InvalidStringClass { actual } => {
1567 format!("Dynamic class instantiation requires string or class-string type, got '{actual}'")
1568 }
1569 IssueKind::NotAnInterface { name } => {
1570 format!("{name} is not an interface")
1571 }
1572
1573 IssueKind::NullArgument { param, fn_name } => {
1574 format!("Argument ${param} of {fn_name}() cannot be null")
1575 }
1576 IssueKind::NullPropertyFetch { property } => {
1577 format!("Cannot access property ${property} on null")
1578 }
1579 IssueKind::NullMethodCall { method } => {
1580 format!("Cannot call method {method}() on null")
1581 }
1582 IssueKind::NullArrayAccess => "Cannot access array on null".to_string(),
1583 IssueKind::PossiblyNullArgument { param, fn_name } => {
1584 format!("Argument ${param} of {fn_name}() might be null")
1585 }
1586 IssueKind::PossiblyInvalidArgument {
1587 param,
1588 fn_name,
1589 expected,
1590 actual,
1591 } => {
1592 format!("Argument ${param} of {fn_name}() expects '{expected}', possibly different type '{actual}' provided")
1593 }
1594 IssueKind::PossiblyNullPropertyFetch { property } => {
1595 format!("Cannot access property ${property} on possibly null value")
1596 }
1597 IssueKind::PossiblyNullMethodCall { method } => {
1598 format!("Cannot call method {method}() on possibly null value")
1599 }
1600 IssueKind::PossiblyNullArrayAccess => {
1601 "Cannot access array on possibly null value".to_string()
1602 }
1603 IssueKind::NullableReturnStatement { expected, actual } => {
1604 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1605 }
1606
1607 IssueKind::InvalidReturnType { expected, actual } => {
1608 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1609 }
1610 IssueKind::InvalidArgument {
1611 param,
1612 fn_name,
1613 expected,
1614 actual,
1615 } => {
1616 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}'")
1617 }
1618 IssueKind::TooFewArguments {
1619 fn_name,
1620 expected,
1621 actual,
1622 } => {
1623 format!(
1624 "Too few arguments for {}(): expected {}, got {}",
1625 fn_name, expected, actual
1626 )
1627 }
1628 IssueKind::TooManyArguments {
1629 fn_name,
1630 expected,
1631 actual,
1632 } => {
1633 format!(
1634 "Too many arguments for {}(): expected {}, got {}",
1635 fn_name, expected, actual
1636 )
1637 }
1638 IssueKind::InvalidNamedArgument {
1639 fn_name,
1640 name,
1641 overwrites: true,
1642 } => {
1643 format!(
1644 "{}() argument ${} overwrites a previous argument",
1645 fn_name, name
1646 )
1647 }
1648 IssueKind::InvalidNamedArgument { fn_name, name, .. } => {
1649 format!("{}() has no parameter named ${}", fn_name, name)
1650 }
1651 IssueKind::InvalidNamedArguments { fn_name } => {
1652 format!("{}() does not accept named arguments", fn_name)
1653 }
1654 IssueKind::InvalidPassByReference { fn_name, param } => {
1655 format!(
1656 "Argument ${} of {}() must be passed by reference",
1657 param, fn_name
1658 )
1659 }
1660 IssueKind::InvalidPropertyFetch { ty } => {
1661 format!("Cannot fetch property on non-object type '{ty}'")
1662 }
1663 IssueKind::InvalidArrayAccess { ty } => {
1664 format!("Cannot use [] operator on non-array type '{ty}'")
1665 }
1666 IssueKind::PossiblyInvalidArrayAccess { ty } => {
1667 format!("Possibly invalid array access: '{ty}' might not support []")
1668 }
1669 IssueKind::InvalidArrayAssignment { ty } => {
1670 format!("Cannot use [] assignment on non-array type '{ty}'")
1671 }
1672 IssueKind::InvalidPropertyAssignment {
1673 property,
1674 expected,
1675 actual,
1676 } => {
1677 format!("Property ${property} expects '{expected}', cannot assign '{actual}'")
1678 }
1679 IssueKind::InvalidCast { from, to } => {
1680 format!("Cannot cast '{from}' to '{to}'")
1681 }
1682 IssueKind::InvalidStaticInvocation { class, method } => {
1683 format!("Non-static method {class}::{method}() cannot be called statically")
1684 }
1685 IssueKind::InvalidOperand { op, left, right } => {
1686 if right.is_empty() {
1687 format!("Operator '{op}' not supported for operand of type '{left}'")
1688 } else {
1689 format!("Operator '{op}' not supported between '{left}' and '{right}'")
1690 }
1691 }
1692 IssueKind::PossiblyInvalidOperand { op, left, right } => {
1693 format!("Operator '{op}' might not be supported between '{left}' and '{right}'")
1694 }
1695 IssueKind::PossiblyNullOperand { op, ty } => {
1696 format!("Operator '{op}' operand '{ty}' might be null")
1697 }
1698 IssueKind::DivisionByZero { op } => {
1699 format!("Division by zero: right operand of '{op}' is always 0")
1700 }
1701 IssueKind::RawObjectIteration { ty } => {
1702 format!("Cannot iterate over non-iterable object '{ty}'")
1703 }
1704 IssueKind::PossiblyRawObjectIteration { ty } => {
1705 format!("Cannot iterate over possibly non-iterable object '{ty}'")
1706 }
1707 IssueKind::MismatchingDocblockReturnType { declared, inferred } => {
1708 format!("Docblock return type '{declared}' does not match inferred '{inferred}'")
1709 }
1710 IssueKind::MismatchingDocblockParamType {
1711 param,
1712 declared,
1713 inferred,
1714 } => {
1715 format!(
1716 "Docblock type '{declared}' for ${param} does not match inferred '{inferred}'"
1717 )
1718 }
1719 IssueKind::TypeCheckMismatch {
1720 var,
1721 expected,
1722 actual,
1723 } => {
1724 format!("Type of {var} is expected to be {expected}, got {actual}")
1725 }
1726 IssueKind::Trace {
1727 variable,
1728 type_info,
1729 } => {
1730 format!("Type of ${variable} is {type_info}")
1731 }
1732
1733 IssueKind::InvalidArrayOffset { expected, actual } => {
1734 format!("Array offset expects '{expected}', got '{actual}'")
1735 }
1736 IssueKind::NonExistentArrayOffset { key } => {
1737 format!("Array offset '{key}' does not exist")
1738 }
1739 IssueKind::PossiblyInvalidArrayOffset { expected, actual } => {
1740 format!("Array offset might be invalid: expects '{expected}', got '{actual}'")
1741 }
1742 IssueKind::DuplicateArrayKey { key } => {
1743 format!("Array key {key} is duplicated — the earlier entry is silently overwritten")
1744 }
1745
1746 IssueKind::RedundantCondition {
1747 always_true,
1748 unreachable,
1749 } => match unreachable {
1750 Some(branch) => {
1751 format!("Condition is always {always_true}, so the {branch} is never reached")
1752 }
1753 None => format!("Condition is always {always_true}, so the check is redundant"),
1754 },
1755 IssueKind::RedundantCast { from, to } => {
1756 format!("Casting '{from}' to '{to}' is redundant")
1757 }
1758 IssueKind::UnnecessaryVarAnnotation { var } => {
1759 format!("@var annotation for ${var} is unnecessary")
1760 }
1761 IssueKind::TypeDoesNotContainType { left, right } => {
1762 format!("Type '{left}' can never contain type '{right}'")
1763 }
1764 IssueKind::ParadoxicalCondition { value } => {
1765 format!("Value {value} is duplicated; this branch can never be reached")
1766 }
1767 IssueKind::UnhandledMatchCondition { detail } => {
1768 format!("Unhandled match condition: {detail}")
1769 }
1770 IssueKind::DocblockTypeContradiction { expr, declared } => {
1771 format!("Type '{declared}' makes '{expr}' impossible — this can never hold")
1772 }
1773 IssueKind::ImpossibleIdenticalComparison { op, left, right } => {
1774 let result = if op == "===" { "false" } else { "true" };
1775 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be identical")
1776 }
1777 IssueKind::ImpossibleLooseComparison { op, left, right } => {
1778 let result = if op == "==" { "false" } else { "true" };
1779 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be loosely equal")
1780 }
1781 IssueKind::UnevaluatedCode { reason } => {
1782 format!("Unevaluated code: {reason}")
1783 }
1784 IssueKind::IfThisIsMismatch {
1785 class,
1786 method,
1787 expected,
1788 actual,
1789 } => {
1790 format!(
1791 "Cannot call {class}::{method}() — @if-this-is requires $this to be '{expected}', but it is '{actual}'"
1792 )
1793 }
1794
1795 IssueKind::UnusedVariable { name } => format!("Variable ${name} is never read"),
1796 IssueKind::UnusedParam { name } => format!("Parameter ${name} is never used"),
1797 IssueKind::UnreachableCode => "Unreachable code detected".to_string(),
1798 IssueKind::UnusedMethod { class, method } => {
1799 format!("Private method {class}::{method}() is never called")
1800 }
1801 IssueKind::UnusedProperty { class, property } => {
1802 format!("Private property {class}::${property} is never read")
1803 }
1804 IssueKind::UnusedFunction { name } => {
1805 format!("Function {name}() is never called")
1806 }
1807 IssueKind::UnusedForeachValue { name } => {
1808 format!("Foreach value ${name} is never read")
1809 }
1810 IssueKind::UnusedClass { class } => {
1811 format!("Class {class} is never referenced")
1812 }
1813 IssueKind::UnusedSuppress { kind } => {
1814 format!("Suppress annotation for '{kind}' is never used")
1815 }
1816 IssueKind::ArgumentTypeCoercion {
1817 param,
1818 fn_name,
1819 expected,
1820 actual,
1821 } => {
1822 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}' — coercion may fail at runtime")
1823 }
1824 IssueKind::PropertyTypeCoercion {
1825 property,
1826 expected,
1827 actual,
1828 } => {
1829 format!("Property ${property} expects '{expected}', cannot assign '{actual}' — coercion may fail at runtime")
1830 }
1831 IssueKind::ImpurePropertyAssignment { property } => {
1832 format!("Assigning to property {property} of a parameter in a pure or external-mutation-free context")
1833 }
1834 IssueKind::ImpureMethodCall { method } => {
1835 format!("Calling impure method {method}() in a pure or immutable context")
1836 }
1837 IssueKind::ImpureGlobalVariable { variable } => {
1838 format!("Using global variable ${variable} in a @pure function")
1839 }
1840 IssueKind::ImpureStaticVariable { variable } => {
1841 format!("Using static variable ${variable} in a @pure function")
1842 }
1843 IssueKind::ImpureByRefAssignment { variable } => {
1844 format!("Assigning to by-reference parameter ${variable} in a @pure function")
1845 }
1846 IssueKind::ImpureStaticPropertyAssignment { class, property } => {
1847 format!("Assigning to static property {class}::${property} in a @pure function")
1848 }
1849 IssueKind::ImpureStaticPropertyAccess { class, property } => {
1850 format!("Reading static property {class}::${property} in a @pure function")
1851 }
1852 IssueKind::ImpureFunctionCall { fn_name } => {
1853 format!("Calling impure function {fn_name}() in a @pure function")
1854 }
1855 IssueKind::ImmutablePropertyModification { receiver, property } => {
1856 format!("Assigning to property {property} of {receiver} in an immutable context (@psalm-immutable class or @psalm-mutation-free method)")
1857 }
1858
1859 IssueKind::UnimplementedAbstractMethod { class, method } => {
1860 format!("Class {class} must implement abstract method {method}()")
1861 }
1862 IssueKind::UnimplementedInterfaceMethod {
1863 class,
1864 interface,
1865 method,
1866 } => {
1867 format!("Class {class} must implement {interface}::{method}() from interface")
1868 }
1869 IssueKind::MethodSignatureMismatch {
1870 class,
1871 method,
1872 detail,
1873 } => {
1874 format!("Method {class}::{method}() signature mismatch: {detail}")
1875 }
1876 IssueKind::OverriddenMethodAccess { class, method } => {
1877 format!("Method {class}::{method}() overrides with less visibility")
1878 }
1879 IssueKind::OverriddenPropertyAccess { class, property } => {
1880 format!("Property {class}::${property} overrides with less visibility")
1881 }
1882 IssueKind::PropertyTypeRedeclarationMismatch {
1883 class,
1884 property,
1885 expected,
1886 actual,
1887 } => {
1888 format!(
1889 "Type of {class}::${property} must be {expected} (as in parent class), {actual} given"
1890 )
1891 }
1892 IssueKind::ReadonlyPropertyRedeclarationMismatch {
1893 parent_class,
1894 class,
1895 property,
1896 parent_readonly,
1897 } => {
1898 if *parent_readonly {
1899 format!(
1900 "Cannot redeclare readonly property {parent_class}::${property} as non-readonly {class}::${property}"
1901 )
1902 } else {
1903 format!(
1904 "Cannot redeclare non-readonly property {parent_class}::${property} as readonly {class}::${property}"
1905 )
1906 }
1907 }
1908 IssueKind::StaticPropertyRedeclarationMismatch {
1909 parent_class,
1910 class,
1911 property,
1912 parent_static,
1913 } => {
1914 if *parent_static {
1915 format!(
1916 "Cannot redeclare static property {parent_class}::${property} as non-static {class}::${property}"
1917 )
1918 } else {
1919 format!(
1920 "Cannot redeclare non-static property {parent_class}::${property} as static {class}::${property}"
1921 )
1922 }
1923 }
1924 IssueKind::BackedEnumCaseTypeMismatch {
1925 enum_name,
1926 case_name,
1927 expected,
1928 actual,
1929 } => {
1930 format!(
1931 "Backed enum case {enum_name}::{case_name} has value of type {actual}, but backing type is {expected}"
1932 )
1933 }
1934 IssueKind::ReadonlyClassExtendsMismatch {
1935 parent,
1936 child,
1937 parent_readonly,
1938 } => {
1939 if *parent_readonly {
1940 format!("Non-readonly class {child} cannot extend readonly class {parent}")
1941 } else {
1942 format!("Readonly class {child} cannot extend non-readonly class {parent}")
1943 }
1944 }
1945 IssueKind::InvalidReadonlyPropertyDeclaration { class, property } => {
1946 format!("Readonly property {class}::${property} cannot have a default value")
1947 }
1948 IssueKind::ReadonlyPropertyAssignment { class, property } => {
1949 format!(
1950 "Cannot assign to readonly property {class}::${property} outside of constructor"
1951 )
1952 }
1953 IssueKind::ReadonlyPropertyAlreadyInitialized { class, property } => {
1954 format!(
1955 "Cannot modify readonly property {class}::${property} — already initialized"
1956 )
1957 }
1958 IssueKind::InvalidExtendClass { parent, child } => {
1959 format!("Class {child} cannot extend final class {parent}")
1960 }
1961 IssueKind::InvalidTemplateParam {
1962 name,
1963 expected_bound,
1964 actual,
1965 } => {
1966 if actual.split('|').any(|part| part == "false") {
1967 format!(
1968 "Template type '{name}' inferred as '{actual}' can be false and therefore does not satisfy bound '{expected_bound}'"
1969 )
1970 } else {
1971 format!(
1972 "Template type '{name}' inferred as '{actual}' does not satisfy bound '{expected_bound}'"
1973 )
1974 }
1975 }
1976 IssueKind::ShadowedTemplateParam { name } => {
1977 format!(
1978 "Method template parameter '{name}' shadows class-level template parameter with the same name"
1979 )
1980 }
1981 IssueKind::FinalMethodOverridden {
1982 class,
1983 method,
1984 parent,
1985 } => {
1986 format!("Method {class}::{method}() cannot override final method from {parent}")
1987 }
1988 IssueKind::AbstractInstantiation { class } => {
1989 format!("Cannot instantiate abstract class {class}")
1990 }
1991 IssueKind::AbstractMethodCall { class, method } => {
1992 format!("Cannot call abstract method {class}::{method}()")
1993 }
1994 IssueKind::InterfaceInstantiation { class } => {
1995 format!("Cannot instantiate interface {class}")
1996 }
1997 IssueKind::InvalidOverride {
1998 class,
1999 method,
2000 detail,
2001 } => {
2002 format!("Method {class}::{method}() has #[Override] but {detail}")
2003 }
2004
2005 IssueKind::TaintedInput { sink } => format!("Tainted input reaching sink '{sink}'"),
2006 IssueKind::TaintedHtml => "Tainted HTML output — possible XSS".to_string(),
2007 IssueKind::TaintedSql => "Tainted SQL query — possible SQL injection".to_string(),
2008 IssueKind::TaintedShell => {
2009 "Tainted shell command — possible command injection".to_string()
2010 }
2011 IssueKind::TaintedLlmPrompt => {
2012 "Tainted LLM prompt — possible prompt injection".to_string()
2013 }
2014 IssueKind::TaintedHeader => {
2015 "Tainted HTTP header — possible header injection or open redirect".to_string()
2016 }
2017 IssueKind::TaintedCookie => "Tainted cookie — possible cookie injection".to_string(),
2018
2019 IssueKind::DeprecatedCall { name, message } => {
2020 let base = format!("Call to deprecated function {name}");
2021 append_deprecation_message(base, message)
2022 }
2023 IssueKind::DeprecatedProperty {
2024 class,
2025 property,
2026 message,
2027 } => {
2028 let base = format!("Property {class}::${property} is deprecated");
2029 append_deprecation_message(base, message)
2030 }
2031 IssueKind::DeprecatedConstant {
2032 class,
2033 constant,
2034 message,
2035 } => {
2036 let base = format!("Constant {class}::{constant} is deprecated");
2037 append_deprecation_message(base, message)
2038 }
2039 IssueKind::DeprecatedInterface { name, message } => {
2040 let base = format!("Interface {name} is deprecated");
2041 append_deprecation_message(base, message)
2042 }
2043 IssueKind::DeprecatedTrait { name, message } => {
2044 let base = format!("Trait {name} is deprecated");
2045 append_deprecation_message(base, message)
2046 }
2047 IssueKind::DeprecatedMethodCall {
2048 class,
2049 method,
2050 message,
2051 } => {
2052 let base = format!("Call to deprecated method {class}::{method}");
2053 append_deprecation_message(base, message)
2054 }
2055 IssueKind::DeprecatedMethod {
2056 class,
2057 method,
2058 message,
2059 } => {
2060 let base = format!("Method {class}::{method}() is deprecated");
2061 append_deprecation_message(base, message)
2062 }
2063 IssueKind::DeprecatedClass { name, message } => {
2064 let base = format!("Class {name} is deprecated");
2065 append_deprecation_message(base, message)
2066 }
2067 IssueKind::InternalMethod { class, method } => {
2068 format!("Method {class}::{method}() is marked @internal")
2069 }
2070 IssueKind::MissingReturnType { fn_name } => {
2071 format!("Function {fn_name}() has no return type annotation")
2072 }
2073 IssueKind::MissingClosureReturnType => {
2074 "Closure has no return type annotation".to_string()
2075 }
2076 IssueKind::MissingParamType { fn_name, param } => {
2077 format!("Parameter ${param} of {fn_name}() has no type annotation")
2078 }
2079 IssueKind::MissingPropertyType { class, property } => {
2080 format!("Property {class}::${property} has no type annotation")
2081 }
2082 IssueKind::InvalidThrow { ty } => {
2083 format!("Thrown type '{ty}' does not extend Throwable")
2084 }
2085 IssueKind::InvalidCatch { ty } => {
2086 format!("Caught type '{ty}' does not extend Throwable")
2087 }
2088 IssueKind::UnreachableCatch { ty, shadowed_by } => {
2089 format!("Catch block for '{ty}' is unreachable — already caught by '{shadowed_by}'")
2090 }
2091 IssueKind::MissingThrowsDocblock { class } => {
2092 format!("Exception {class} is thrown but not declared in @throws")
2093 }
2094 IssueKind::ImplicitToStringCast { class } => {
2095 format!("Class {class} is implicitly cast to string")
2096 }
2097 IssueKind::ImplicitFloatToIntCast { from } => {
2098 format!("Implicit cast from {from} to int truncates the fractional part")
2099 }
2100 IssueKind::ParseError { message } => format!("Parse error: {message}"),
2101 IssueKind::InvalidDocblock { message } => format!("Invalid docblock: {message}"),
2102 IssueKind::UndefinedThrowsDocblock { name } => {
2103 format!("@throws class '{name}' does not exist")
2104 }
2105 IssueKind::InvalidDocblockType { message } => {
2106 format!("Invalid docblock type: {message}")
2107 }
2108 IssueKind::MixedArgument { param, fn_name } => {
2109 format!("Argument ${param} of {fn_name}() is mixed")
2110 }
2111 IssueKind::MixedAssignment { var } => {
2112 format!("Variable ${var} is assigned a mixed type")
2113 }
2114 IssueKind::MixedMethodCall { method } => {
2115 format!("Method {method}() called on mixed type")
2116 }
2117 IssueKind::UnsupportedReferenceUsage => {
2118 "Reference assignment is not supported".to_string()
2119 }
2120 IssueKind::NoInterfaceProperties { property } => {
2121 format!("Property ${property} is not defined on this interface")
2122 }
2123 IssueKind::UndefinedDocblockClass { name } => {
2124 format!("Docblock type '{name}' does not exist")
2125 }
2126 IssueKind::MissingConstructor { class } => {
2127 format!("Class {class} has uninitialized properties but no constructor")
2128 }
2129 IssueKind::PropertyPossiblyUninitialized { class, property } => {
2130 format!(
2131 "Property {class}::${property} may be left uninitialized by the constructor"
2132 )
2133 }
2134 IssueKind::MixedFunctionCall => "Cannot call mixed type as a function".to_string(),
2135 IssueKind::MixedReturnStatement { declared } => {
2136 format!("Cannot return a mixed type from function with declared return type '{declared}'")
2137 }
2138 IssueKind::MixedPropertyFetch { property } => {
2139 format!("Property ${property} fetched on mixed type")
2140 }
2141 IssueKind::MixedPropertyAssignment { property } => {
2142 format!("Property ${property} assigned on mixed type")
2143 }
2144 IssueKind::MixedArrayAccess => "Array access on mixed type".to_string(),
2145 IssueKind::MixedArrayOffset => "Mixed type used as array offset".to_string(),
2146 IssueKind::MixedClone => "cannot clone mixed".to_string(),
2147 IssueKind::InvalidClone { ty } => format!("cannot clone non-object {ty}"),
2148 IssueKind::PossiblyInvalidClone { ty } => {
2149 format!("cannot clone possibly non-object {ty}")
2150 }
2151 IssueKind::InvalidToString { class } => {
2152 format!("Method {class}::__toString() must return a string")
2153 }
2154 IssueKind::CircularInheritance { class } => {
2155 format!("Class {class} has a circular inheritance chain")
2156 }
2157 IssueKind::InvalidTraitUse { trait_name, reason } => {
2158 format!("Trait {trait_name} used incorrectly: {reason}")
2159 }
2160 IssueKind::WrongCaseFunction { used, canonical } => {
2161 format!("Function name '{used}' has incorrect casing; use '{canonical}'")
2162 }
2163 IssueKind::WrongCaseMethod {
2164 class,
2165 used,
2166 canonical,
2167 } => {
2168 format!("Method name '{class}::{used}' has incorrect casing; use '{canonical}'")
2169 }
2170 IssueKind::WrongCaseClass { used, canonical } => {
2171 format!("Class name '{used}' has incorrect casing; use '{canonical}'")
2172 }
2173 IssueKind::InvalidAttribute { message } => message.clone(),
2174 IssueKind::UndefinedAttributeClass { name } => {
2175 format!("Attribute class {name} does not exist")
2176 }
2177 IssueKind::ForbiddenCode { message } => message.clone(),
2178 IssueKind::DuplicateClass { name } => {
2179 format!("Class {name} has already been defined")
2180 }
2181 IssueKind::DuplicateInterface { name } => {
2182 format!("Interface {name} has already been defined")
2183 }
2184 IssueKind::DuplicateTrait { name } => {
2185 format!("Trait {name} has already been defined")
2186 }
2187 IssueKind::DuplicateEnum { name } => {
2188 format!("Enum {name} has already been defined")
2189 }
2190 IssueKind::DuplicateFunction { name } => {
2191 format!("Function {name}() has already been defined")
2192 }
2193 IssueKind::PluginIssue { message, .. } => message.clone(),
2194 }
2195 }
2196}
2197
2198#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2203pub struct Issue {
2204 pub kind: IssueKind,
2205 pub severity: Severity,
2206 pub location: Location,
2207 pub snippet: Option<String>,
2208 pub suppressed: bool,
2209}
2210
2211impl Issue {
2212 pub fn new(kind: IssueKind, location: Location) -> Self {
2213 let severity = kind.default_severity();
2214 Self {
2215 severity,
2216 kind,
2217 location,
2218 snippet: None,
2219 suppressed: false,
2220 }
2221 }
2222
2223 pub fn with_snippet(mut self, snippet: impl Into<String>) -> Self {
2224 self.snippet = Some(snippet.into());
2225 self
2226 }
2227
2228 pub fn suppress(mut self) -> Self {
2229 self.suppressed = true;
2230 self
2231 }
2232}
2233
2234#[derive(Debug, Default)]
2239pub struct IssueBuffer {
2240 issues: Vec<Issue>,
2241 seen: HashSet<(&'static str, Arc<str>, u32, u16)>,
2242 file_suppressions: Vec<String>,
2244 discard: bool,
2245}
2246
2247impl IssueBuffer {
2248 pub fn new() -> Self {
2249 Self::default()
2250 }
2251
2252 pub fn discarding(mut self) -> Self {
2253 self.discard = true;
2254 self
2255 }
2256
2257 pub fn add(&mut self, issue: Issue) {
2258 if self.discard {
2259 return;
2260 }
2261 let key = (
2262 issue.kind.name(),
2263 issue.location.file.clone(),
2264 issue.location.line,
2265 issue.location.col_start,
2266 );
2267 if self.seen.insert(key) {
2268 self.issues.push(issue);
2269 }
2270 }
2271
2272 pub fn has_impossible_comparison_within(&self, span: &Location) -> bool {
2275 self.issues.iter().any(|i| {
2276 matches!(
2277 i.kind,
2278 IssueKind::ImpossibleIdenticalComparison { .. }
2279 | IssueKind::ImpossibleLooseComparison { .. }
2280 | IssueKind::DocblockTypeContradiction { .. }
2281 | IssueKind::TypeDoesNotContainType { .. }
2282 ) && i.location.file == span.file
2283 && (i.location.line, i.location.col_start) >= (span.line, span.col_start)
2284 && (i.location.line, i.location.col_start) < (span.line_end, span.col_end)
2285 })
2286 }
2287
2288 pub fn add_suppression(&mut self, name: impl Into<String>) {
2289 self.file_suppressions.push(name.into());
2290 }
2291
2292 pub fn into_issues(self) -> Vec<Issue> {
2294 self.issues
2295 .into_iter()
2296 .filter(|i| !i.suppressed)
2297 .filter(|i| {
2298 !self
2299 .file_suppressions
2300 .iter()
2301 .any(|s| s == i.kind.display_name())
2302 })
2303 .collect()
2304 }
2305
2306 pub fn into_all_issues(self) -> Vec<Issue> {
2311 self.issues
2312 .into_iter()
2313 .map(|mut i| {
2314 if self
2315 .file_suppressions
2316 .iter()
2317 .any(|s| s == i.kind.display_name())
2318 {
2319 i.suppressed = true;
2320 }
2321 i
2322 })
2323 .collect()
2324 }
2325
2326 pub fn suppress_range(&mut self, from: usize, suppressions: &[String]) {
2329 if suppressions.is_empty() {
2330 return;
2331 }
2332 for issue in self.issues[from..].iter_mut() {
2333 if suppressions.iter().any(|s| s == issue.kind.display_name()) {
2334 issue.suppressed = true;
2335 }
2336 }
2337 }
2338
2339 pub fn issue_count(&self) -> usize {
2342 self.issues.len()
2343 }
2344
2345 pub fn truncate_to(&mut self, mark: usize) {
2351 for issue in self.issues.drain(mark..) {
2352 let key = (
2353 issue.kind.name(),
2354 issue.location.file.clone(),
2355 issue.location.line,
2356 issue.location.col_start,
2357 );
2358 self.seen.remove(&key);
2359 }
2360 }
2361
2362 pub fn take_since(&mut self, mark: usize, pred: impl Fn(&IssueKind) -> bool) -> Vec<Issue> {
2364 self.take_range(mark, self.issues.len(), pred)
2365 }
2366
2367 pub fn take_range(
2369 &mut self,
2370 mark: usize,
2371 mut end: usize,
2372 pred: impl Fn(&IssueKind) -> bool,
2373 ) -> Vec<Issue> {
2374 let mut taken = Vec::new();
2375 let mut i = mark;
2376 while i < end {
2377 if pred(&self.issues[i].kind) {
2378 let issue = self.issues.remove(i);
2379 self.seen.remove(&(
2380 issue.kind.name(),
2381 issue.location.file.clone(),
2382 issue.location.line,
2383 issue.location.col_start,
2384 ));
2385 end -= 1;
2386 taken.push(issue);
2387 } else {
2388 i += 1;
2389 }
2390 }
2391 taken
2392 }
2393
2394 pub fn is_empty(&self) -> bool {
2395 self.issues.is_empty()
2396 }
2397
2398 pub fn len(&self) -> usize {
2399 self.issues.len()
2400 }
2401
2402 pub fn error_count(&self) -> usize {
2403 self.issues
2404 .iter()
2405 .filter(|i| !i.suppressed && i.severity == Severity::Error)
2406 .count()
2407 }
2408
2409 pub fn warning_count(&self) -> usize {
2410 self.issues
2411 .iter()
2412 .filter(|i| !i.suppressed && i.severity == Severity::Warning)
2413 .count()
2414 }
2415}
2416
2417#[cfg(test)]
2418mod code_tests {
2419 use super::*;
2420 use std::collections::HashSet;
2421
2422 fn one_of_each() -> Vec<IssueKind> {
2428 let s = || String::new();
2429 vec![
2430 IssueKind::InvalidScope { in_class: false },
2431 IssueKind::NonStaticSelfCall {
2432 class: s(),
2433 method: s(),
2434 },
2435 IssueKind::DirectConstructorCall { class: s() },
2436 IssueKind::UndefinedVariable { name: s() },
2437 IssueKind::UndefinedFunction { name: s() },
2438 IssueKind::UndefinedMethod {
2439 class: s(),
2440 method: s(),
2441 },
2442 IssueKind::UndefinedClass { name: s() },
2443 IssueKind::NotAnInterface { name: s() },
2444 IssueKind::UndefinedProperty {
2445 class: s(),
2446 property: s(),
2447 },
2448 IssueKind::UndefinedConstant { name: s() },
2449 IssueKind::InaccessibleClassConstant {
2450 class: s(),
2451 constant: s(),
2452 },
2453 IssueKind::InaccessibleProperty {
2454 class: s(),
2455 property: s(),
2456 },
2457 IssueKind::PossiblyUndefinedVariable { name: s() },
2458 IssueKind::UndefinedTrait { name: s() },
2459 IssueKind::TraitConstantAccessedDirectly {
2460 trait_name: s(),
2461 constant: s(),
2462 },
2463 IssueKind::UndefinedTraitAliasMethod {
2464 trait_name: Some(s()),
2465 method: s(),
2466 },
2467 IssueKind::ParentNotFound,
2468 IssueKind::NullArgument {
2469 param: s(),
2470 fn_name: s(),
2471 },
2472 IssueKind::NullPropertyFetch { property: s() },
2473 IssueKind::NullMethodCall { method: s() },
2474 IssueKind::NullArrayAccess,
2475 IssueKind::PossiblyNullArgument {
2476 param: s(),
2477 fn_name: s(),
2478 },
2479 IssueKind::PossiblyInvalidArgument {
2480 param: s(),
2481 fn_name: s(),
2482 expected: s(),
2483 actual: s(),
2484 },
2485 IssueKind::PossiblyNullPropertyFetch { property: s() },
2486 IssueKind::PossiblyNullMethodCall { method: s() },
2487 IssueKind::PossiblyNullArrayAccess,
2488 IssueKind::NullableReturnStatement {
2489 expected: s(),
2490 actual: s(),
2491 },
2492 IssueKind::InvalidReturnType {
2493 expected: s(),
2494 actual: s(),
2495 },
2496 IssueKind::InvalidArgument {
2497 param: s(),
2498 fn_name: s(),
2499 expected: s(),
2500 actual: s(),
2501 },
2502 IssueKind::TooFewArguments {
2503 fn_name: s(),
2504 expected: 0,
2505 actual: 0,
2506 },
2507 IssueKind::TooManyArguments {
2508 fn_name: s(),
2509 expected: 0,
2510 actual: 0,
2511 },
2512 IssueKind::InvalidNamedArgument {
2513 fn_name: s(),
2514 name: s(),
2515 overwrites: false,
2516 },
2517 IssueKind::InvalidNamedArguments { fn_name: s() },
2518 IssueKind::InvalidPassByReference {
2519 fn_name: s(),
2520 param: s(),
2521 },
2522 IssueKind::InvalidPropertyFetch { ty: s() },
2523 IssueKind::InvalidArrayAccess { ty: s() },
2524 IssueKind::PossiblyInvalidArrayAccess { ty: s() },
2525 IssueKind::InvalidArrayAssignment { ty: s() },
2526 IssueKind::InvalidPropertyAssignment {
2527 property: s(),
2528 expected: s(),
2529 actual: s(),
2530 },
2531 IssueKind::InvalidCast { from: s(), to: s() },
2532 IssueKind::InvalidStaticInvocation {
2533 class: s(),
2534 method: s(),
2535 },
2536 IssueKind::InvalidOperand {
2537 op: s(),
2538 left: s(),
2539 right: s(),
2540 },
2541 IssueKind::PossiblyInvalidOperand {
2542 op: s(),
2543 left: s(),
2544 right: s(),
2545 },
2546 IssueKind::PossiblyNullOperand { op: s(), ty: s() },
2547 IssueKind::DivisionByZero { op: s() },
2548 IssueKind::RawObjectIteration { ty: s() },
2549 IssueKind::PossiblyRawObjectIteration { ty: s() },
2550 IssueKind::MismatchingDocblockReturnType {
2551 declared: s(),
2552 inferred: s(),
2553 },
2554 IssueKind::MismatchingDocblockParamType {
2555 param: s(),
2556 declared: s(),
2557 inferred: s(),
2558 },
2559 IssueKind::TypeCheckMismatch {
2560 var: s(),
2561 expected: s(),
2562 actual: s(),
2563 },
2564 IssueKind::Trace {
2565 variable: s(),
2566 type_info: s(),
2567 },
2568 IssueKind::InvalidArrayOffset {
2569 expected: s(),
2570 actual: s(),
2571 },
2572 IssueKind::NonExistentArrayOffset { key: s() },
2573 IssueKind::PossiblyInvalidArrayOffset {
2574 expected: s(),
2575 actual: s(),
2576 },
2577 IssueKind::DuplicateArrayKey { key: s() },
2578 IssueKind::RedundantCondition {
2579 always_true: true,
2580 unreachable: None,
2581 },
2582 IssueKind::RedundantCast { from: s(), to: s() },
2583 IssueKind::UnnecessaryVarAnnotation { var: s() },
2584 IssueKind::TypeDoesNotContainType {
2585 left: s(),
2586 right: s(),
2587 },
2588 IssueKind::UnusedVariable { name: s() },
2589 IssueKind::UnusedParam { name: s() },
2590 IssueKind::UnreachableCode,
2591 IssueKind::UnhandledMatchCondition { detail: s() },
2592 IssueKind::UnusedMethod {
2593 class: s(),
2594 method: s(),
2595 },
2596 IssueKind::UnusedProperty {
2597 class: s(),
2598 property: s(),
2599 },
2600 IssueKind::UnusedFunction { name: s() },
2601 IssueKind::UnusedForeachValue { name: s() },
2602 IssueKind::UnusedClass { class: s() },
2603 IssueKind::UnusedSuppress { kind: s() },
2604 IssueKind::ArgumentTypeCoercion {
2605 param: s(),
2606 fn_name: s(),
2607 expected: s(),
2608 actual: s(),
2609 },
2610 IssueKind::PropertyTypeCoercion {
2611 property: s(),
2612 expected: s(),
2613 actual: s(),
2614 },
2615 IssueKind::ImpurePropertyAssignment { property: s() },
2616 IssueKind::ImpureMethodCall { method: s() },
2617 IssueKind::ImpureGlobalVariable { variable: s() },
2618 IssueKind::ImpureStaticVariable { variable: s() },
2619 IssueKind::ImpureByRefAssignment { variable: s() },
2620 IssueKind::ImpureStaticPropertyAssignment {
2621 class: s(),
2622 property: s(),
2623 },
2624 IssueKind::ImpureStaticPropertyAccess {
2625 class: s(),
2626 property: s(),
2627 },
2628 IssueKind::ImpureFunctionCall { fn_name: s() },
2629 IssueKind::ImmutablePropertyModification {
2630 receiver: s(),
2631 property: s(),
2632 },
2633 IssueKind::ReadonlyPropertyAssignment {
2634 class: s(),
2635 property: s(),
2636 },
2637 IssueKind::ReadonlyPropertyAlreadyInitialized {
2638 class: s(),
2639 property: s(),
2640 },
2641 IssueKind::UnimplementedAbstractMethod {
2642 class: s(),
2643 method: s(),
2644 },
2645 IssueKind::UnimplementedInterfaceMethod {
2646 class: s(),
2647 interface: s(),
2648 method: s(),
2649 },
2650 IssueKind::MethodSignatureMismatch {
2651 class: s(),
2652 method: s(),
2653 detail: s(),
2654 },
2655 IssueKind::OverriddenMethodAccess {
2656 class: s(),
2657 method: s(),
2658 },
2659 IssueKind::OverriddenPropertyAccess {
2660 class: s(),
2661 property: s(),
2662 },
2663 IssueKind::PropertyTypeRedeclarationMismatch {
2664 class: s(),
2665 property: s(),
2666 expected: s(),
2667 actual: s(),
2668 },
2669 IssueKind::ReadonlyPropertyRedeclarationMismatch {
2670 parent_class: s(),
2671 class: s(),
2672 property: s(),
2673 parent_readonly: true,
2674 },
2675 IssueKind::StaticPropertyRedeclarationMismatch {
2676 parent_class: s(),
2677 class: s(),
2678 property: s(),
2679 parent_static: true,
2680 },
2681 IssueKind::ReadonlyClassExtendsMismatch {
2682 parent: s(),
2683 child: s(),
2684 parent_readonly: true,
2685 },
2686 IssueKind::InvalidReadonlyPropertyDeclaration {
2687 class: s(),
2688 property: s(),
2689 },
2690 IssueKind::BackedEnumCaseTypeMismatch {
2691 enum_name: s(),
2692 case_name: s(),
2693 expected: s(),
2694 actual: s(),
2695 },
2696 IssueKind::InvalidExtendClass {
2697 parent: s(),
2698 child: s(),
2699 },
2700 IssueKind::FinalMethodOverridden {
2701 class: s(),
2702 method: s(),
2703 parent: s(),
2704 },
2705 IssueKind::AbstractInstantiation { class: s() },
2706 IssueKind::AbstractMethodCall {
2707 class: s(),
2708 method: s(),
2709 },
2710 IssueKind::InterfaceInstantiation { class: s() },
2711 IssueKind::InvalidOverride {
2712 class: s(),
2713 method: s(),
2714 detail: s(),
2715 },
2716 IssueKind::CircularInheritance { class: s() },
2717 IssueKind::TaintedInput { sink: s() },
2718 IssueKind::TaintedHtml,
2719 IssueKind::TaintedSql,
2720 IssueKind::TaintedShell,
2721 IssueKind::TaintedLlmPrompt,
2722 IssueKind::TaintedHeader,
2723 IssueKind::TaintedCookie,
2724 IssueKind::InvalidTemplateParam {
2725 name: s(),
2726 expected_bound: s(),
2727 actual: s(),
2728 },
2729 IssueKind::ShadowedTemplateParam { name: s() },
2730 IssueKind::DeprecatedCall {
2731 name: s(),
2732 message: None,
2733 },
2734 IssueKind::DeprecatedProperty {
2735 class: s(),
2736 property: s(),
2737 message: None,
2738 },
2739 IssueKind::DeprecatedConstant {
2740 class: s(),
2741 constant: s(),
2742 message: None,
2743 },
2744 IssueKind::DeprecatedInterface {
2745 name: s(),
2746 message: None,
2747 },
2748 IssueKind::DeprecatedTrait {
2749 name: s(),
2750 message: None,
2751 },
2752 IssueKind::DeprecatedMethodCall {
2753 class: s(),
2754 method: s(),
2755 message: None,
2756 },
2757 IssueKind::DeprecatedMethod {
2758 class: s(),
2759 method: s(),
2760 message: None,
2761 },
2762 IssueKind::DeprecatedClass {
2763 name: s(),
2764 message: None,
2765 },
2766 IssueKind::InternalMethod {
2767 class: s(),
2768 method: s(),
2769 },
2770 IssueKind::MissingReturnType { fn_name: s() },
2771 IssueKind::MissingClosureReturnType,
2772 IssueKind::MissingParamType {
2773 fn_name: s(),
2774 param: s(),
2775 },
2776 IssueKind::MissingPropertyType {
2777 class: s(),
2778 property: s(),
2779 },
2780 IssueKind::MissingThrowsDocblock { class: s() },
2781 IssueKind::InvalidDocblock { message: s() },
2782 IssueKind::UndefinedThrowsDocblock { name: s() },
2783 IssueKind::InvalidDocblockType { message: s() },
2784 IssueKind::MixedArgument {
2785 param: s(),
2786 fn_name: s(),
2787 },
2788 IssueKind::MixedAssignment { var: s() },
2789 IssueKind::MixedMethodCall { method: s() },
2790 IssueKind::UnsupportedReferenceUsage,
2791 IssueKind::NoInterfaceProperties { property: s() },
2792 IssueKind::UndefinedDocblockClass { name: s() },
2793 IssueKind::MissingConstructor { class: s() },
2794 IssueKind::PropertyPossiblyUninitialized {
2795 class: s(),
2796 property: s(),
2797 },
2798 IssueKind::MixedFunctionCall,
2799 IssueKind::MixedReturnStatement { declared: s() },
2800 IssueKind::MixedPropertyFetch { property: s() },
2801 IssueKind::MixedPropertyAssignment { property: s() },
2802 IssueKind::MixedArrayAccess,
2803 IssueKind::MixedArrayOffset,
2804 IssueKind::MixedClone,
2805 IssueKind::InvalidClone { ty: s() },
2806 IssueKind::PossiblyInvalidClone { ty: s() },
2807 IssueKind::InvalidToString { class: s() },
2808 IssueKind::InvalidTraitUse {
2809 trait_name: s(),
2810 reason: s(),
2811 },
2812 IssueKind::ParseError { message: s() },
2813 IssueKind::InvalidThrow { ty: s() },
2814 IssueKind::InvalidCatch { ty: s() },
2815 IssueKind::UnreachableCatch {
2816 ty: s(),
2817 shadowed_by: s(),
2818 },
2819 IssueKind::ImplicitToStringCast { class: s() },
2820 IssueKind::ImplicitFloatToIntCast { from: s() },
2821 IssueKind::WrongCaseFunction {
2822 used: s(),
2823 canonical: s(),
2824 },
2825 IssueKind::WrongCaseMethod {
2826 class: s(),
2827 used: s(),
2828 canonical: s(),
2829 },
2830 IssueKind::WrongCaseClass {
2831 used: s(),
2832 canonical: s(),
2833 },
2834 IssueKind::InvalidAttribute { message: s() },
2835 IssueKind::UndefinedAttributeClass { name: s() },
2836 IssueKind::ForbiddenCode { message: s() },
2837 IssueKind::DuplicateClass { name: s() },
2838 IssueKind::DuplicateInterface { name: s() },
2839 IssueKind::DuplicateTrait { name: s() },
2840 IssueKind::DuplicateEnum { name: s() },
2841 IssueKind::DuplicateFunction { name: s() },
2842 ]
2843 }
2844
2845 #[test]
2846 fn codes_have_expected_shape() {
2847 for kind in one_of_each() {
2848 let code = kind.code();
2849 assert!(
2850 code.len() == 7
2851 && code.starts_with("MIR")
2852 && code[3..].chars().all(|c| c.is_ascii_digit()),
2853 "code {code:?} for {} does not match MIR####",
2854 kind.name(),
2855 );
2856 }
2857 }
2858
2859 #[test]
2860 fn codes_are_unique() {
2861 let kinds = one_of_each();
2862 let mut seen: HashSet<&'static str> = HashSet::new();
2863 for kind in &kinds {
2864 assert!(
2865 seen.insert(kind.code()),
2866 "duplicate code {} (variant {})",
2867 kind.code(),
2868 kind.name(),
2869 );
2870 }
2871 }
2872
2873 #[test]
2874 fn default_severity_for_code_round_trips() {
2875 for kind in one_of_each() {
2876 let code = kind.code();
2877 assert_eq!(
2878 IssueKind::default_severity_for_code(code),
2879 Some(kind.default_severity()),
2880 "severity mismatch for {code} (variant {})",
2881 kind.name(),
2882 );
2883 }
2884 }
2885
2886 #[test]
2887 fn default_severity_for_code_unknown_returns_none() {
2888 assert_eq!(IssueKind::default_severity_for_code("MIR9999"), None);
2889 assert_eq!(IssueKind::default_severity_for_code(""), None);
2890 assert_eq!(IssueKind::default_severity_for_code("mir0001"), None);
2891 }
2892
2893 #[test]
2896 fn one_of_each_has_every_variant() {
2897 assert_eq!(one_of_each().len(), 162);
2900 }
2901}