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