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