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)]
46pub enum CloneFailure {
47 NonObject,
48 Enum,
49 PrivateMethod {
51 owner: String,
52 scope: Option<String>,
53 },
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57#[non_exhaustive]
58pub enum IssueKind {
59 NonStaticSelfCall { class: String, method: String },
65 InvalidScope {
66 in_class: bool,
68 },
69 UndefinedVariable { name: String },
72 UndefinedFunction { name: String },
75 UndefinedMethod { class: String, method: String },
78 PossiblyUndefinedMethod { class: String, method: String },
81 UndefinedClass { name: String },
84 UndefinedProperty { class: String, property: String },
87 InaccessibleProperty { class: String, property: String },
92 UndefinedConstant { name: String },
95 InaccessibleClassConstant { class: String, constant: String },
98 PossiblyUndefinedVariable { name: String },
101 UndefinedTrait { name: String },
104 TraitConstantAccessedDirectly {
111 trait_name: String,
112 constant: String,
113 },
114 UndefinedTraitAliasMethod {
122 trait_name: Option<String>,
123 method: String,
124 },
125 ParentNotFound,
128 InvalidStringClass { actual: String },
131 NotAnInterface { name: String },
135
136 NullArgument { param: String, fn_name: String },
140 NullPropertyFetch { property: String },
143 NullMethodCall { method: String },
146 NullArrayAccess,
149 PossiblyNullArgument { param: String, fn_name: String },
152 PossiblyInvalidArgument {
155 param: String,
156 fn_name: String,
157 expected: String,
158 actual: String,
159 },
160 PossiblyNullPropertyFetch { property: String },
163 PossiblyNullMethodCall { method: String },
166 PossiblyNullArrayAccess,
169 NullableReturnStatement { expected: String, actual: String },
172
173 InvalidReturnType { expected: String, actual: String },
177 InvalidArgument {
180 param: String,
181 fn_name: String,
182 expected: String,
183 actual: String,
184 },
185 TooFewArguments {
188 fn_name: String,
189 expected: usize,
190 actual: usize,
191 },
192 TooManyArguments {
195 fn_name: String,
196 expected: usize,
197 actual: usize,
198 },
199 InvalidNamedArgument {
202 fn_name: String,
203 name: String,
204 overwrites: bool,
205 },
206 InvalidNamedArguments { fn_name: String },
209 InvalidPassByReference { fn_name: String, param: String },
212 InvalidPropertyFetch { ty: String },
215 InvalidMethodCall { method: String, ty: String },
218 InvalidUnpack {
221 ty: String,
223 },
224 InvalidArrayAccess { ty: String },
227 PossiblyInvalidArrayAccess { ty: String },
230 InvalidArrayAssignment { ty: String },
233 InvalidPropertyAssignment {
236 property: String,
237 expected: String,
238 actual: String,
239 },
240 InvalidCast { from: String, to: String },
243 InvalidStaticInvocation { class: String, method: String },
246 InvalidOperand {
250 op: String,
251 left: String,
252 right: String,
253 },
254 PossiblyInvalidOperand {
257 op: String,
258 left: String,
259 right: String,
260 },
261 PossiblyNullOperand { op: String, ty: String },
264 DivisionByZero { op: String },
268 RawObjectIteration { ty: String },
271 PossiblyRawObjectIteration { ty: String },
274 MismatchingDocblockReturnType { declared: String, inferred: String },
277 MismatchingDocblockParamType {
280 param: String,
281 declared: String,
282 inferred: String,
283 },
284 TypeCheckMismatch {
290 var: String,
291 expected: String,
292 actual: String,
293 },
294
295 Trace { variable: String, type_info: String },
298
299 InvalidArrayOffset { expected: String, actual: String },
303 NonExistentArrayOffset { key: String },
307 PossiblyInvalidArrayOffset { expected: String, actual: String },
310 DuplicateArrayKey { key: String },
315
316 RedundantCondition {
320 always_true: bool,
321 unreachable: Option<String>,
323 },
324 RedundantCast { from: String, to: String },
327 UnnecessaryVarAnnotation { var: String },
330 TypeDoesNotContainType { left: String, right: String },
333 ParadoxicalCondition { value: String },
336 DocblockTypeContradiction { expr: String, declared: String },
341 ImpossibleIdenticalComparison {
347 op: String,
348 left: String,
349 right: String,
350 },
351 ImpossibleLooseComparison {
358 op: String,
359 left: String,
360 right: String,
361 },
362 UnevaluatedCode { reason: String },
369
370 UnusedVariable { name: String },
374 UnusedParam { name: String },
377 UnreachableCode,
380 UnhandledMatchCondition { detail: String },
383 UnusedMethod { class: String, method: String },
386 UnusedProperty { class: String, property: String },
389 UnusedFunction { name: String },
392 UnusedForeachValue { name: String },
395 UnusedClass { class: String },
398 UnusedConstant { class: String, constant: String },
401 UnusedSuppress { kind: String },
405
406 ArgumentTypeCoercion {
409 param: String,
410 fn_name: String,
411 expected: String,
412 actual: String,
413 },
414
415 PropertyTypeCoercion {
418 property: String,
419 expected: String,
420 actual: String,
421 },
422
423 ImpurePropertyAssignment { property: String },
426 ImpureMethodCall { method: String },
428 ImpureGlobalVariable { variable: String },
430 ImpureStaticVariable { variable: String },
432 ImpureStaticPropertyAssignment { class: String, property: String },
436 ImpureStaticPropertyAccess { class: String, property: String },
441 ImpureByRefAssignment { variable: String },
446 ImpureFunctionCall { fn_name: String },
450 ImmutablePropertyModification { receiver: String, property: String },
456
457 ReadonlyPropertyAssignment {
461 class: String,
462 property: String,
463 unset: bool,
464 },
465 ReadonlyPropertyAlreadyInitialized { class: String, property: String },
472
473 UnimplementedAbstractMethod { class: String, method: String },
477 UnimplementedInterfaceMethod {
480 class: String,
481 interface: String,
482 method: String,
483 },
484 MethodSignatureMismatch {
487 class: String,
488 method: String,
489 detail: String,
490 },
491 OverriddenMethodAccess {
494 class: String,
495 method: String,
496 parent: String,
497 visibility: String,
498 },
499 OverriddenPropertyAccess {
502 class: String,
503 property: String,
504 parent: String,
505 visibility: String,
506 },
507 PropertyTypeRedeclarationMismatch {
510 class: String,
511 property: String,
512 parent: String,
513 expected: String,
514 covariant: bool,
516 before_php85: bool,
518 },
519 ReadonlyPropertyRedeclarationMismatch {
522 parent_class: String,
523 class: String,
524 property: String,
525 parent_readonly: bool,
528 },
529 StaticPropertyRedeclarationMismatch {
532 parent_class: String,
533 class: String,
534 property: String,
535 parent_static: bool,
538 },
539 ReadonlyClassExtendsMismatch {
545 parent: String,
546 child: String,
547 parent_readonly: bool,
550 },
551 InvalidReadonlyPropertyDeclaration { class: String, property: String },
557 BackedEnumCaseTypeMismatch {
560 enum_name: String,
561 case_name: String,
562 expected: String,
563 actual: String,
564 },
565 DuplicateEnumCaseValue {
569 enum_name: String,
570 first_case: String,
571 case_name: String,
572 },
573 DirectConstructorCall { class: String },
576 InvalidExtendClass { parent: String, child: String },
579 FinalMethodOverridden {
582 class: String,
583 method: String,
584 parent: String,
585 },
586 AbstractInstantiation { class: String },
589 AbstractMethodCall { class: String, method: String },
592 InterfaceInstantiation { class: String },
595 EnumInstantiation { class: String },
598 TraitInstantiation { class: String },
601 InvalidDynamicProperty { class: String, property: String },
605 InvalidOverride { class: String, member: String },
610
611 TaintedInput { sink: String },
615 TaintedHtml,
618 TaintedSql,
621 TaintedShell,
624 TaintedLlmPrompt,
628 TaintedHeader,
631 TaintedCookie,
634 TaintedSsrf,
637 TaintedLdap,
640 TaintedXpath,
643 TaintedCallable,
646 TaintedFile,
649 TaintedInclude,
652 TaintedEval,
655 TaintedUnserialize,
658
659 InvalidTemplateParam {
663 name: String,
664 expected_bound: String,
665 actual: String,
666 },
667 ShadowedTemplateParam { name: String },
670 IfThisIsMismatch {
675 class: String,
676 method: String,
677 expected: String,
678 actual: String,
679 },
680
681 DeprecatedCall {
685 name: String,
686 message: Option<Arc<str>>,
687 },
688 DeprecatedProperty {
691 class: String,
692 property: String,
693 message: Option<Arc<str>>,
694 },
695 DeprecatedConstant {
698 class: String,
699 constant: String,
700 message: Option<Arc<str>>,
701 },
702 OptionalParameterBeforeRequired { message: String },
707 ImplicitlyNullableParameter { message: String },
712 DynamicPropertyCreation { class: String, property: String },
716 DeprecatedInterface {
719 name: String,
720 message: Option<Arc<str>>,
721 },
722 DeprecatedTrait {
725 name: String,
726 message: Option<Arc<str>>,
727 },
728 DeprecatedMethodCall {
731 class: String,
732 method: String,
733 message: Option<Arc<str>>,
734 },
735 DeprecatedMethod {
738 class: String,
739 method: String,
740 message: Option<Arc<str>>,
741 },
742 DeprecatedClass {
745 name: String,
746 message: Option<Arc<str>>,
747 },
748 InternalMethod { class: String, method: String },
751 MissingReturnType { fn_name: String },
754 MissingClosureReturnType,
757 MissingParamType { fn_name: String, param: String },
760 MissingPropertyType { class: String, property: String },
763 InvalidThrow { ty: String },
766 InvalidCatch { ty: String },
769 UnreachableCatch { ty: String, shadowed_by: String },
774 MissingThrowsDocblock { class: String },
777 UndefinedThrowsDocblock { name: String },
782 ImplicitToStringCast { class: String },
785 ImplicitFloatToIntCast { from: String },
788 ParseError {
791 message: String,
792 code: String,
794 },
795 CompileError {
798 message: String,
799 code: String,
801 },
802 InvalidDocblock { message: String },
805 InvalidDocblockType { message: String },
812 MixedArgument { param: String, fn_name: String },
815 MixedAssignment { var: String },
818 MixedMethodCall { method: String },
821 UnsupportedReferenceUsage,
824 NoInterfaceProperties { property: String },
828 UndefinedDocblockClass { name: String },
832 MissingConstructor { class: String },
835 PropertyPossiblyUninitialized { class: String, property: String },
847 MixedFunctionCall,
850 MixedReturnStatement { declared: String },
853 MixedPropertyFetch { property: String },
856 MixedPropertyAssignment { property: String },
859 MixedArrayAccess,
862 MixedArrayOffset,
865 MixedClone,
868 InvalidClone { ty: String, reason: CloneFailure },
873 PossiblyInvalidClone { ty: String },
877 InvalidToString { class: String },
881 CircularInheritance { class: String },
884
885 InvalidTraitUse { trait_name: String, reason: String },
889 ForbiddenCode { message: String },
892
893 InvalidAttribute { message: String },
897 UndefinedAttributeClass { name: String },
900
901 WrongCaseFunction { used: String, canonical: String },
905 WrongCaseMethod {
908 class: String,
909 used: String,
910 canonical: String,
911 },
912 WrongCaseClass { used: String, canonical: String },
915 DuplicateClass { name: String },
918 DuplicateInterface { name: String },
921 DuplicateTrait { name: String },
924 DuplicateEnum { name: String },
927 DuplicateFunction { name: String },
930
931 UseStatementHasNoEffect { name: String },
937 PrivateFinalMethod { class: String, method: String },
942 NonPublicMagicMethod { class: String, method: String },
947
948 PluginIssue { name: String, message: String },
952}
953
954fn append_deprecation_message(base: String, message: &Option<Arc<str>>) -> String {
955 match message.as_deref().filter(|m| !m.is_empty()) {
956 Some(msg) => format!("{base}: {msg}"),
957 None => base,
958 }
959}
960
961impl IssueKind {
962 pub fn is_constant_condition(&self) -> bool {
964 matches!(
965 self,
966 IssueKind::RedundantCondition { .. }
967 | IssueKind::ImpossibleIdenticalComparison { .. }
968 | IssueKind::ImpossibleLooseComparison { .. }
969 | IssueKind::TypeDoesNotContainType { .. }
970 | IssueKind::DocblockTypeContradiction { .. }
971 | IssueKind::ParadoxicalCondition { .. }
972 )
973 }
974
975 pub fn default_severity(&self) -> Severity {
977 match self {
978 IssueKind::NonStaticSelfCall { .. }
980 | IssueKind::DirectConstructorCall { .. }
981 | IssueKind::InvalidScope { .. }
982 | IssueKind::UndefinedVariable { .. }
983 | IssueKind::UndefinedFunction { .. }
984 | IssueKind::UndefinedMethod { .. }
985 | IssueKind::UndefinedClass { .. }
986 | IssueKind::NotAnInterface { .. }
987 | IssueKind::UndefinedConstant { .. }
988 | IssueKind::InaccessibleClassConstant { .. }
989 | IssueKind::InaccessibleProperty { .. }
990 | IssueKind::InvalidReturnType { .. }
991 | IssueKind::InvalidArgument { .. }
992 | IssueKind::InvalidUnpack { .. }
993 | IssueKind::TooFewArguments { .. }
994 | IssueKind::TooManyArguments { .. }
995 | IssueKind::InvalidNamedArgument { .. }
996 | IssueKind::InvalidNamedArguments { .. }
997 | IssueKind::InvalidPassByReference { .. }
998 | IssueKind::InvalidThrow { .. }
999 | IssueKind::InvalidCatch { .. }
1000 | IssueKind::InvalidStaticInvocation { .. }
1001 | IssueKind::UnimplementedAbstractMethod { .. }
1002 | IssueKind::UnimplementedInterfaceMethod { .. }
1003 | IssueKind::MethodSignatureMismatch { .. }
1004 | IssueKind::InvalidExtendClass { .. }
1005 | IssueKind::FinalMethodOverridden { .. }
1006 | IssueKind::OverriddenMethodAccess { .. }
1007 | IssueKind::OverriddenPropertyAccess { .. }
1008 | IssueKind::AbstractInstantiation { .. }
1009 | IssueKind::AbstractMethodCall { .. }
1010 | IssueKind::InterfaceInstantiation { .. }
1011 | IssueKind::EnumInstantiation { .. }
1012 | IssueKind::TraitInstantiation { .. }
1013 | IssueKind::InvalidDynamicProperty { .. }
1014 | IssueKind::InvalidOverride { .. }
1015 | IssueKind::InvalidTemplateParam { .. }
1016 | IssueKind::ReadonlyPropertyAssignment { .. }
1017 | IssueKind::ReadonlyPropertyAlreadyInitialized { .. }
1018 | IssueKind::ParseError { .. }
1019 | IssueKind::CompileError { .. }
1020 | IssueKind::TaintedInput { .. }
1021 | IssueKind::TaintedHtml
1022 | IssueKind::TaintedSql
1023 | IssueKind::TaintedShell
1024 | IssueKind::TaintedLlmPrompt
1025 | IssueKind::TaintedHeader
1026 | IssueKind::TaintedCookie
1027 | IssueKind::TaintedSsrf
1028 | IssueKind::TaintedLdap
1029 | IssueKind::TaintedXpath
1030 | IssueKind::TaintedCallable
1031 | IssueKind::TaintedFile
1032 | IssueKind::TaintedInclude
1033 | IssueKind::TaintedEval
1034 | IssueKind::TaintedUnserialize
1035 | IssueKind::CircularInheritance { .. }
1036 | IssueKind::InvalidTraitUse { .. }
1037 | IssueKind::UndefinedTrait { .. }
1038 | IssueKind::TraitConstantAccessedDirectly { .. }
1039 | IssueKind::UndefinedTraitAliasMethod { .. }
1040 | IssueKind::InvalidClone { .. }
1041 | IssueKind::InvalidToString { .. }
1042 | IssueKind::TypeCheckMismatch { .. }
1043 | IssueKind::PropertyTypeRedeclarationMismatch { .. }
1044 | IssueKind::ReadonlyPropertyRedeclarationMismatch { .. }
1045 | IssueKind::StaticPropertyRedeclarationMismatch { .. }
1046 | IssueKind::ReadonlyClassExtendsMismatch { .. }
1047 | IssueKind::InvalidReadonlyPropertyDeclaration { .. }
1048 | IssueKind::BackedEnumCaseTypeMismatch { .. }
1049 | IssueKind::DuplicateEnumCaseValue { .. }
1050 | IssueKind::DivisionByZero { .. }
1051 | IssueKind::ParentNotFound => Severity::Error,
1052 IssueKind::Trace { .. } => Severity::Info,
1053
1054 IssueKind::NullArgument { .. }
1056 | IssueKind::NullPropertyFetch { .. }
1057 | IssueKind::NullMethodCall { .. }
1058 | IssueKind::NullArrayAccess
1059 | IssueKind::NullableReturnStatement { .. }
1060 | IssueKind::InvalidPropertyFetch { .. }
1061 | IssueKind::InvalidMethodCall { .. }
1062 | IssueKind::InvalidArrayAccess { .. }
1063 | IssueKind::InvalidArrayAssignment { .. }
1064 | IssueKind::InvalidPropertyAssignment { .. }
1065 | IssueKind::InvalidArrayOffset { .. }
1066 | IssueKind::NonExistentArrayOffset { .. }
1067 | IssueKind::PossiblyInvalidArrayOffset { .. }
1068 | IssueKind::UndefinedProperty { .. }
1069 | IssueKind::InvalidOperand { .. }
1070 | IssueKind::ImplicitToStringCast { .. }
1071 | IssueKind::ImplicitFloatToIntCast { .. }
1072 | IssueKind::UnusedVariable { .. }
1073 | IssueKind::UnusedForeachValue { .. }
1074 | IssueKind::ImpurePropertyAssignment { .. }
1075 | IssueKind::ImpureMethodCall { .. }
1076 | IssueKind::ImpureGlobalVariable { .. }
1077 | IssueKind::ImpureStaticVariable { .. }
1078 | IssueKind::ImpureStaticPropertyAssignment { .. }
1079 | IssueKind::ImpureStaticPropertyAccess { .. }
1080 | IssueKind::ImpureFunctionCall { .. }
1081 | IssueKind::ImpureByRefAssignment { .. }
1082 | IssueKind::ImmutablePropertyModification { .. }
1083 | IssueKind::UnsupportedReferenceUsage
1084 | IssueKind::ParadoxicalCondition { .. }
1085 | IssueKind::UnhandledMatchCondition { .. }
1086 | IssueKind::InvalidStringClass { .. }
1087 | IssueKind::ImpossibleIdenticalComparison { .. }
1088 | IssueKind::ImpossibleLooseComparison { .. }
1089 | IssueKind::DuplicateArrayKey { .. }
1090 | IssueKind::ForbiddenCode { .. }
1091 | IssueKind::UndefinedThrowsDocblock { .. }
1092 | IssueKind::InvalidDocblockType { .. }
1093 | IssueKind::UseStatementHasNoEffect { .. }
1094 | IssueKind::PrivateFinalMethod { .. }
1095 | IssueKind::NonPublicMagicMethod { .. } => Severity::Warning,
1096
1097 IssueKind::PossiblyUndefinedVariable { .. } => Severity::Warning,
1099
1100 IssueKind::PossiblyNullArgument { .. }
1102 | IssueKind::PossiblyInvalidArgument { .. }
1103 | IssueKind::PossiblyNullPropertyFetch { .. }
1104 | IssueKind::PossiblyNullMethodCall { .. }
1105 | IssueKind::PossiblyUndefinedMethod { .. }
1106 | IssueKind::PossiblyNullArrayAccess
1107 | IssueKind::PossiblyInvalidArrayAccess { .. }
1108 | IssueKind::PossiblyInvalidClone { .. }
1109 | IssueKind::PossiblyInvalidOperand { .. }
1110 | IssueKind::PossiblyNullOperand { .. }
1111 | IssueKind::PossiblyRawObjectIteration { .. } => Severity::Info,
1112
1113 IssueKind::RawObjectIteration { .. } => Severity::Warning,
1114
1115 IssueKind::RedundantCondition { .. }
1117 | IssueKind::RedundantCast { .. }
1118 | IssueKind::UnnecessaryVarAnnotation { .. }
1119 | IssueKind::TypeDoesNotContainType { .. }
1120 | IssueKind::DocblockTypeContradiction { .. }
1121 | IssueKind::UnevaluatedCode { .. }
1122 | IssueKind::IfThisIsMismatch { .. }
1123 | IssueKind::UnusedParam { .. }
1124 | IssueKind::UnreachableCode
1125 | IssueKind::UnreachableCatch { .. }
1126 | IssueKind::UnusedMethod { .. }
1127 | IssueKind::UnusedProperty { .. }
1128 | IssueKind::UnusedFunction { .. }
1129 | IssueKind::UnusedClass { .. }
1130 | IssueKind::UnusedConstant { .. }
1131 | IssueKind::UnusedSuppress { .. }
1132 | IssueKind::ArgumentTypeCoercion { .. }
1133 | IssueKind::PropertyTypeCoercion { .. }
1134 | IssueKind::DeprecatedCall { .. }
1135 | IssueKind::DeprecatedProperty { .. }
1136 | IssueKind::DeprecatedConstant { .. }
1137 | IssueKind::OptionalParameterBeforeRequired { .. }
1138 | IssueKind::ImplicitlyNullableParameter { .. }
1139 | IssueKind::DynamicPropertyCreation { .. }
1140 | IssueKind::DeprecatedInterface { .. }
1141 | IssueKind::DeprecatedTrait { .. }
1142 | IssueKind::DeprecatedMethodCall { .. }
1143 | IssueKind::DeprecatedMethod { .. }
1144 | IssueKind::DeprecatedClass { .. }
1145 | IssueKind::InternalMethod { .. }
1146 | IssueKind::MissingReturnType { .. }
1147 | IssueKind::MissingClosureReturnType
1148 | IssueKind::MissingParamType { .. }
1149 | IssueKind::MissingPropertyType { .. }
1150 | IssueKind::MismatchingDocblockReturnType { .. }
1151 | IssueKind::MismatchingDocblockParamType { .. }
1152 | IssueKind::InvalidDocblock { .. }
1153 | IssueKind::InvalidCast { .. }
1154 | IssueKind::MixedArgument { .. }
1155 | IssueKind::MixedAssignment { .. }
1156 | IssueKind::MixedMethodCall { .. }
1157 | IssueKind::NoInterfaceProperties { .. }
1158 | IssueKind::UndefinedDocblockClass { .. }
1159 | IssueKind::MissingConstructor { .. }
1160 | IssueKind::PropertyPossiblyUninitialized { .. }
1161 | IssueKind::MixedFunctionCall
1162 | IssueKind::MixedReturnStatement { .. }
1163 | IssueKind::MixedPropertyFetch { .. }
1164 | IssueKind::MixedPropertyAssignment { .. }
1165 | IssueKind::MixedArrayAccess
1166 | IssueKind::MixedArrayOffset
1167 | IssueKind::MixedClone
1168 | IssueKind::ShadowedTemplateParam { .. }
1169 | IssueKind::MissingThrowsDocblock { .. }
1170 | IssueKind::WrongCaseFunction { .. }
1171 | IssueKind::WrongCaseMethod { .. }
1172 | IssueKind::WrongCaseClass { .. }
1173 | IssueKind::InvalidAttribute { .. }
1174 | IssueKind::UndefinedAttributeClass { .. } => Severity::Info,
1175 IssueKind::DuplicateClass { .. }
1176 | IssueKind::DuplicateInterface { .. }
1177 | IssueKind::DuplicateTrait { .. }
1178 | IssueKind::DuplicateEnum { .. }
1179 | IssueKind::DuplicateFunction { .. } => Severity::Error,
1180 IssueKind::PluginIssue { .. } => Severity::Error,
1183 }
1184 }
1185
1186 pub fn code(&self) -> &'static str {
1216 match self {
1217 IssueKind::NonStaticSelfCall { .. } => "MIR0216",
1219 IssueKind::DirectConstructorCall { .. } => "MIR0217",
1220 IssueKind::InvalidScope { .. } => "MIR0001",
1221 IssueKind::UndefinedVariable { .. } => "MIR0002",
1222 IssueKind::UndefinedFunction { .. } => "MIR0003",
1223 IssueKind::UndefinedMethod { .. } => "MIR0004",
1224 IssueKind::PossiblyUndefinedMethod { .. } => "MIR0015",
1225 IssueKind::InvalidDynamicProperty { .. } => "MIR0016",
1226 IssueKind::UndefinedClass { .. } => "MIR0005",
1227 IssueKind::UndefinedProperty { .. } => "MIR0006",
1228 IssueKind::UndefinedConstant { .. } => "MIR0007",
1229 IssueKind::InaccessibleClassConstant { .. } => "MIR0011",
1230 IssueKind::InaccessibleProperty { .. } => "MIR0014",
1231 IssueKind::TraitConstantAccessedDirectly { .. } => "MIR0012",
1232 IssueKind::UndefinedTraitAliasMethod { .. } => "MIR0013",
1233 IssueKind::PossiblyUndefinedVariable { .. } => "MIR0008",
1234 IssueKind::UndefinedTrait { .. } => "MIR0009",
1235 IssueKind::ParentNotFound => "MIR0010",
1236
1237 IssueKind::NullArgument { .. } => "MIR0100",
1239 IssueKind::NullPropertyFetch { .. } => "MIR0101",
1240 IssueKind::NullMethodCall { .. } => "MIR0102",
1241 IssueKind::NullArrayAccess => "MIR0103",
1242 IssueKind::PossiblyNullArgument { .. } => "MIR0104",
1243 IssueKind::PossiblyInvalidArgument { .. } => "MIR0105",
1244 IssueKind::PossiblyNullPropertyFetch { .. } => "MIR0106",
1245 IssueKind::PossiblyNullMethodCall { .. } => "MIR0107",
1246 IssueKind::PossiblyNullArrayAccess => "MIR0108",
1247 IssueKind::NullableReturnStatement { .. } => "MIR0109",
1248
1249 IssueKind::InvalidReturnType { .. } => "MIR0200",
1251 IssueKind::InvalidArgument { .. } => "MIR0201",
1252 IssueKind::TooFewArguments { .. } => "MIR0202",
1253 IssueKind::TooManyArguments { .. } => "MIR0203",
1254 IssueKind::InvalidNamedArgument { .. } => "MIR0204",
1255 IssueKind::InvalidNamedArguments { .. } => "MIR0224",
1256 IssueKind::InvalidPassByReference { .. } => "MIR0205",
1257 IssueKind::InvalidPropertyFetch { .. } => "MIR0218",
1258 IssueKind::InvalidArrayAccess { .. } => "MIR0219",
1259 IssueKind::PossiblyInvalidArrayAccess { .. } => "MIR0227",
1260 IssueKind::InvalidArrayAssignment { .. } => "MIR0220",
1261 IssueKind::InvalidPropertyAssignment { .. } => "MIR0206",
1262 IssueKind::InvalidCast { .. } => "MIR0207",
1263 IssueKind::InvalidStaticInvocation { .. } => "MIR0215",
1264 IssueKind::InvalidOperand { .. } => "MIR0208",
1265 IssueKind::PossiblyInvalidOperand { .. } => "MIR0213",
1266 IssueKind::PossiblyNullOperand { .. } => "MIR0214",
1267 IssueKind::DivisionByZero { .. } => "MIR0229",
1268 IssueKind::InvalidMethodCall { .. } => "MIR0230",
1269 IssueKind::InvalidUnpack { .. } => "MIR0231",
1270 IssueKind::RawObjectIteration { .. } => "MIR0222",
1271 IssueKind::PossiblyRawObjectIteration { .. } => "MIR0223",
1272 IssueKind::MismatchingDocblockReturnType { .. } => "MIR0209",
1273 IssueKind::MismatchingDocblockParamType { .. } => "MIR0210",
1274 IssueKind::InvalidStringClass { .. } => "MIR0211",
1275 IssueKind::NotAnInterface { .. } => "MIR0228",
1276 IssueKind::TypeCheckMismatch { .. } => "MIR0212",
1277 IssueKind::Trace { .. } => "MIR0221",
1278 IssueKind::ArgumentTypeCoercion { .. } => "MIR0225",
1279 IssueKind::PropertyTypeCoercion { .. } => "MIR0226",
1280
1281 IssueKind::InvalidArrayOffset { .. } => "MIR0300",
1283 IssueKind::NonExistentArrayOffset { .. } => "MIR0301",
1284 IssueKind::PossiblyInvalidArrayOffset { .. } => "MIR0302",
1285 IssueKind::DuplicateArrayKey { .. } => "MIR0303",
1286
1287 IssueKind::RedundantCondition { .. } => "MIR0400",
1289 IssueKind::RedundantCast { .. } => "MIR0401",
1290 IssueKind::UnnecessaryVarAnnotation { .. } => "MIR0402",
1291 IssueKind::TypeDoesNotContainType { .. } => "MIR0403",
1292 IssueKind::ParadoxicalCondition { .. } => "MIR0404",
1293 IssueKind::UnhandledMatchCondition { .. } => "MIR0405",
1294 IssueKind::DocblockTypeContradiction { .. } => "MIR0406",
1295 IssueKind::UnevaluatedCode { .. } => "MIR0407",
1296 IssueKind::ImpossibleIdenticalComparison { .. } => "MIR0408",
1297 IssueKind::ImpossibleLooseComparison { .. } => "MIR0409",
1298
1299 IssueKind::UnusedVariable { .. } => "MIR0500",
1301 IssueKind::UnusedParam { .. } => "MIR0501",
1302 IssueKind::UnreachableCode => "MIR0502",
1303 IssueKind::UnusedMethod { .. } => "MIR0503",
1304 IssueKind::UnusedProperty { .. } => "MIR0504",
1305 IssueKind::UnusedFunction { .. } => "MIR0505",
1306 IssueKind::UnusedForeachValue { .. } => "MIR0506",
1307 IssueKind::UnusedClass { .. } => "MIR0507",
1308 IssueKind::UnusedSuppress { .. } => "MIR0508",
1309 IssueKind::UnusedConstant { .. } => "MIR0509",
1310
1311 IssueKind::ImpurePropertyAssignment { .. } => "MIR1700",
1313 IssueKind::ImpureMethodCall { .. } => "MIR1701",
1314 IssueKind::ImpureGlobalVariable { .. } => "MIR1702",
1315 IssueKind::ImpureStaticVariable { .. } => "MIR1703",
1316 IssueKind::ImpureStaticPropertyAssignment { .. } => "MIR1706",
1317 IssueKind::ImpureByRefAssignment { .. } => "MIR1707",
1318 IssueKind::ImpureStaticPropertyAccess { .. } => "MIR1708",
1319 IssueKind::ImpureFunctionCall { .. } => "MIR1704",
1320 IssueKind::ImmutablePropertyModification { .. } => "MIR1705",
1321 IssueKind::UnsupportedReferenceUsage => "MIR1506",
1322 IssueKind::NoInterfaceProperties { .. } => "MIR1504",
1323 IssueKind::UndefinedDocblockClass { .. } => "MIR1505",
1324 IssueKind::MissingConstructor { .. } => "MIR1507",
1325 IssueKind::PropertyPossiblyUninitialized { .. } => "MIR1510",
1326 IssueKind::MixedFunctionCall => "MIR1211",
1327 IssueKind::MixedReturnStatement { .. } => "MIR1212",
1328
1329 IssueKind::ReadonlyPropertyAssignment { .. } => "MIR0600",
1331 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => "MIR0601",
1332
1333 IssueKind::UnimplementedAbstractMethod { .. } => "MIR0700",
1335 IssueKind::UnimplementedInterfaceMethod { .. } => "MIR0701",
1336 IssueKind::MethodSignatureMismatch { .. } => "MIR0702",
1337 IssueKind::OverriddenMethodAccess { .. } => "MIR0703",
1338 IssueKind::OverriddenPropertyAccess { .. } => "MIR0710",
1339 IssueKind::PropertyTypeRedeclarationMismatch { .. } => "MIR0712",
1340 IssueKind::BackedEnumCaseTypeMismatch { .. } => "MIR0713",
1341 IssueKind::DuplicateEnumCaseValue { .. } => "MIR0718",
1342 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => "MIR0714",
1343 IssueKind::StaticPropertyRedeclarationMismatch { .. } => "MIR0715",
1344 IssueKind::ReadonlyClassExtendsMismatch { .. } => "MIR0716",
1345 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => "MIR0717",
1346 IssueKind::InvalidExtendClass { .. } => "MIR0704",
1347 IssueKind::FinalMethodOverridden { .. } => "MIR0705",
1348 IssueKind::AbstractInstantiation { .. } => "MIR0706",
1349 IssueKind::AbstractMethodCall { .. } => "MIR0711",
1350 IssueKind::InterfaceInstantiation { .. } => "MIR0709",
1351 IssueKind::EnumInstantiation { .. } => "MIR0719",
1352 IssueKind::TraitInstantiation { .. } => "MIR0720",
1353 IssueKind::CircularInheritance { .. } => "MIR0707",
1354 IssueKind::InvalidOverride { .. } => "MIR0708",
1355
1356 IssueKind::TaintedInput { .. } => "MIR0800",
1358 IssueKind::TaintedHtml => "MIR0801",
1359 IssueKind::TaintedSql => "MIR0802",
1360 IssueKind::TaintedShell => "MIR0803",
1361 IssueKind::TaintedLlmPrompt => "MIR0804",
1362 IssueKind::TaintedHeader => "MIR0805",
1363 IssueKind::TaintedCookie => "MIR0806",
1364 IssueKind::TaintedSsrf => "MIR0807",
1365 IssueKind::TaintedLdap => "MIR0808",
1366 IssueKind::TaintedXpath => "MIR0809",
1367 IssueKind::TaintedCallable => "MIR0810",
1368 IssueKind::TaintedFile => "MIR0811",
1369 IssueKind::TaintedInclude => "MIR0812",
1370 IssueKind::TaintedEval => "MIR0813",
1371 IssueKind::TaintedUnserialize => "MIR0814",
1372
1373 IssueKind::InvalidTemplateParam { .. } => "MIR0900",
1375 IssueKind::ShadowedTemplateParam { .. } => "MIR0901",
1376 IssueKind::IfThisIsMismatch { .. } => "MIR0902",
1377
1378 IssueKind::DeprecatedCall { .. } => "MIR1000",
1380 IssueKind::WrongCaseFunction { .. } => "MIR1009",
1381 IssueKind::WrongCaseMethod { .. } => "MIR1010",
1382 IssueKind::WrongCaseClass { .. } => "MIR1011",
1383 IssueKind::DeprecatedProperty { .. } => "MIR1005",
1384 IssueKind::DeprecatedInterface { .. } => "MIR1006",
1385 IssueKind::DeprecatedTrait { .. } => "MIR1007",
1386 IssueKind::DeprecatedConstant { .. } => "MIR1008",
1387 IssueKind::DeprecatedMethodCall { .. } => "MIR1001",
1388 IssueKind::DeprecatedMethod { .. } => "MIR1002",
1389 IssueKind::DeprecatedClass { .. } => "MIR1003",
1390 IssueKind::InternalMethod { .. } => "MIR1004",
1391 IssueKind::OptionalParameterBeforeRequired { .. } => "MIR1012",
1392 IssueKind::ImplicitlyNullableParameter { .. } => "MIR1013",
1393 IssueKind::DynamicPropertyCreation { .. } => "MIR1014",
1394
1395 IssueKind::MissingReturnType { .. } => "MIR1100",
1397 IssueKind::MissingParamType { .. } => "MIR1101",
1398 IssueKind::MissingPropertyType { .. } => "MIR1104",
1399 IssueKind::MissingClosureReturnType => "MIR1105",
1400 IssueKind::MissingThrowsDocblock { .. } => "MIR1102",
1401 IssueKind::InvalidDocblock { .. } => "MIR1103",
1402 IssueKind::UndefinedThrowsDocblock { .. } => "MIR1106",
1403 IssueKind::InvalidDocblockType { .. } => "MIR1107",
1404
1405 IssueKind::MixedArgument { .. } => "MIR1200",
1407 IssueKind::MixedAssignment { .. } => "MIR1201",
1408 IssueKind::MixedMethodCall { .. } => "MIR1202",
1409 IssueKind::MixedPropertyFetch { .. } => "MIR1203",
1410 IssueKind::MixedPropertyAssignment { .. } => "MIR1208",
1411 IssueKind::MixedArrayAccess => "MIR1209",
1412 IssueKind::MixedArrayOffset => "MIR1210",
1413 IssueKind::MixedClone => "MIR1204",
1414 IssueKind::InvalidClone { .. } => "MIR1205",
1415 IssueKind::PossiblyInvalidClone { .. } => "MIR1206",
1416 IssueKind::InvalidToString { .. } => "MIR1207",
1417
1418 IssueKind::InvalidTraitUse { .. } => "MIR1300",
1420 IssueKind::ForbiddenCode { .. } => "MIR1301",
1421
1422 IssueKind::ParseError { .. } => "MIR1400",
1424 IssueKind::CompileError { .. } => "MIR1401",
1425
1426 IssueKind::InvalidAttribute { .. } => "MIR1600",
1428 IssueKind::UndefinedAttributeClass { .. } => "MIR1601",
1429 IssueKind::DuplicateClass { .. } => "MIR1602",
1430 IssueKind::DuplicateInterface { .. } => "MIR1603",
1431 IssueKind::DuplicateTrait { .. } => "MIR1604",
1432 IssueKind::DuplicateEnum { .. } => "MIR1605",
1433 IssueKind::DuplicateFunction { .. } => "MIR1606",
1434
1435 IssueKind::InvalidThrow { .. } => "MIR1500",
1437 IssueKind::InvalidCatch { .. } => "MIR1503",
1438 IssueKind::UnreachableCatch { .. } => "MIR1508",
1439 IssueKind::PluginIssue { .. } => "MIR1509",
1440 IssueKind::ImplicitToStringCast { .. } => "MIR1501",
1441 IssueKind::ImplicitFloatToIntCast { .. } => "MIR1502",
1442 IssueKind::UseStatementHasNoEffect { .. } => "MIR1511",
1443 IssueKind::PrivateFinalMethod { .. } => "MIR1512",
1444 IssueKind::NonPublicMagicMethod { .. } => "MIR1513",
1445 }
1446 }
1447
1448 pub fn default_severity_for_code(code: &str) -> Option<Severity> {
1454 match code {
1455 "MIR0001" | "MIR0002" | "MIR0003" | "MIR0004" | "MIR0005" | "MIR0007" | "MIR0009"
1457 | "MIR0010" | "MIR0011" | "MIR0012" | "MIR0013" | "MIR0014" | "MIR0016" | "MIR0200"
1458 | "MIR0201" | "MIR0202" | "MIR0203" | "MIR0204" | "MIR0205" | "MIR0212" | "MIR0215"
1459 | "MIR0216" | "MIR0217" | "MIR0224" | "MIR0231" | "MIR0600" | "MIR0601" | "MIR0700"
1460 | "MIR0701" | "MIR0702" | "MIR0703" | "MIR0704" | "MIR0705" | "MIR0706" | "MIR0707"
1461 | "MIR0708" | "MIR0709" | "MIR0710" | "MIR0711" | "MIR0712" | "MIR0713" | "MIR0714"
1462 | "MIR0715" | "MIR0716" | "MIR0717" | "MIR0718" | "MIR0719" | "MIR0720" | "MIR0228"
1463 | "MIR0229" | "MIR0800" | "MIR0801" | "MIR0802" | "MIR0803" | "MIR0804" | "MIR0805"
1464 | "MIR0806" | "MIR0807" | "MIR0808" | "MIR0809" | "MIR0810" | "MIR0811" | "MIR0812"
1465 | "MIR0813" | "MIR0814" | "MIR0900" | "MIR1205" | "MIR1207" | "MIR1300" | "MIR1400"
1466 | "MIR1401" | "MIR1500" | "MIR1503" | "MIR1602" | "MIR1603" | "MIR1604" | "MIR1605"
1467 | "MIR1606" => Some(Severity::Error),
1468
1469 "MIR0006" | "MIR0008" | "MIR0100" | "MIR0101" | "MIR0102" | "MIR0103" | "MIR0109"
1471 | "MIR0206" | "MIR0208" | "MIR0211" | "MIR0218" | "MIR0230" | "MIR0219" | "MIR0220"
1472 | "MIR0222" | "MIR0300" | "MIR0301" | "MIR0302" | "MIR0303" | "MIR0404" | "MIR0405"
1473 | "MIR0408" | "MIR0500" | "MIR0506" | "MIR1301" | "MIR1501" | "MIR1502" | "MIR1700"
1474 | "MIR1701" | "MIR1702" | "MIR1703" | "MIR1704" | "MIR1705" | "MIR1706" | "MIR1707"
1475 | "MIR1708" | "MIR1506" | "MIR1106" | "MIR1107" | "MIR1511" | "MIR1512" | "MIR1513" => {
1476 Some(Severity::Warning)
1477 }
1478
1479 "MIR0015" | "MIR0104" | "MIR0105" | "MIR0106" | "MIR0107" | "MIR0108" | "MIR0207"
1481 | "MIR0209" | "MIR0210" | "MIR0213" | "MIR0214" | "MIR0221" | "MIR0223" | "MIR0400"
1482 | "MIR0401" | "MIR0402" | "MIR0403" | "MIR0501" | "MIR0502" | "MIR0503" | "MIR0504"
1483 | "MIR0505" | "MIR0507" | "MIR0508" | "MIR0509" | "MIR0901" | "MIR1000" | "MIR1001"
1484 | "MIR1002" | "MIR1003" | "MIR1004" | "MIR1005" | "MIR1006" | "MIR1007" | "MIR1008"
1485 | "MIR1009" | "MIR1010" | "MIR1011" | "MIR1012" | "MIR1013" | "MIR1014" | "MIR1100"
1486 | "MIR1101" | "MIR1102" | "MIR1103" | "MIR1104" | "MIR1105" | "MIR1200" | "MIR1201"
1487 | "MIR1202" | "MIR1203" | "MIR1204" | "MIR1206" | "MIR1208" | "MIR1209" | "MIR1210"
1488 | "MIR1211" | "MIR1212" | "MIR1504" | "MIR1505" | "MIR1507" | "MIR1508" | "MIR1600"
1489 | "MIR1601" | "MIR0225" | "MIR0226" | "MIR0227" | "MIR0406" | "MIR0407" | "MIR0902"
1490 | "MIR1510" => Some(Severity::Info),
1491
1492 _ => None,
1493 }
1494 }
1495
1496 pub fn name(&self) -> &'static str {
1498 match self {
1499 IssueKind::NonStaticSelfCall { .. } => "NonStaticSelfCall",
1500 IssueKind::DirectConstructorCall { .. } => "DirectConstructorCall",
1501 IssueKind::InvalidScope { .. } => "InvalidScope",
1502 IssueKind::UndefinedVariable { .. } => "UndefinedVariable",
1503 IssueKind::UndefinedFunction { .. } => "UndefinedFunction",
1504 IssueKind::UndefinedMethod { .. } => "UndefinedMethod",
1505 IssueKind::PossiblyUndefinedMethod { .. } => "PossiblyUndefinedMethod",
1506 IssueKind::UndefinedClass { .. } => "UndefinedClass",
1507 IssueKind::UndefinedProperty { .. } => "UndefinedProperty",
1508 IssueKind::UndefinedConstant { .. } => "UndefinedConstant",
1509 IssueKind::InaccessibleClassConstant { .. } => "InaccessibleClassConstant",
1510 IssueKind::InaccessibleProperty { .. } => "InaccessibleProperty",
1511 IssueKind::TraitConstantAccessedDirectly { .. } => "TraitConstantAccessedDirectly",
1512 IssueKind::UndefinedTraitAliasMethod { .. } => "UndefinedTraitAliasMethod",
1513 IssueKind::PossiblyUndefinedVariable { .. } => "PossiblyUndefinedVariable",
1514 IssueKind::UndefinedTrait { .. } => "UndefinedTrait",
1515 IssueKind::ParentNotFound => "ParentNotFound",
1516 IssueKind::InvalidStringClass { .. } => "InvalidStringClass",
1517 IssueKind::NotAnInterface { .. } => "NotAnInterface",
1518 IssueKind::NullArgument { .. } => "NullArgument",
1519 IssueKind::NullPropertyFetch { .. } => "NullPropertyFetch",
1520 IssueKind::NullMethodCall { .. } => "NullMethodCall",
1521 IssueKind::NullArrayAccess => "NullArrayAccess",
1522 IssueKind::PossiblyNullArgument { .. } => "PossiblyNullArgument",
1523 IssueKind::PossiblyInvalidArgument { .. } => "PossiblyInvalidArgument",
1524 IssueKind::PossiblyNullPropertyFetch { .. } => "PossiblyNullPropertyFetch",
1525 IssueKind::PossiblyNullMethodCall { .. } => "PossiblyNullMethodCall",
1526 IssueKind::PossiblyNullArrayAccess => "PossiblyNullArrayAccess",
1527 IssueKind::NullableReturnStatement { .. } => "NullableReturnStatement",
1528 IssueKind::InvalidReturnType { .. } => "InvalidReturnType",
1529 IssueKind::InvalidArgument { .. } => "InvalidArgument",
1530 IssueKind::TooFewArguments { .. } => "TooFewArguments",
1531 IssueKind::TooManyArguments { .. } => "TooManyArguments",
1532 IssueKind::InvalidNamedArgument { .. } => "InvalidNamedArgument",
1533 IssueKind::InvalidNamedArguments { .. } => "InvalidNamedArguments",
1534 IssueKind::InvalidPassByReference { .. } => "InvalidPassByReference",
1535 IssueKind::InvalidPropertyFetch { .. } => "InvalidPropertyFetch",
1536 IssueKind::InvalidMethodCall { .. } => "InvalidMethodCall",
1537 IssueKind::InvalidUnpack { .. } => "InvalidUnpack",
1538 IssueKind::InvalidArrayAccess { .. } => "InvalidArrayAccess",
1539 IssueKind::PossiblyInvalidArrayAccess { .. } => "PossiblyInvalidArrayAccess",
1540 IssueKind::InvalidArrayAssignment { .. } => "InvalidArrayAssignment",
1541 IssueKind::InvalidPropertyAssignment { .. } => "InvalidPropertyAssignment",
1542 IssueKind::InvalidCast { .. } => "InvalidCast",
1543 IssueKind::InvalidStaticInvocation { .. } => "InvalidStaticInvocation",
1544 IssueKind::InvalidOperand { .. } => "InvalidOperand",
1545 IssueKind::PossiblyInvalidOperand { .. } => "PossiblyInvalidOperand",
1546 IssueKind::PossiblyNullOperand { .. } => "PossiblyNullOperand",
1547 IssueKind::DivisionByZero { .. } => "DivisionByZero",
1548 IssueKind::RawObjectIteration { .. } => "RawObjectIteration",
1549 IssueKind::PossiblyRawObjectIteration { .. } => "PossiblyRawObjectIteration",
1550 IssueKind::MismatchingDocblockReturnType { .. } => "MismatchingDocblockReturnType",
1551 IssueKind::MismatchingDocblockParamType { .. } => "MismatchingDocblockParamType",
1552 IssueKind::TypeCheckMismatch { .. } => "TypeCheckMismatch",
1553 IssueKind::DocblockTypeContradiction { .. } => "DocblockTypeContradiction",
1554 IssueKind::ImpossibleIdenticalComparison { .. } => "ImpossibleIdenticalComparison",
1555 IssueKind::ImpossibleLooseComparison { .. } => "ImpossibleLooseComparison",
1556 IssueKind::UnevaluatedCode { .. } => "UnevaluatedCode",
1557 IssueKind::IfThisIsMismatch { .. } => "IfThisIsMismatch",
1558 IssueKind::Trace { .. } => "Trace",
1559 IssueKind::InvalidArrayOffset { .. } => "InvalidArrayOffset",
1560 IssueKind::NonExistentArrayOffset { .. } => "NonExistentArrayOffset",
1561 IssueKind::PossiblyInvalidArrayOffset { .. } => "PossiblyInvalidArrayOffset",
1562 IssueKind::DuplicateArrayKey { .. } => "DuplicateArrayKey",
1563 IssueKind::RedundantCondition { .. } => "RedundantCondition",
1564 IssueKind::RedundantCast { .. } => "RedundantCast",
1565 IssueKind::UnnecessaryVarAnnotation { .. } => "UnnecessaryVarAnnotation",
1566 IssueKind::TypeDoesNotContainType { .. } => "TypeDoesNotContainType",
1567 IssueKind::ParadoxicalCondition { .. } => "ParadoxicalCondition",
1568 IssueKind::UnhandledMatchCondition { .. } => "UnhandledMatchCondition",
1569 IssueKind::UnusedVariable { .. } => "UnusedVariable",
1570 IssueKind::UnusedParam { .. } => "UnusedParam",
1571 IssueKind::UnreachableCode => "UnreachableCode",
1572 IssueKind::UnusedMethod { .. } => "UnusedMethod",
1573 IssueKind::UnusedProperty { .. } => "UnusedProperty",
1574 IssueKind::UnusedFunction { .. } => "UnusedFunction",
1575 IssueKind::UnusedForeachValue { .. } => "UnusedForeachValue",
1576 IssueKind::UnusedClass { .. } => "UnusedClass",
1577 IssueKind::UnusedConstant { .. } => "UnusedConstant",
1578 IssueKind::UnusedSuppress { .. } => "UnusedSuppress",
1579 IssueKind::ArgumentTypeCoercion { .. } => "ArgumentTypeCoercion",
1580 IssueKind::PropertyTypeCoercion { .. } => "PropertyTypeCoercion",
1581 IssueKind::ImpurePropertyAssignment { .. } => "ImpurePropertyAssignment",
1582 IssueKind::ImpureMethodCall { .. } => "ImpureMethodCall",
1583 IssueKind::ImpureGlobalVariable { .. } => "ImpureGlobalVariable",
1584 IssueKind::ImpureStaticVariable { .. } => "ImpureStaticVariable",
1585 IssueKind::ImpureByRefAssignment { .. } => "ImpureByRefAssignment",
1586 IssueKind::ImpureStaticPropertyAssignment { .. } => "ImpureStaticPropertyAssignment",
1587 IssueKind::ImpureStaticPropertyAccess { .. } => "ImpureStaticPropertyAccess",
1588 IssueKind::ImpureFunctionCall { .. } => "ImpureFunctionCall",
1589 IssueKind::ImmutablePropertyModification { .. } => "ImmutablePropertyModification",
1590 IssueKind::UnsupportedReferenceUsage => "UnsupportedReferenceUsage",
1591 IssueKind::NoInterfaceProperties { .. } => "NoInterfaceProperties",
1592 IssueKind::UndefinedDocblockClass { .. } => "UndefinedDocblockClass",
1593 IssueKind::MissingConstructor { .. } => "MissingConstructor",
1594 IssueKind::PropertyPossiblyUninitialized { .. } => "PropertyPossiblyUninitialized",
1595 IssueKind::MixedFunctionCall => "MixedFunctionCall",
1596 IssueKind::MixedReturnStatement { .. } => "MixedReturnStatement",
1597 IssueKind::UnimplementedAbstractMethod { .. } => "UnimplementedAbstractMethod",
1598 IssueKind::UnimplementedInterfaceMethod { .. } => "UnimplementedInterfaceMethod",
1599 IssueKind::MethodSignatureMismatch { .. } => "MethodSignatureMismatch",
1600 IssueKind::OverriddenMethodAccess { .. } => "OverriddenMethodAccess",
1601 IssueKind::OverriddenPropertyAccess { .. } => "OverriddenPropertyAccess",
1602 IssueKind::PropertyTypeRedeclarationMismatch { .. } => {
1603 "PropertyTypeRedeclarationMismatch"
1604 }
1605 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => {
1606 "ReadonlyPropertyRedeclarationMismatch"
1607 }
1608 IssueKind::StaticPropertyRedeclarationMismatch { .. } => {
1609 "StaticPropertyRedeclarationMismatch"
1610 }
1611 IssueKind::ReadonlyClassExtendsMismatch { .. } => "ReadonlyClassExtendsMismatch",
1612 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => {
1613 "InvalidReadonlyPropertyDeclaration"
1614 }
1615 IssueKind::BackedEnumCaseTypeMismatch { .. } => "BackedEnumCaseTypeMismatch",
1616 IssueKind::DuplicateEnumCaseValue { .. } => "DuplicateEnumCaseValue",
1617 IssueKind::InvalidExtendClass { .. } => "InvalidExtendClass",
1618 IssueKind::FinalMethodOverridden { .. } => "FinalMethodOverridden",
1619 IssueKind::AbstractInstantiation { .. } => "AbstractInstantiation",
1620 IssueKind::AbstractMethodCall { .. } => "AbstractMethodCall",
1621 IssueKind::InterfaceInstantiation { .. } => "InterfaceInstantiation",
1622 IssueKind::EnumInstantiation { .. } => "EnumInstantiation",
1623 IssueKind::TraitInstantiation { .. } => "TraitInstantiation",
1624 IssueKind::InvalidDynamicProperty { .. } => "InvalidDynamicProperty",
1625 IssueKind::InvalidOverride { .. } => "InvalidOverride",
1626 IssueKind::ReadonlyPropertyAssignment { .. } => "ReadonlyPropertyAssignment",
1627 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => {
1628 "ReadonlyPropertyAlreadyInitialized"
1629 }
1630 IssueKind::InvalidTemplateParam { .. } => "InvalidTemplateParam",
1631 IssueKind::ShadowedTemplateParam { .. } => "ShadowedTemplateParam",
1632 IssueKind::TaintedInput { .. } => "TaintedInput",
1633 IssueKind::TaintedHtml => "TaintedHtml",
1634 IssueKind::TaintedSql => "TaintedSql",
1635 IssueKind::TaintedShell => "TaintedShell",
1636 IssueKind::TaintedLlmPrompt => "TaintedLlmPrompt",
1637 IssueKind::TaintedHeader => "TaintedHeader",
1638 IssueKind::TaintedCookie => "TaintedCookie",
1639 IssueKind::TaintedSsrf => "TaintedSsrf",
1640 IssueKind::TaintedLdap => "TaintedLdap",
1641 IssueKind::TaintedXpath => "TaintedXpath",
1642 IssueKind::TaintedCallable => "TaintedCallable",
1643 IssueKind::TaintedFile => "TaintedFile",
1644 IssueKind::TaintedInclude => "TaintedInclude",
1645 IssueKind::TaintedEval => "TaintedEval",
1646 IssueKind::TaintedUnserialize => "TaintedUnserialize",
1647 IssueKind::DeprecatedCall { .. } => "DeprecatedCall",
1648 IssueKind::DeprecatedProperty { .. } => "DeprecatedProperty",
1649 IssueKind::DeprecatedConstant { .. } => "DeprecatedConstant",
1650 IssueKind::DeprecatedInterface { .. } => "DeprecatedInterface",
1651 IssueKind::DeprecatedTrait { .. } => "DeprecatedTrait",
1652 IssueKind::DeprecatedMethodCall { .. } => "DeprecatedMethodCall",
1653 IssueKind::DeprecatedMethod { .. } => "DeprecatedMethod",
1654 IssueKind::DeprecatedClass { .. } => "DeprecatedClass",
1655 IssueKind::InternalMethod { .. } => "InternalMethod",
1656 IssueKind::OptionalParameterBeforeRequired { .. } => "OptionalParameterBeforeRequired",
1657 IssueKind::ImplicitlyNullableParameter { .. } => "ImplicitlyNullableParameter",
1658 IssueKind::DynamicPropertyCreation { .. } => "DynamicPropertyCreation",
1659 IssueKind::MissingReturnType { .. } => "MissingReturnType",
1660 IssueKind::MissingClosureReturnType => "MissingClosureReturnType",
1661 IssueKind::MissingParamType { .. } => "MissingParamType",
1662 IssueKind::MissingPropertyType { .. } => "MissingPropertyType",
1663 IssueKind::InvalidThrow { .. } => "InvalidThrow",
1664 IssueKind::InvalidCatch { .. } => "InvalidCatch",
1665 IssueKind::UnreachableCatch { .. } => "UnreachableCatch",
1666 IssueKind::MissingThrowsDocblock { .. } => "MissingThrowsDocblock",
1667 IssueKind::ImplicitToStringCast { .. } => "ImplicitToStringCast",
1668 IssueKind::ImplicitFloatToIntCast { .. } => "ImplicitFloatToIntCast",
1669 IssueKind::ParseError { .. } => "ParseError",
1670 IssueKind::CompileError { .. } => "CompileError",
1671 IssueKind::InvalidDocblock { .. } => "InvalidDocblock",
1672 IssueKind::UndefinedThrowsDocblock { .. } => "UndefinedThrowsDocblock",
1673 IssueKind::InvalidDocblockType { .. } => "InvalidDocblockType",
1674 IssueKind::MixedArgument { .. } => "MixedArgument",
1675 IssueKind::MixedAssignment { .. } => "MixedAssignment",
1676 IssueKind::MixedMethodCall { .. } => "MixedMethodCall",
1677 IssueKind::MixedPropertyFetch { .. } => "MixedPropertyFetch",
1678 IssueKind::MixedPropertyAssignment { .. } => "MixedPropertyAssignment",
1679 IssueKind::MixedArrayAccess => "MixedArrayAccess",
1680 IssueKind::MixedArrayOffset => "MixedArrayOffset",
1681 IssueKind::MixedClone => "MixedClone",
1682 IssueKind::InvalidClone { .. } => "InvalidClone",
1683 IssueKind::PossiblyInvalidClone { .. } => "PossiblyInvalidClone",
1684 IssueKind::InvalidToString { .. } => "InvalidToString",
1685 IssueKind::CircularInheritance { .. } => "CircularInheritance",
1686 IssueKind::InvalidTraitUse { .. } => "InvalidTraitUse",
1687 IssueKind::ForbiddenCode { .. } => "ForbiddenCode",
1688 IssueKind::WrongCaseFunction { .. } => "WrongCaseFunction",
1689 IssueKind::WrongCaseMethod { .. } => "WrongCaseMethod",
1690 IssueKind::WrongCaseClass { .. } => "WrongCaseClass",
1691 IssueKind::InvalidAttribute { .. } => "InvalidAttribute",
1692 IssueKind::UndefinedAttributeClass { .. } => "UndefinedAttributeClass",
1693 IssueKind::DuplicateClass { .. } => "DuplicateClass",
1694 IssueKind::DuplicateInterface { .. } => "DuplicateInterface",
1695 IssueKind::DuplicateTrait { .. } => "DuplicateTrait",
1696 IssueKind::DuplicateEnum { .. } => "DuplicateEnum",
1697 IssueKind::DuplicateFunction { .. } => "DuplicateFunction",
1698 IssueKind::UseStatementHasNoEffect { .. } => "UseStatementHasNoEffect",
1699 IssueKind::PrivateFinalMethod { .. } => "PrivateFinalMethod",
1700 IssueKind::NonPublicMagicMethod { .. } => "NonPublicMagicMethod",
1701 IssueKind::PluginIssue { .. } => "PluginIssue",
1702 }
1703 }
1704
1705 pub fn referenced_symbol(&self) -> Option<String> {
1707 use IssueKind::*;
1708 Some(match self {
1709 UndefinedClass { name }
1710 | UndefinedFunction { name }
1711 | UndefinedConstant { name }
1712 | UndefinedTrait { name }
1713 | UnusedFunction { name }
1714 | DeprecatedCall { name, .. }
1715 | DeprecatedClass { name, .. }
1716 | DeprecatedInterface { name, .. }
1717 | DeprecatedTrait { name, .. } => name.clone(),
1718 UnusedClass { class } => class.clone(),
1719 UndefinedMethod { class, method }
1720 | PossiblyUndefinedMethod { class, method }
1721 | UnusedMethod { class, method }
1722 | InternalMethod { class, method }
1723 | DeprecatedMethod { class, method, .. }
1724 | DeprecatedMethodCall { class, method, .. } => format!("{class}::{method}"),
1725 UndefinedProperty { class, property }
1726 | InaccessibleProperty { class, property }
1727 | UnusedProperty { class, property }
1728 | DeprecatedProperty {
1729 class, property, ..
1730 } => format!("{class}::${property}"),
1731 InaccessibleClassConstant { class, constant }
1732 | UnusedConstant { class, constant }
1733 | DeprecatedConstant {
1734 class, constant, ..
1735 } => format!("{class}::{constant}"),
1736 UndefinedVariable { name }
1737 | PossiblyUndefinedVariable { name }
1738 | UnusedVariable { name } => format!("${name}"),
1739 _ => return None,
1740 })
1741 }
1742
1743 pub fn display_name(&self) -> &str {
1747 match self {
1748 IssueKind::PluginIssue { name, .. } => name,
1749 other => other.name(),
1750 }
1751 }
1752
1753 pub fn message(&self) -> String {
1755 match self {
1756 IssueKind::NonStaticSelfCall { class, method } => {
1757 format!("Non-static method {class}::{method}() cannot be called statically")
1758 }
1759 IssueKind::DirectConstructorCall { class } => {
1760 format!("Cannot call constructor of {class} directly")
1761 }
1762 IssueKind::InvalidScope { in_class } => {
1763 if *in_class {
1764 "$this cannot be used in a static method".to_string()
1765 } else {
1766 "$this cannot be used outside of a class".to_string()
1767 }
1768 }
1769 IssueKind::UndefinedVariable { name } => format!("Variable ${name} is not defined"),
1770 IssueKind::UndefinedFunction { name } => format!("Function {name}() is not defined"),
1771 IssueKind::UndefinedMethod { class, method } => {
1772 format!("Method {class}::{method}() does not exist")
1773 }
1774 IssueKind::PossiblyUndefinedMethod { class, method } => {
1775 format!("Method {class}::{method}() might not exist")
1776 }
1777 IssueKind::UndefinedClass { name } => format!("Class {name} does not exist"),
1778 IssueKind::UndefinedProperty { class, property } => {
1779 format!("Property {class}::${property} does not exist")
1780 }
1781 IssueKind::UndefinedConstant { name } => format!("Constant {name} is not defined"),
1782 IssueKind::InaccessibleClassConstant { class, constant } => {
1783 format!("Cannot access constant {class}::{constant}")
1784 }
1785 IssueKind::InaccessibleProperty { class, property } => {
1786 format!("Cannot access property {class}::${property}")
1787 }
1788 IssueKind::PossiblyUndefinedVariable { name } => {
1789 format!("Variable ${name} might not be defined")
1790 }
1791 IssueKind::UndefinedTrait { name } => format!("Trait {name} does not exist"),
1792 IssueKind::TraitConstantAccessedDirectly {
1793 trait_name,
1794 constant,
1795 } => {
1796 format!("Cannot access trait constant {trait_name}::{constant} directly")
1797 }
1798 IssueKind::UndefinedTraitAliasMethod { trait_name, method } => match trait_name {
1799 Some(t) => {
1800 format!("An alias was defined for {t}::{method} but this method does not exist")
1801 }
1802 None => format!("An alias was defined for {method} but this method does not exist"),
1803 },
1804 IssueKind::ParentNotFound => {
1805 "Cannot use parent:: when current class has no parent".to_string()
1806 }
1807 IssueKind::InvalidStringClass { actual } => {
1808 format!("Dynamic class instantiation requires string or class-string type, got '{actual}'")
1809 }
1810 IssueKind::NotAnInterface { name } => {
1811 format!("{name} is not an interface")
1812 }
1813
1814 IssueKind::NullArgument { param, fn_name } => {
1815 format!("Argument ${param} of {fn_name}() cannot be null")
1816 }
1817 IssueKind::NullPropertyFetch { property } => {
1818 format!("Cannot access property ${property} on null")
1819 }
1820 IssueKind::NullMethodCall { method } => {
1821 format!("Cannot call method {method}() on null")
1822 }
1823 IssueKind::NullArrayAccess => "Cannot access array on null".to_string(),
1824 IssueKind::PossiblyNullArgument { param, fn_name } => {
1825 format!("Argument ${param} of {fn_name}() might be null")
1826 }
1827 IssueKind::PossiblyInvalidArgument {
1828 param,
1829 fn_name,
1830 expected,
1831 actual,
1832 } => {
1833 format!("Argument ${param} of {fn_name}() expects '{expected}', possibly different type '{actual}' provided")
1834 }
1835 IssueKind::PossiblyNullPropertyFetch { property } => {
1836 format!("Cannot access property ${property} on possibly null value")
1837 }
1838 IssueKind::PossiblyNullMethodCall { method } => {
1839 format!("Cannot call method {method}() on possibly null value")
1840 }
1841 IssueKind::PossiblyNullArrayAccess => {
1842 "Cannot access array on possibly null value".to_string()
1843 }
1844 IssueKind::NullableReturnStatement { expected, actual } => {
1845 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1846 }
1847
1848 IssueKind::InvalidReturnType { expected, actual } => {
1849 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1850 }
1851 IssueKind::InvalidArgument {
1852 param,
1853 fn_name,
1854 expected,
1855 actual,
1856 } => {
1857 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}'")
1858 }
1859 IssueKind::TooFewArguments {
1860 fn_name,
1861 expected,
1862 actual,
1863 } => {
1864 format!(
1865 "Too few arguments for {}(): expected {}, got {}",
1866 fn_name, expected, actual
1867 )
1868 }
1869 IssueKind::TooManyArguments {
1870 fn_name,
1871 expected,
1872 actual,
1873 } => {
1874 format!(
1875 "Too many arguments for {}(): expected {}, got {}",
1876 fn_name, expected, actual
1877 )
1878 }
1879 IssueKind::InvalidNamedArgument {
1880 fn_name,
1881 name,
1882 overwrites: true,
1883 } => {
1884 format!(
1885 "{}() argument ${} overwrites a previous argument",
1886 fn_name, name
1887 )
1888 }
1889 IssueKind::InvalidNamedArgument { fn_name, name, .. } => {
1890 format!("{}() has no parameter named ${}", fn_name, name)
1891 }
1892 IssueKind::InvalidNamedArguments { fn_name } => {
1893 format!("{}() does not accept named arguments", fn_name)
1894 }
1895 IssueKind::InvalidPassByReference { fn_name, param } => {
1896 format!(
1897 "Argument ${} of {}() must be passed by reference",
1898 param, fn_name
1899 )
1900 }
1901 IssueKind::InvalidPropertyFetch { ty } => {
1902 format!("Cannot fetch property on non-object type '{ty}'")
1903 }
1904 IssueKind::InvalidMethodCall { method, ty } => {
1905 format!("Cannot call method {method}() on non-object type '{ty}'")
1906 }
1907 IssueKind::InvalidUnpack { ty } if ty.is_empty() => {
1908 "Only arrays and Traversables can be unpacked".to_string()
1909 }
1910 IssueKind::InvalidUnpack { ty } => {
1911 format!("Only arrays and Traversables can be unpacked, {ty} given")
1912 }
1913 IssueKind::InvalidArrayAccess { ty } => {
1914 format!("Cannot use [] operator on non-array type '{ty}'")
1915 }
1916 IssueKind::PossiblyInvalidArrayAccess { ty } => {
1917 format!("Possibly invalid array access: '{ty}' might not support []")
1918 }
1919 IssueKind::InvalidArrayAssignment { ty } => {
1920 format!("Cannot use [] assignment on non-array type '{ty}'")
1921 }
1922 IssueKind::InvalidPropertyAssignment {
1923 property,
1924 expected,
1925 actual,
1926 } => {
1927 format!("Property ${property} expects '{expected}', cannot assign '{actual}'")
1928 }
1929 IssueKind::InvalidCast { from, to } => {
1930 format!("Cannot cast '{from}' to '{to}'")
1931 }
1932 IssueKind::InvalidStaticInvocation { class, method } => {
1933 format!("Non-static method {class}::{method}() cannot be called statically")
1934 }
1935 IssueKind::InvalidOperand { op, left, right } => {
1936 if right.is_empty() {
1937 format!("Operator '{op}' not supported for operand of type '{left}'")
1938 } else {
1939 format!("Operator '{op}' not supported between '{left}' and '{right}'")
1940 }
1941 }
1942 IssueKind::PossiblyInvalidOperand { op, left, right } => {
1943 format!("Operator '{op}' might not be supported between '{left}' and '{right}'")
1944 }
1945 IssueKind::PossiblyNullOperand { op, ty } => {
1946 format!("Operator '{op}' operand '{ty}' might be null")
1947 }
1948 IssueKind::DivisionByZero { op } => {
1949 format!("Division by zero: right operand of '{op}' is always 0")
1950 }
1951 IssueKind::RawObjectIteration { ty } => {
1952 format!("Cannot iterate over non-iterable object '{ty}'")
1953 }
1954 IssueKind::PossiblyRawObjectIteration { ty } => {
1955 format!("Cannot iterate over possibly non-iterable object '{ty}'")
1956 }
1957 IssueKind::MismatchingDocblockReturnType { declared, inferred } => {
1958 format!("Docblock return type '{declared}' does not match inferred '{inferred}'")
1959 }
1960 IssueKind::MismatchingDocblockParamType {
1961 param,
1962 declared,
1963 inferred,
1964 } => {
1965 format!(
1966 "Docblock type '{declared}' for ${param} does not match inferred '{inferred}'"
1967 )
1968 }
1969 IssueKind::TypeCheckMismatch {
1970 var,
1971 expected,
1972 actual,
1973 } => {
1974 format!("Type of {var} is expected to be {expected}, got {actual}")
1975 }
1976 IssueKind::Trace {
1977 variable,
1978 type_info,
1979 } => {
1980 format!("Type of ${variable} is {type_info}")
1981 }
1982
1983 IssueKind::InvalidArrayOffset { expected, actual } => {
1984 format!("Array offset expects '{expected}', got '{actual}'")
1985 }
1986 IssueKind::NonExistentArrayOffset { key } => {
1987 format!("Array offset '{key}' does not exist")
1988 }
1989 IssueKind::PossiblyInvalidArrayOffset { expected, actual } => {
1990 format!("Array offset might be invalid: expects '{expected}', got '{actual}'")
1991 }
1992 IssueKind::DuplicateArrayKey { key } => {
1993 format!("Array key {key} is duplicated — the earlier entry is silently overwritten")
1994 }
1995
1996 IssueKind::RedundantCondition {
1997 always_true,
1998 unreachable,
1999 } => match unreachable {
2000 Some(branch) => {
2001 format!("Condition is always {always_true}, so the {branch} is never reached")
2002 }
2003 None => format!("Condition is always {always_true}, so the check is redundant"),
2004 },
2005 IssueKind::RedundantCast { from, to } => {
2006 format!("Casting '{from}' to '{to}' is redundant")
2007 }
2008 IssueKind::UnnecessaryVarAnnotation { var } => {
2009 format!("@var annotation for ${var} is unnecessary")
2010 }
2011 IssueKind::TypeDoesNotContainType { left, right } => {
2012 format!("Type '{left}' can never contain type '{right}'")
2013 }
2014 IssueKind::ParadoxicalCondition { value } => {
2015 format!("Value {value} is duplicated; this branch can never be reached")
2016 }
2017 IssueKind::UnhandledMatchCondition { detail } => {
2018 format!("Unhandled match condition: {detail}")
2019 }
2020 IssueKind::DocblockTypeContradiction { expr, declared } => {
2021 format!("Type '{declared}' makes '{expr}' impossible — this can never hold")
2022 }
2023 IssueKind::ImpossibleIdenticalComparison { op, left, right } => {
2024 let result = if op == "===" { "false" } else { "true" };
2025 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be identical")
2026 }
2027 IssueKind::ImpossibleLooseComparison { op, left, right } => {
2028 let result = if op == "==" { "false" } else { "true" };
2029 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be loosely equal")
2030 }
2031 IssueKind::UnevaluatedCode { reason } => reason.clone(),
2032 IssueKind::IfThisIsMismatch {
2033 class,
2034 method,
2035 expected,
2036 actual,
2037 } => {
2038 format!(
2039 "Cannot call {class}::{method}() — @if-this-is requires $this to be '{expected}', but it is '{actual}'"
2040 )
2041 }
2042
2043 IssueKind::UnusedVariable { name } => format!("Variable ${name} is never read"),
2044 IssueKind::UnusedParam { name } => format!("Parameter ${name} is never used"),
2045 IssueKind::UnreachableCode => "Unreachable code detected".to_string(),
2046 IssueKind::UnusedMethod { class, method } => {
2047 format!("Private method {class}::{method}() is never called")
2048 }
2049 IssueKind::UnusedProperty { class, property } => {
2050 format!("Private property {class}::${property} is never read")
2051 }
2052 IssueKind::UnusedFunction { name } => {
2053 format!("Function {name}() is never called")
2054 }
2055 IssueKind::UnusedForeachValue { name } => {
2056 format!("Foreach value ${name} is never read")
2057 }
2058 IssueKind::UnusedClass { class } => {
2059 format!("Class {class} is never referenced")
2060 }
2061 IssueKind::UnusedConstant { class, constant } => {
2062 format!("Private constant {class}::{constant} is never used")
2063 }
2064 IssueKind::UnusedSuppress { kind } => {
2065 format!("Suppress annotation for '{kind}' is never used")
2066 }
2067 IssueKind::ArgumentTypeCoercion {
2068 param,
2069 fn_name,
2070 expected,
2071 actual,
2072 } => {
2073 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}' — coercion may fail at runtime")
2074 }
2075 IssueKind::PropertyTypeCoercion {
2076 property,
2077 expected,
2078 actual,
2079 } => {
2080 format!("Property ${property} expects '{expected}', cannot assign '{actual}' — coercion may fail at runtime")
2081 }
2082 IssueKind::ImpurePropertyAssignment { property } => {
2083 format!("Assigning to property {property} of a parameter in a pure or external-mutation-free context")
2084 }
2085 IssueKind::ImpureMethodCall { method } => {
2086 format!("Calling impure method {method}() in a pure or immutable context")
2087 }
2088 IssueKind::ImpureGlobalVariable { variable } => {
2089 format!("Using global variable ${variable} in a @pure function")
2090 }
2091 IssueKind::ImpureStaticVariable { variable } => {
2092 format!("Using static variable ${variable} in a @pure function")
2093 }
2094 IssueKind::ImpureByRefAssignment { variable } => {
2095 format!("Assigning to by-reference parameter ${variable} in a @pure function")
2096 }
2097 IssueKind::ImpureStaticPropertyAssignment { class, property } => {
2098 format!("Assigning to static property {class}::${property} in a @pure function")
2099 }
2100 IssueKind::ImpureStaticPropertyAccess { class, property } => {
2101 format!("Reading static property {class}::${property} in a @pure function")
2102 }
2103 IssueKind::ImpureFunctionCall { fn_name } => {
2104 format!("Calling impure function {fn_name}() in a @pure function")
2105 }
2106 IssueKind::ImmutablePropertyModification { receiver, property } => {
2107 format!("Assigning to property {property} of {receiver} in an immutable context (@psalm-immutable class or @psalm-mutation-free method)")
2108 }
2109
2110 IssueKind::UnimplementedAbstractMethod { class, method } => {
2111 format!("Class {class} must implement abstract method {method}()")
2112 }
2113 IssueKind::UnimplementedInterfaceMethod {
2114 class,
2115 interface,
2116 method,
2117 } => {
2118 format!("Class {class} must implement {interface}::{method}() from interface")
2119 }
2120 IssueKind::MethodSignatureMismatch {
2121 class,
2122 method,
2123 detail,
2124 } => {
2125 format!("Method {class}::{method}() signature mismatch: {detail}")
2126 }
2127 IssueKind::OverriddenMethodAccess {
2128 class,
2129 method,
2130 parent,
2131 visibility,
2132 } => {
2133 let weaker = if visibility == "public" {
2134 ""
2135 } else {
2136 " or weaker"
2137 };
2138 format!("Access level to {class}::{method}() must be {visibility} (as in class {parent}){weaker}")
2139 }
2140 IssueKind::OverriddenPropertyAccess {
2141 class,
2142 property,
2143 parent,
2144 visibility,
2145 } => {
2146 let weaker = if visibility == "public" {
2147 ""
2148 } else {
2149 " or weaker"
2150 };
2151 format!("Access level to {class}::${property} must be {visibility} (as in class {parent}){weaker}")
2152 }
2153 IssueKind::PropertyTypeRedeclarationMismatch {
2154 class,
2155 property,
2156 parent,
2157 expected,
2158 covariant,
2159 before_php85,
2160 } => {
2161 if expected.is_empty() && *before_php85 {
2162 format!(
2163 "Type of {class}::${property} must not be defined (as in class {parent})"
2164 )
2165 } else if expected.is_empty() {
2166 format!(
2167 "Type of {class}::${property} must be omitted to match the parent definition in class {parent}"
2168 )
2169 } else {
2170 let subtype = if *covariant { "subtype of " } else { "" };
2171 format!(
2172 "Type of {class}::${property} must be {subtype}{expected} (as in class {parent})"
2173 )
2174 }
2175 }
2176 IssueKind::ReadonlyPropertyRedeclarationMismatch {
2177 parent_class,
2178 class,
2179 property,
2180 parent_readonly,
2181 } => {
2182 if *parent_readonly {
2183 format!(
2184 "Cannot redeclare readonly property {parent_class}::${property} as non-readonly {class}::${property}"
2185 )
2186 } else {
2187 format!(
2188 "Cannot redeclare non-readonly property {parent_class}::${property} as readonly {class}::${property}"
2189 )
2190 }
2191 }
2192 IssueKind::StaticPropertyRedeclarationMismatch {
2193 parent_class,
2194 class,
2195 property,
2196 parent_static,
2197 } => {
2198 if *parent_static {
2199 format!(
2200 "Cannot redeclare static property {parent_class}::${property} as non-static {class}::${property}"
2201 )
2202 } else {
2203 format!(
2204 "Cannot redeclare non-static property {parent_class}::${property} as static {class}::${property}"
2205 )
2206 }
2207 }
2208 IssueKind::BackedEnumCaseTypeMismatch {
2209 expected, actual, ..
2210 } => format!("Enum case type {actual} does not match enum backing type {expected}"),
2211 IssueKind::DuplicateEnumCaseValue {
2212 enum_name,
2213 first_case,
2214 case_name,
2215 } => {
2216 format!(
2217 "Duplicate value in enum {enum_name} for cases {first_case} and {case_name}"
2218 )
2219 }
2220 IssueKind::ReadonlyClassExtendsMismatch {
2221 parent,
2222 child,
2223 parent_readonly,
2224 } => {
2225 if *parent_readonly {
2226 format!("Non-readonly class {child} cannot extend readonly class {parent}")
2227 } else {
2228 format!("Readonly class {child} cannot extend non-readonly class {parent}")
2229 }
2230 }
2231 IssueKind::InvalidReadonlyPropertyDeclaration { class, property } => {
2232 format!("Readonly property {class}::${property} cannot have default value")
2233 }
2234 IssueKind::ReadonlyPropertyAssignment {
2235 class,
2236 property,
2237 unset: true,
2238 } => format!("Cannot unset readonly property {class}::${property}"),
2239 IssueKind::ReadonlyPropertyAssignment {
2240 class, property, ..
2241 } => {
2242 format!(
2243 "Cannot assign to readonly property {class}::${property} outside of constructor"
2244 )
2245 }
2246 IssueKind::ReadonlyPropertyAlreadyInitialized { class, property } => {
2247 format!(
2248 "Cannot modify readonly property {class}::${property} — already initialized"
2249 )
2250 }
2251 IssueKind::InvalidExtendClass { parent, child } => {
2252 format!("Class {child} cannot extend final class {parent}")
2253 }
2254 IssueKind::InvalidTemplateParam {
2255 name,
2256 expected_bound,
2257 actual,
2258 } => {
2259 if actual.split('|').any(|part| part == "false") {
2260 format!(
2261 "Template type '{name}' inferred as '{actual}' can be false and therefore does not satisfy bound '{expected_bound}'"
2262 )
2263 } else {
2264 format!(
2265 "Template type '{name}' inferred as '{actual}' does not satisfy bound '{expected_bound}'"
2266 )
2267 }
2268 }
2269 IssueKind::ShadowedTemplateParam { name } => {
2270 format!(
2271 "Method template parameter '{name}' shadows class-level template parameter with the same name"
2272 )
2273 }
2274 IssueKind::FinalMethodOverridden {
2275 class,
2276 method,
2277 parent,
2278 } => {
2279 format!("Method {class}::{method}() cannot override final method from {parent}")
2280 }
2281 IssueKind::AbstractInstantiation { class } => {
2282 format!("Cannot instantiate abstract class {class}")
2283 }
2284 IssueKind::AbstractMethodCall { class, method } => {
2285 format!("Cannot call abstract method {class}::{method}()")
2286 }
2287 IssueKind::InterfaceInstantiation { class } => {
2288 format!("Cannot instantiate interface {class}")
2289 }
2290 IssueKind::EnumInstantiation { class } => format!("Cannot instantiate enum {class}"),
2291 IssueKind::TraitInstantiation { class } => format!("Cannot instantiate trait {class}"),
2292 IssueKind::InvalidDynamicProperty { class, property } => {
2293 format!("Cannot create dynamic property {class}::${property}")
2294 }
2295 IssueKind::DynamicPropertyCreation { class, property } => {
2296 format!("Creation of dynamic property {class}::${property} is deprecated")
2297 }
2298 IssueKind::InvalidOverride { class, member } => {
2299 let kind = if member.starts_with('$') {
2300 "property"
2301 } else {
2302 "method"
2303 };
2304 format!(
2305 "{class}::{member} has #[\\Override] attribute, but no matching parent {kind} exists"
2306 )
2307 }
2308
2309 IssueKind::TaintedInput { sink } => format!("Tainted input reaching sink '{sink}'"),
2310 IssueKind::TaintedHtml => "Tainted HTML output — possible XSS".to_string(),
2311 IssueKind::TaintedSql => "Tainted SQL query — possible SQL injection".to_string(),
2312 IssueKind::TaintedShell => {
2313 "Tainted shell command — possible command injection".to_string()
2314 }
2315 IssueKind::TaintedLlmPrompt => {
2316 "Tainted LLM prompt — possible prompt injection".to_string()
2317 }
2318 IssueKind::TaintedHeader => {
2319 "Tainted HTTP header — possible header injection or open redirect".to_string()
2320 }
2321 IssueKind::TaintedCookie => "Tainted cookie — possible cookie injection".to_string(),
2322 IssueKind::TaintedSsrf => {
2323 "Tainted URL — possible server-side request forgery".to_string()
2324 }
2325 IssueKind::TaintedLdap => "Tainted LDAP query — possible LDAP injection".to_string(),
2326 IssueKind::TaintedXpath => "Tainted XPath query — possible XPath injection".to_string(),
2327 IssueKind::TaintedCallable => {
2328 "Tainted callable — user input chooses the code that runs".to_string()
2329 }
2330 IssueKind::TaintedFile => "Tainted file path — possible path traversal".to_string(),
2331 IssueKind::TaintedInclude => {
2332 "Tainted include path — possible file inclusion".to_string()
2333 }
2334 IssueKind::TaintedEval => "Tainted eval() code — possible code injection".to_string(),
2335 IssueKind::TaintedUnserialize => {
2336 "Tainted unserialize() payload — possible object injection".to_string()
2337 }
2338
2339 IssueKind::DeprecatedCall { name, message } => {
2340 let base = format!("Call to deprecated function {name}");
2341 append_deprecation_message(base, message)
2342 }
2343 IssueKind::DeprecatedProperty {
2344 class,
2345 property,
2346 message,
2347 } => {
2348 let base = format!("Property {class}::${property} is deprecated");
2349 append_deprecation_message(base, message)
2350 }
2351 IssueKind::DeprecatedConstant {
2352 class,
2353 constant,
2354 message,
2355 } => {
2356 let base = format!("Constant {class}::{constant} is deprecated");
2357 append_deprecation_message(base, message)
2358 }
2359 IssueKind::OptionalParameterBeforeRequired { message }
2360 | IssueKind::ImplicitlyNullableParameter { message } => message.clone(),
2361 IssueKind::DeprecatedInterface { name, message } => {
2362 let base = format!("Interface {name} is deprecated");
2363 append_deprecation_message(base, message)
2364 }
2365 IssueKind::DeprecatedTrait { name, message } => {
2366 let base = format!("Trait {name} is deprecated");
2367 append_deprecation_message(base, message)
2368 }
2369 IssueKind::DeprecatedMethodCall {
2370 class,
2371 method,
2372 message,
2373 } => {
2374 let base = format!("Call to deprecated method {class}::{method}");
2375 append_deprecation_message(base, message)
2376 }
2377 IssueKind::DeprecatedMethod {
2378 class,
2379 method,
2380 message,
2381 } => {
2382 let base = format!("Method {class}::{method}() is deprecated");
2383 append_deprecation_message(base, message)
2384 }
2385 IssueKind::DeprecatedClass { name, message } => {
2386 let base = format!("Class {name} is deprecated");
2387 append_deprecation_message(base, message)
2388 }
2389 IssueKind::InternalMethod { class, method } => {
2390 format!("Method {class}::{method}() is marked @internal")
2391 }
2392 IssueKind::MissingReturnType { fn_name } => {
2393 format!("Function {fn_name}() has no return type annotation")
2394 }
2395 IssueKind::MissingClosureReturnType => {
2396 "Closure has no return type annotation".to_string()
2397 }
2398 IssueKind::MissingParamType { fn_name, param } => {
2399 format!("Parameter ${param} of {fn_name}() has no type annotation")
2400 }
2401 IssueKind::MissingPropertyType { class, property } => {
2402 format!("Property {class}::${property} has no type annotation")
2403 }
2404 IssueKind::InvalidThrow { ty } => {
2405 format!("Thrown type '{ty}' does not extend Throwable")
2406 }
2407 IssueKind::InvalidCatch { ty } => {
2408 format!("Caught type '{ty}' does not extend Throwable")
2409 }
2410 IssueKind::UnreachableCatch { ty, shadowed_by } => {
2411 format!("Catch block for '{ty}' is unreachable — already caught by '{shadowed_by}'")
2412 }
2413 IssueKind::MissingThrowsDocblock { class } => {
2414 format!("Exception {class} is thrown but not declared in @throws")
2415 }
2416 IssueKind::ImplicitToStringCast { class } => {
2417 format!("Class {class} is implicitly cast to string")
2418 }
2419 IssueKind::ImplicitFloatToIntCast { from } => {
2420 format!("Implicit cast from {from} to int truncates the fractional part")
2421 }
2422 IssueKind::ParseError { message, code } => format!("{message} [{code}]"),
2423 IssueKind::CompileError { message, code } => {
2424 format!("{message} [{code}]")
2425 }
2426 IssueKind::InvalidDocblock { message } => message.clone(),
2427 IssueKind::UndefinedThrowsDocblock { name } => {
2428 format!("@throws class '{name}' does not exist")
2429 }
2430 IssueKind::InvalidDocblockType { message } => message.clone(),
2431 IssueKind::MixedArgument { param, fn_name } => {
2432 format!("Argument ${param} of {fn_name}() is mixed")
2433 }
2434 IssueKind::MixedAssignment { var } => {
2435 format!("Variable ${var} is assigned a mixed type")
2436 }
2437 IssueKind::MixedMethodCall { method } => {
2438 format!("Method {method}() called on mixed type")
2439 }
2440 IssueKind::UnsupportedReferenceUsage => {
2441 "Reference assignment is not supported".to_string()
2442 }
2443 IssueKind::NoInterfaceProperties { property } => {
2444 format!("Property ${property} is not defined on this interface")
2445 }
2446 IssueKind::UndefinedDocblockClass { name } => {
2447 format!("Docblock type '{name}' does not exist")
2448 }
2449 IssueKind::MissingConstructor { class } => {
2450 format!("Class {class} has uninitialized properties but no constructor")
2451 }
2452 IssueKind::PropertyPossiblyUninitialized { class, property } => {
2453 format!(
2454 "Property {class}::${property} may be left uninitialized by the constructor"
2455 )
2456 }
2457 IssueKind::MixedFunctionCall => "Cannot call mixed type as a function".to_string(),
2458 IssueKind::MixedReturnStatement { declared } => {
2459 format!("Cannot return a mixed type from function with declared return type '{declared}'")
2460 }
2461 IssueKind::MixedPropertyFetch { property } => {
2462 format!("Property ${property} fetched on mixed type")
2463 }
2464 IssueKind::MixedPropertyAssignment { property } => {
2465 format!("Property ${property} assigned on mixed type")
2466 }
2467 IssueKind::MixedArrayAccess => "Array access on mixed type".to_string(),
2468 IssueKind::MixedArrayOffset => "Mixed type used as array offset".to_string(),
2469 IssueKind::MixedClone => "cannot clone mixed".to_string(),
2470 IssueKind::InvalidClone { ty, reason } => match reason {
2471 CloneFailure::NonObject => format!("cannot clone non-object {ty}"),
2472 CloneFailure::Enum => {
2473 format!("Trying to clone an uncloneable object of class {ty}")
2474 }
2475 CloneFailure::PrivateMethod { owner, scope } => match scope {
2476 Some(scope) => {
2477 format!("Call to private method {owner}::__clone() from scope {scope}")
2478 }
2479 None => format!("Call to private method {owner}::__clone() from global scope"),
2480 },
2481 },
2482 IssueKind::PossiblyInvalidClone { ty } => {
2483 format!("cannot clone possibly non-object {ty}")
2484 }
2485 IssueKind::InvalidToString { class } => {
2486 format!("Method {class}::__toString() must return a string")
2487 }
2488 IssueKind::CircularInheritance { class } => {
2489 format!("Class {class} has a circular inheritance chain")
2490 }
2491 IssueKind::InvalidTraitUse { trait_name, reason } => {
2492 format!("Trait {trait_name} used incorrectly: {reason}")
2493 }
2494 IssueKind::WrongCaseFunction { used, canonical } => {
2495 format!("Function name '{used}' has incorrect casing; use '{canonical}'")
2496 }
2497 IssueKind::WrongCaseMethod {
2498 class,
2499 used,
2500 canonical,
2501 } => {
2502 format!("Method name '{class}::{used}' has incorrect casing; use '{canonical}'")
2503 }
2504 IssueKind::WrongCaseClass { used, canonical } => {
2505 format!("Class name '{used}' has incorrect casing; use '{canonical}'")
2506 }
2507 IssueKind::InvalidAttribute { message } => message.clone(),
2508 IssueKind::UndefinedAttributeClass { name } => {
2509 format!("Attribute class {name} does not exist")
2510 }
2511 IssueKind::ForbiddenCode { message } => message.clone(),
2512 IssueKind::DuplicateClass { name } => {
2513 format!("Class {name} has already been defined")
2514 }
2515 IssueKind::DuplicateInterface { name } => {
2516 format!("Interface {name} has already been defined")
2517 }
2518 IssueKind::DuplicateTrait { name } => {
2519 format!("Trait {name} has already been defined")
2520 }
2521 IssueKind::DuplicateEnum { name } => {
2522 format!("Enum {name} has already been defined")
2523 }
2524 IssueKind::DuplicateFunction { name } => {
2525 format!("Function {name}() has already been defined")
2526 }
2527 IssueKind::UseStatementHasNoEffect { name } => {
2528 format!("The use statement with non-compound name '{name}' has no effect")
2529 }
2530 IssueKind::PrivateFinalMethod { class, method } => {
2531 format!("Private method {class}::{method}() cannot be final as it is never overridden by other classes")
2532 }
2533 IssueKind::NonPublicMagicMethod { class, method } => {
2534 format!("The magic method {class}::{method}() must have public visibility")
2535 }
2536 IssueKind::PluginIssue { message, .. } => message.clone(),
2537 }
2538 }
2539}
2540
2541#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2547pub struct TaintStep {
2548 pub label: String,
2550 pub location: Location,
2551}
2552
2553#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2554pub struct Issue {
2555 pub kind: IssueKind,
2556 pub severity: Severity,
2557 pub location: Location,
2558 pub snippet: Option<String>,
2559 pub suppressed: bool,
2560 #[serde(default)]
2562 pub taint_trace: Option<Box<[TaintStep]>>,
2563}
2564
2565impl Issue {
2566 pub fn new(kind: IssueKind, location: Location) -> Self {
2567 let severity = kind.default_severity();
2568 Self {
2569 severity,
2570 kind,
2571 location,
2572 snippet: None,
2573 suppressed: false,
2574 taint_trace: None,
2575 }
2576 }
2577
2578 pub fn with_snippet(mut self, snippet: impl Into<String>) -> Self {
2579 self.snippet = Some(snippet.into());
2580 self
2581 }
2582
2583 pub fn suppress(mut self) -> Self {
2584 self.suppressed = true;
2585 self
2586 }
2587}
2588
2589#[derive(Debug, Default)]
2594pub struct IssueBuffer {
2595 issues: Vec<Issue>,
2596 seen: HashSet<(&'static str, Arc<str>, u32, u16)>,
2597 file_suppressions: Vec<String>,
2599 discard: bool,
2600}
2601
2602impl IssueBuffer {
2603 pub fn new() -> Self {
2604 Self::default()
2605 }
2606
2607 pub fn discarding(mut self) -> Self {
2608 self.discard = true;
2609 self
2610 }
2611
2612 pub fn is_discarding(&self) -> bool {
2613 self.discard
2614 }
2615
2616 pub fn add(&mut self, issue: Issue) {
2617 if self.discard {
2618 return;
2619 }
2620 let key = (
2621 issue.kind.name(),
2622 issue.location.file.clone(),
2623 issue.location.line,
2624 issue.location.col_start,
2625 );
2626 if self.seen.insert(key) {
2627 self.issues.push(issue);
2628 }
2629 }
2630
2631 pub fn has_impossible_comparison_within(&self, span: &Location) -> bool {
2634 self.issues.iter().any(|i| {
2635 matches!(
2636 i.kind,
2637 IssueKind::ImpossibleIdenticalComparison { .. }
2638 | IssueKind::ImpossibleLooseComparison { .. }
2639 | IssueKind::DocblockTypeContradiction { .. }
2640 | IssueKind::TypeDoesNotContainType { .. }
2641 ) && i.location.file == span.file
2642 && (i.location.line, i.location.col_start) >= (span.line, span.col_start)
2643 && (i.location.line, i.location.col_start) < (span.line_end, span.col_end)
2644 })
2645 }
2646
2647 pub fn add_suppression(&mut self, name: impl Into<String>) {
2648 self.file_suppressions.push(name.into());
2649 }
2650
2651 pub fn into_issues(self) -> Vec<Issue> {
2653 self.issues
2654 .into_iter()
2655 .filter(|i| !i.suppressed)
2656 .filter(|i| {
2657 !self
2658 .file_suppressions
2659 .iter()
2660 .any(|s| s == i.kind.display_name())
2661 })
2662 .collect()
2663 }
2664
2665 pub fn into_all_issues(self) -> Vec<Issue> {
2670 self.issues
2671 .into_iter()
2672 .map(|mut i| {
2673 if self
2674 .file_suppressions
2675 .iter()
2676 .any(|s| s == i.kind.display_name())
2677 {
2678 i.suppressed = true;
2679 }
2680 i
2681 })
2682 .collect()
2683 }
2684
2685 pub fn suppress_range(&mut self, from: usize, suppressions: &[String]) {
2688 if suppressions.is_empty() {
2689 return;
2690 }
2691 for issue in self.issues[from..].iter_mut() {
2692 if suppressions.iter().any(|s| s == issue.kind.display_name()) {
2693 issue.suppressed = true;
2694 }
2695 }
2696 }
2697
2698 pub fn suppress_range_where(&mut self, from: usize, pred: impl Fn(&Issue) -> bool) {
2700 for issue in self.issues[from..].iter_mut().filter(|i| pred(i)) {
2701 issue.suppressed = true;
2702 }
2703 }
2704
2705 pub fn issue_count(&self) -> usize {
2708 self.issues.len()
2709 }
2710
2711 pub fn truncate_to(&mut self, mark: usize) {
2717 for issue in self.issues.drain(mark..) {
2718 let key = (
2719 issue.kind.name(),
2720 issue.location.file.clone(),
2721 issue.location.line,
2722 issue.location.col_start,
2723 );
2724 self.seen.remove(&key);
2725 }
2726 }
2727
2728 pub fn take_since(&mut self, mark: usize, pred: impl Fn(&IssueKind) -> bool) -> Vec<Issue> {
2730 self.take_range(mark, self.issues.len(), pred)
2731 }
2732
2733 pub fn take_range(
2735 &mut self,
2736 mark: usize,
2737 mut end: usize,
2738 pred: impl Fn(&IssueKind) -> bool,
2739 ) -> Vec<Issue> {
2740 let mut taken = Vec::new();
2741 let mut i = mark;
2742 while i < end {
2743 if pred(&self.issues[i].kind) {
2744 let issue = self.issues.remove(i);
2745 self.seen.remove(&(
2746 issue.kind.name(),
2747 issue.location.file.clone(),
2748 issue.location.line,
2749 issue.location.col_start,
2750 ));
2751 end -= 1;
2752 taken.push(issue);
2753 } else {
2754 i += 1;
2755 }
2756 }
2757 taken
2758 }
2759
2760 pub fn is_empty(&self) -> bool {
2761 self.issues.is_empty()
2762 }
2763
2764 pub fn len(&self) -> usize {
2765 self.issues.len()
2766 }
2767
2768 pub fn error_count(&self) -> usize {
2769 self.issues
2770 .iter()
2771 .filter(|i| !i.suppressed && i.severity == Severity::Error)
2772 .count()
2773 }
2774
2775 pub fn warning_count(&self) -> usize {
2776 self.issues
2777 .iter()
2778 .filter(|i| !i.suppressed && i.severity == Severity::Warning)
2779 .count()
2780 }
2781}
2782
2783#[cfg(test)]
2784mod code_tests {
2785 use super::*;
2786 use std::collections::HashSet;
2787
2788 fn one_of_each() -> Vec<IssueKind> {
2794 let s = || String::new();
2795 vec![
2796 IssueKind::InvalidScope { in_class: false },
2797 IssueKind::NonStaticSelfCall {
2798 class: s(),
2799 method: s(),
2800 },
2801 IssueKind::DirectConstructorCall { class: s() },
2802 IssueKind::UndefinedVariable { name: s() },
2803 IssueKind::UndefinedFunction { name: s() },
2804 IssueKind::UndefinedMethod {
2805 class: s(),
2806 method: s(),
2807 },
2808 IssueKind::PossiblyUndefinedMethod {
2809 class: s(),
2810 method: s(),
2811 },
2812 IssueKind::UndefinedClass { name: s() },
2813 IssueKind::NotAnInterface { name: s() },
2814 IssueKind::UndefinedProperty {
2815 class: s(),
2816 property: s(),
2817 },
2818 IssueKind::UndefinedConstant { name: s() },
2819 IssueKind::InaccessibleClassConstant {
2820 class: s(),
2821 constant: s(),
2822 },
2823 IssueKind::InaccessibleProperty {
2824 class: s(),
2825 property: s(),
2826 },
2827 IssueKind::PossiblyUndefinedVariable { name: s() },
2828 IssueKind::UndefinedTrait { name: s() },
2829 IssueKind::TraitConstantAccessedDirectly {
2830 trait_name: s(),
2831 constant: s(),
2832 },
2833 IssueKind::UndefinedTraitAliasMethod {
2834 trait_name: Some(s()),
2835 method: s(),
2836 },
2837 IssueKind::ParentNotFound,
2838 IssueKind::NullArgument {
2839 param: s(),
2840 fn_name: s(),
2841 },
2842 IssueKind::NullPropertyFetch { property: s() },
2843 IssueKind::NullMethodCall { method: s() },
2844 IssueKind::NullArrayAccess,
2845 IssueKind::PossiblyNullArgument {
2846 param: s(),
2847 fn_name: s(),
2848 },
2849 IssueKind::PossiblyInvalidArgument {
2850 param: s(),
2851 fn_name: s(),
2852 expected: s(),
2853 actual: s(),
2854 },
2855 IssueKind::PossiblyNullPropertyFetch { property: s() },
2856 IssueKind::PossiblyNullMethodCall { method: s() },
2857 IssueKind::PossiblyNullArrayAccess,
2858 IssueKind::NullableReturnStatement {
2859 expected: s(),
2860 actual: s(),
2861 },
2862 IssueKind::InvalidReturnType {
2863 expected: s(),
2864 actual: s(),
2865 },
2866 IssueKind::InvalidArgument {
2867 param: s(),
2868 fn_name: s(),
2869 expected: s(),
2870 actual: s(),
2871 },
2872 IssueKind::TooFewArguments {
2873 fn_name: s(),
2874 expected: 0,
2875 actual: 0,
2876 },
2877 IssueKind::TooManyArguments {
2878 fn_name: s(),
2879 expected: 0,
2880 actual: 0,
2881 },
2882 IssueKind::InvalidNamedArgument {
2883 fn_name: s(),
2884 name: s(),
2885 overwrites: false,
2886 },
2887 IssueKind::InvalidNamedArguments { fn_name: s() },
2888 IssueKind::InvalidPassByReference {
2889 fn_name: s(),
2890 param: s(),
2891 },
2892 IssueKind::InvalidPropertyFetch { ty: s() },
2893 IssueKind::InvalidMethodCall {
2894 method: s(),
2895 ty: s(),
2896 },
2897 IssueKind::InvalidUnpack { ty: s() },
2898 IssueKind::InvalidArrayAccess { ty: s() },
2899 IssueKind::PossiblyInvalidArrayAccess { ty: s() },
2900 IssueKind::InvalidArrayAssignment { ty: s() },
2901 IssueKind::InvalidPropertyAssignment {
2902 property: s(),
2903 expected: s(),
2904 actual: s(),
2905 },
2906 IssueKind::InvalidCast { from: s(), to: s() },
2907 IssueKind::InvalidStaticInvocation {
2908 class: s(),
2909 method: s(),
2910 },
2911 IssueKind::InvalidOperand {
2912 op: s(),
2913 left: s(),
2914 right: s(),
2915 },
2916 IssueKind::PossiblyInvalidOperand {
2917 op: s(),
2918 left: s(),
2919 right: s(),
2920 },
2921 IssueKind::PossiblyNullOperand { op: s(), ty: s() },
2922 IssueKind::DivisionByZero { op: s() },
2923 IssueKind::RawObjectIteration { ty: s() },
2924 IssueKind::PossiblyRawObjectIteration { ty: s() },
2925 IssueKind::MismatchingDocblockReturnType {
2926 declared: s(),
2927 inferred: s(),
2928 },
2929 IssueKind::MismatchingDocblockParamType {
2930 param: s(),
2931 declared: s(),
2932 inferred: s(),
2933 },
2934 IssueKind::TypeCheckMismatch {
2935 var: s(),
2936 expected: s(),
2937 actual: s(),
2938 },
2939 IssueKind::Trace {
2940 variable: s(),
2941 type_info: s(),
2942 },
2943 IssueKind::InvalidArrayOffset {
2944 expected: s(),
2945 actual: s(),
2946 },
2947 IssueKind::NonExistentArrayOffset { key: s() },
2948 IssueKind::PossiblyInvalidArrayOffset {
2949 expected: s(),
2950 actual: s(),
2951 },
2952 IssueKind::DuplicateArrayKey { key: s() },
2953 IssueKind::RedundantCondition {
2954 always_true: true,
2955 unreachable: None,
2956 },
2957 IssueKind::RedundantCast { from: s(), to: s() },
2958 IssueKind::UnnecessaryVarAnnotation { var: s() },
2959 IssueKind::TypeDoesNotContainType {
2960 left: s(),
2961 right: s(),
2962 },
2963 IssueKind::UnusedVariable { name: s() },
2964 IssueKind::UnusedParam { name: s() },
2965 IssueKind::UnreachableCode,
2966 IssueKind::UnhandledMatchCondition { detail: s() },
2967 IssueKind::UnusedMethod {
2968 class: s(),
2969 method: s(),
2970 },
2971 IssueKind::UnusedProperty {
2972 class: s(),
2973 property: s(),
2974 },
2975 IssueKind::UnusedFunction { name: s() },
2976 IssueKind::UnusedForeachValue { name: s() },
2977 IssueKind::UnusedClass { class: s() },
2978 IssueKind::UnusedConstant {
2979 class: s(),
2980 constant: s(),
2981 },
2982 IssueKind::UnusedSuppress { kind: s() },
2983 IssueKind::ArgumentTypeCoercion {
2984 param: s(),
2985 fn_name: s(),
2986 expected: s(),
2987 actual: s(),
2988 },
2989 IssueKind::PropertyTypeCoercion {
2990 property: s(),
2991 expected: s(),
2992 actual: s(),
2993 },
2994 IssueKind::ImpurePropertyAssignment { property: s() },
2995 IssueKind::ImpureMethodCall { method: s() },
2996 IssueKind::ImpureGlobalVariable { variable: s() },
2997 IssueKind::ImpureStaticVariable { variable: s() },
2998 IssueKind::ImpureByRefAssignment { variable: s() },
2999 IssueKind::ImpureStaticPropertyAssignment {
3000 class: s(),
3001 property: s(),
3002 },
3003 IssueKind::ImpureStaticPropertyAccess {
3004 class: s(),
3005 property: s(),
3006 },
3007 IssueKind::ImpureFunctionCall { fn_name: s() },
3008 IssueKind::ImmutablePropertyModification {
3009 receiver: s(),
3010 property: s(),
3011 },
3012 IssueKind::ReadonlyPropertyAssignment {
3013 class: s(),
3014 property: s(),
3015 unset: false,
3016 },
3017 IssueKind::ReadonlyPropertyAlreadyInitialized {
3018 class: s(),
3019 property: s(),
3020 },
3021 IssueKind::UnimplementedAbstractMethod {
3022 class: s(),
3023 method: s(),
3024 },
3025 IssueKind::UnimplementedInterfaceMethod {
3026 class: s(),
3027 interface: s(),
3028 method: s(),
3029 },
3030 IssueKind::MethodSignatureMismatch {
3031 class: s(),
3032 method: s(),
3033 detail: s(),
3034 },
3035 IssueKind::OverriddenMethodAccess {
3036 class: s(),
3037 method: s(),
3038 parent: s(),
3039 visibility: s(),
3040 },
3041 IssueKind::OverriddenPropertyAccess {
3042 class: s(),
3043 property: s(),
3044 parent: s(),
3045 visibility: s(),
3046 },
3047 IssueKind::PropertyTypeRedeclarationMismatch {
3048 class: s(),
3049 property: s(),
3050 parent: s(),
3051 expected: s(),
3052 covariant: false,
3053 before_php85: false,
3054 },
3055 IssueKind::ReadonlyPropertyRedeclarationMismatch {
3056 parent_class: s(),
3057 class: s(),
3058 property: s(),
3059 parent_readonly: true,
3060 },
3061 IssueKind::StaticPropertyRedeclarationMismatch {
3062 parent_class: s(),
3063 class: s(),
3064 property: s(),
3065 parent_static: true,
3066 },
3067 IssueKind::ReadonlyClassExtendsMismatch {
3068 parent: s(),
3069 child: s(),
3070 parent_readonly: true,
3071 },
3072 IssueKind::InvalidReadonlyPropertyDeclaration {
3073 class: s(),
3074 property: s(),
3075 },
3076 IssueKind::BackedEnumCaseTypeMismatch {
3077 enum_name: s(),
3078 case_name: s(),
3079 expected: s(),
3080 actual: s(),
3081 },
3082 IssueKind::DuplicateEnumCaseValue {
3083 enum_name: s(),
3084 first_case: s(),
3085 case_name: s(),
3086 },
3087 IssueKind::InvalidExtendClass {
3088 parent: s(),
3089 child: s(),
3090 },
3091 IssueKind::FinalMethodOverridden {
3092 class: s(),
3093 method: s(),
3094 parent: s(),
3095 },
3096 IssueKind::AbstractInstantiation { class: s() },
3097 IssueKind::AbstractMethodCall {
3098 class: s(),
3099 method: s(),
3100 },
3101 IssueKind::InterfaceInstantiation { class: s() },
3102 IssueKind::EnumInstantiation { class: s() },
3103 IssueKind::TraitInstantiation { class: s() },
3104 IssueKind::InvalidDynamicProperty {
3105 class: s(),
3106 property: s(),
3107 },
3108 IssueKind::DynamicPropertyCreation {
3109 class: s(),
3110 property: s(),
3111 },
3112 IssueKind::InvalidOverride {
3113 class: s(),
3114 member: s(),
3115 },
3116 IssueKind::CircularInheritance { class: s() },
3117 IssueKind::TaintedInput { sink: s() },
3118 IssueKind::TaintedHtml,
3119 IssueKind::TaintedSql,
3120 IssueKind::TaintedShell,
3121 IssueKind::TaintedLlmPrompt,
3122 IssueKind::TaintedHeader,
3123 IssueKind::TaintedCookie,
3124 IssueKind::TaintedSsrf,
3125 IssueKind::TaintedLdap,
3126 IssueKind::TaintedXpath,
3127 IssueKind::TaintedCallable,
3128 IssueKind::TaintedFile,
3129 IssueKind::TaintedInclude,
3130 IssueKind::TaintedEval,
3131 IssueKind::TaintedUnserialize,
3132 IssueKind::InvalidTemplateParam {
3133 name: s(),
3134 expected_bound: s(),
3135 actual: s(),
3136 },
3137 IssueKind::ShadowedTemplateParam { name: s() },
3138 IssueKind::DeprecatedCall {
3139 name: s(),
3140 message: None,
3141 },
3142 IssueKind::DeprecatedProperty {
3143 class: s(),
3144 property: s(),
3145 message: None,
3146 },
3147 IssueKind::DeprecatedConstant {
3148 class: s(),
3149 constant: s(),
3150 message: None,
3151 },
3152 IssueKind::OptionalParameterBeforeRequired { message: s() },
3153 IssueKind::ImplicitlyNullableParameter { message: s() },
3154 IssueKind::DeprecatedInterface {
3155 name: s(),
3156 message: None,
3157 },
3158 IssueKind::DeprecatedTrait {
3159 name: s(),
3160 message: None,
3161 },
3162 IssueKind::DeprecatedMethodCall {
3163 class: s(),
3164 method: s(),
3165 message: None,
3166 },
3167 IssueKind::DeprecatedMethod {
3168 class: s(),
3169 method: s(),
3170 message: None,
3171 },
3172 IssueKind::DeprecatedClass {
3173 name: s(),
3174 message: None,
3175 },
3176 IssueKind::InternalMethod {
3177 class: s(),
3178 method: s(),
3179 },
3180 IssueKind::MissingReturnType { fn_name: s() },
3181 IssueKind::MissingClosureReturnType,
3182 IssueKind::MissingParamType {
3183 fn_name: s(),
3184 param: s(),
3185 },
3186 IssueKind::MissingPropertyType {
3187 class: s(),
3188 property: s(),
3189 },
3190 IssueKind::MissingThrowsDocblock { class: s() },
3191 IssueKind::InvalidDocblock { message: s() },
3192 IssueKind::UndefinedThrowsDocblock { name: s() },
3193 IssueKind::InvalidDocblockType { message: s() },
3194 IssueKind::MixedArgument {
3195 param: s(),
3196 fn_name: s(),
3197 },
3198 IssueKind::MixedAssignment { var: s() },
3199 IssueKind::MixedMethodCall { method: s() },
3200 IssueKind::UnsupportedReferenceUsage,
3201 IssueKind::NoInterfaceProperties { property: s() },
3202 IssueKind::UndefinedDocblockClass { name: s() },
3203 IssueKind::MissingConstructor { class: s() },
3204 IssueKind::PropertyPossiblyUninitialized {
3205 class: s(),
3206 property: s(),
3207 },
3208 IssueKind::MixedFunctionCall,
3209 IssueKind::MixedReturnStatement { declared: s() },
3210 IssueKind::MixedPropertyFetch { property: s() },
3211 IssueKind::MixedPropertyAssignment { property: s() },
3212 IssueKind::MixedArrayAccess,
3213 IssueKind::MixedArrayOffset,
3214 IssueKind::MixedClone,
3215 IssueKind::InvalidClone {
3216 ty: s(),
3217 reason: CloneFailure::NonObject,
3218 },
3219 IssueKind::PossiblyInvalidClone { ty: s() },
3220 IssueKind::InvalidToString { class: s() },
3221 IssueKind::InvalidTraitUse {
3222 trait_name: s(),
3223 reason: s(),
3224 },
3225 IssueKind::ParseError {
3226 message: s(),
3227 code: s(),
3228 },
3229 IssueKind::CompileError {
3230 message: s(),
3231 code: s(),
3232 },
3233 IssueKind::InvalidThrow { ty: s() },
3234 IssueKind::InvalidCatch { ty: s() },
3235 IssueKind::UnreachableCatch {
3236 ty: s(),
3237 shadowed_by: s(),
3238 },
3239 IssueKind::ImplicitToStringCast { class: s() },
3240 IssueKind::ImplicitFloatToIntCast { from: s() },
3241 IssueKind::WrongCaseFunction {
3242 used: s(),
3243 canonical: s(),
3244 },
3245 IssueKind::WrongCaseMethod {
3246 class: s(),
3247 used: s(),
3248 canonical: s(),
3249 },
3250 IssueKind::WrongCaseClass {
3251 used: s(),
3252 canonical: s(),
3253 },
3254 IssueKind::InvalidAttribute { message: s() },
3255 IssueKind::UndefinedAttributeClass { name: s() },
3256 IssueKind::ForbiddenCode { message: s() },
3257 IssueKind::DuplicateClass { name: s() },
3258 IssueKind::DuplicateInterface { name: s() },
3259 IssueKind::DuplicateTrait { name: s() },
3260 IssueKind::DuplicateEnum { name: s() },
3261 IssueKind::DuplicateFunction { name: s() },
3262 IssueKind::UseStatementHasNoEffect { name: s() },
3263 IssueKind::PrivateFinalMethod {
3264 class: s(),
3265 method: s(),
3266 },
3267 IssueKind::NonPublicMagicMethod {
3268 class: s(),
3269 method: s(),
3270 },
3271 ]
3272 }
3273
3274 #[test]
3275 fn codes_have_expected_shape() {
3276 for kind in one_of_each() {
3277 let code = kind.code();
3278 assert!(
3279 code.len() == 7
3280 && code.starts_with("MIR")
3281 && code[3..].chars().all(|c| c.is_ascii_digit()),
3282 "code {code:?} for {} does not match MIR####",
3283 kind.name(),
3284 );
3285 }
3286 }
3287
3288 #[test]
3289 fn codes_are_unique() {
3290 let kinds = one_of_each();
3291 let mut seen: HashSet<&'static str> = HashSet::new();
3292 for kind in &kinds {
3293 assert!(
3294 seen.insert(kind.code()),
3295 "duplicate code {} (variant {})",
3296 kind.code(),
3297 kind.name(),
3298 );
3299 }
3300 }
3301
3302 #[test]
3303 fn default_severity_for_code_round_trips() {
3304 for kind in one_of_each() {
3305 let code = kind.code();
3306 assert_eq!(
3307 IssueKind::default_severity_for_code(code),
3308 Some(kind.default_severity()),
3309 "severity mismatch for {code} (variant {})",
3310 kind.name(),
3311 );
3312 }
3313 }
3314
3315 #[test]
3316 fn default_severity_for_code_unknown_returns_none() {
3317 assert_eq!(IssueKind::default_severity_for_code("MIR9999"), None);
3318 assert_eq!(IssueKind::default_severity_for_code(""), None);
3319 assert_eq!(IssueKind::default_severity_for_code("mir0001"), None);
3320 }
3321
3322 #[test]
3325 fn one_of_each_has_every_variant() {
3326 assert_eq!(one_of_each().len(), 185);
3329 }
3330}