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