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