1use std::collections::HashSet;
2use std::fmt;
3use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
12pub enum Severity {
13 Info,
15 Warning,
17 Error,
19}
20
21impl fmt::Display for Severity {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 Severity::Info => write!(f, "info"),
25 Severity::Warning => write!(f, "warning"),
26 Severity::Error => write!(f, "error"),
27 }
28 }
29}
30
31pub use mir_types::Location;
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42#[non_exhaustive]
43pub enum IssueKind {
44 NonStaticSelfCall { class: String, method: String },
50 InvalidScope {
51 in_class: bool,
53 },
54 UndefinedVariable { name: String },
57 UndefinedFunction { name: String },
60 UndefinedMethod { class: String, method: String },
63 UndefinedClass { name: String },
66 UndefinedProperty { class: String, property: String },
69 InaccessibleProperty { class: String, property: String },
74 UndefinedConstant { name: String },
77 InaccessibleClassConstant { class: String, constant: String },
80 PossiblyUndefinedVariable { name: String },
83 UndefinedTrait { name: String },
86 TraitConstantAccessedDirectly {
93 trait_name: String,
94 constant: String,
95 },
96 UndefinedTraitAliasMethod {
104 trait_name: Option<String>,
105 method: String,
106 },
107 ParentNotFound,
110 InvalidStringClass { actual: String },
113 NotAnInterface { name: String },
117
118 NullArgument { param: String, fn_name: String },
122 NullPropertyFetch { property: String },
125 NullMethodCall { method: String },
128 NullArrayAccess,
131 PossiblyNullArgument { param: String, fn_name: String },
134 PossiblyInvalidArgument {
137 param: String,
138 fn_name: String,
139 expected: String,
140 actual: String,
141 },
142 PossiblyNullPropertyFetch { property: String },
145 PossiblyNullMethodCall { method: String },
148 PossiblyNullArrayAccess,
151 NullableReturnStatement { expected: String, actual: String },
154
155 InvalidReturnType { expected: String, actual: String },
159 InvalidArgument {
162 param: String,
163 fn_name: String,
164 expected: String,
165 actual: String,
166 },
167 TooFewArguments {
170 fn_name: String,
171 expected: usize,
172 actual: usize,
173 },
174 TooManyArguments {
177 fn_name: String,
178 expected: usize,
179 actual: usize,
180 },
181 InvalidNamedArgument { fn_name: String, name: String },
184 InvalidNamedArguments { fn_name: String },
187 InvalidPassByReference { fn_name: String, param: String },
190 InvalidPropertyFetch { ty: String },
193 InvalidArrayAccess { ty: String },
196 PossiblyInvalidArrayAccess { ty: String },
199 InvalidArrayAssignment { ty: String },
202 InvalidPropertyAssignment {
205 property: String,
206 expected: String,
207 actual: String,
208 },
209 InvalidCast { from: String, to: String },
212 InvalidStaticInvocation { class: String, method: String },
215 InvalidOperand {
219 op: String,
220 left: String,
221 right: String,
222 },
223 PossiblyInvalidOperand {
226 op: String,
227 left: String,
228 right: String,
229 },
230 PossiblyNullOperand { op: String, ty: String },
233 DivisionByZero { op: String },
237 RawObjectIteration { ty: String },
240 PossiblyRawObjectIteration { ty: String },
243 MismatchingDocblockReturnType { declared: String, inferred: String },
246 MismatchingDocblockParamType {
249 param: String,
250 declared: String,
251 inferred: String,
252 },
253 TypeCheckMismatch {
259 var: String,
260 expected: String,
261 actual: String,
262 },
263
264 Trace { variable: String, type_info: String },
267
268 InvalidArrayOffset { expected: String, actual: String },
272 NonExistentArrayOffset { key: String },
276 PossiblyInvalidArrayOffset { expected: String, actual: String },
279 DuplicateArrayKey { key: String },
284
285 RedundantCondition { ty: String },
289 RedundantCast { from: String, to: String },
292 UnnecessaryVarAnnotation { var: String },
295 TypeDoesNotContainType { left: String, right: String },
298 ParadoxicalCondition { value: String },
301 DocblockTypeContradiction { expr: String, declared: String },
306 ImpossibleIdenticalComparison {
312 op: String,
313 left: String,
314 right: String,
315 },
316 ImpossibleLooseComparison {
323 op: String,
324 left: String,
325 right: String,
326 },
327 UnevaluatedCode { reason: String },
334
335 UnusedVariable { name: String },
339 UnusedParam { name: String },
342 UnreachableCode,
345 UnhandledMatchCondition { detail: String },
348 UnusedMethod { class: String, method: String },
351 UnusedProperty { class: String, property: String },
354 UnusedFunction { name: String },
357 UnusedForeachValue { name: String },
360 UnusedClass { class: String },
363 UnusedSuppress { kind: String },
367
368 ArgumentTypeCoercion {
371 param: String,
372 fn_name: String,
373 expected: String,
374 actual: String,
375 },
376
377 PropertyTypeCoercion {
380 property: String,
381 expected: String,
382 actual: String,
383 },
384
385 ImpurePropertyAssignment { property: String },
388 ImpureMethodCall { method: String },
390 ImpureGlobalVariable { variable: String },
392 ImpureStaticVariable { variable: String },
394 ImpureStaticPropertyAssignment { class: String, property: String },
398 ImpureStaticPropertyAccess { class: String, property: String },
403 ImpureByRefAssignment { variable: String },
408 ImpureFunctionCall { fn_name: String },
412 ImmutablePropertyModification { receiver: String, property: String },
418
419 ReadonlyPropertyAssignment { class: String, property: String },
423 ReadonlyPropertyAlreadyInitialized { class: String, property: String },
430
431 UnimplementedAbstractMethod { class: String, method: String },
435 UnimplementedInterfaceMethod {
438 class: String,
439 interface: String,
440 method: String,
441 },
442 MethodSignatureMismatch {
445 class: String,
446 method: String,
447 detail: String,
448 },
449 OverriddenMethodAccess { class: String, method: String },
452 OverriddenPropertyAccess { class: String, property: String },
455 PropertyTypeRedeclarationMismatch {
458 class: String,
459 property: String,
460 expected: String,
461 actual: String,
462 },
463 ReadonlyPropertyRedeclarationMismatch {
466 parent_class: String,
467 class: String,
468 property: String,
469 parent_readonly: bool,
472 },
473 StaticPropertyRedeclarationMismatch {
476 parent_class: String,
477 class: String,
478 property: String,
479 parent_static: bool,
482 },
483 ReadonlyClassExtendsMismatch {
489 parent: String,
490 child: String,
491 parent_readonly: bool,
494 },
495 InvalidReadonlyPropertyDeclaration { class: String, property: String },
501 BackedEnumCaseTypeMismatch {
504 enum_name: String,
505 case_name: String,
506 expected: String,
507 actual: String,
508 },
509 DirectConstructorCall { class: String },
512 InvalidExtendClass { parent: String, child: String },
515 FinalMethodOverridden {
518 class: String,
519 method: String,
520 parent: String,
521 },
522 AbstractInstantiation { class: String },
525 AbstractMethodCall { class: String, method: String },
528 InterfaceInstantiation { class: String },
531 InvalidOverride {
535 class: String,
536 method: String,
537 detail: String,
538 },
539
540 TaintedInput { sink: String },
544 TaintedHtml,
547 TaintedSql,
550 TaintedShell,
553 TaintedLlmPrompt,
557
558 InvalidTemplateParam {
562 name: String,
563 expected_bound: String,
564 actual: String,
565 },
566 ShadowedTemplateParam { name: String },
569 IfThisIsMismatch {
574 class: String,
575 method: String,
576 expected: String,
577 actual: String,
578 },
579
580 DeprecatedCall {
584 name: String,
585 message: Option<Arc<str>>,
586 },
587 DeprecatedProperty {
590 class: String,
591 property: String,
592 message: Option<Arc<str>>,
593 },
594 DeprecatedConstant {
597 class: String,
598 constant: String,
599 message: Option<Arc<str>>,
600 },
601 DeprecatedInterface {
604 name: String,
605 message: Option<Arc<str>>,
606 },
607 DeprecatedTrait {
610 name: String,
611 message: Option<Arc<str>>,
612 },
613 DeprecatedMethodCall {
616 class: String,
617 method: String,
618 message: Option<Arc<str>>,
619 },
620 DeprecatedMethod {
623 class: String,
624 method: String,
625 message: Option<Arc<str>>,
626 },
627 DeprecatedClass {
630 name: String,
631 message: Option<Arc<str>>,
632 },
633 InternalMethod { class: String, method: String },
636 MissingReturnType { fn_name: String },
639 MissingClosureReturnType,
642 MissingParamType { fn_name: String, param: String },
645 MissingPropertyType { class: String, property: String },
648 InvalidThrow { ty: String },
651 InvalidCatch { ty: String },
654 UnreachableCatch { ty: String, shadowed_by: String },
659 MissingThrowsDocblock { class: String },
662 UndefinedThrowsDocblock { name: String },
667 ImplicitToStringCast { class: String },
670 ImplicitFloatToIntCast { from: String },
673 ParseError { message: String },
676 InvalidDocblock { message: String },
679 InvalidDocblockType { message: String },
686 MixedArgument { param: String, fn_name: String },
689 MixedAssignment { var: String },
692 MixedMethodCall { method: String },
695 UnsupportedReferenceUsage,
698 NoInterfaceProperties { property: String },
702 UndefinedDocblockClass { name: String },
706 MissingConstructor { class: String },
709 PropertyPossiblyUninitialized { class: String, property: String },
721 MixedFunctionCall,
724 MixedReturnStatement { declared: String },
727 MixedPropertyFetch { property: String },
730 MixedPropertyAssignment { property: String },
733 MixedArrayAccess,
736 MixedArrayOffset,
739 MixedClone,
742 InvalidClone { ty: String },
746 PossiblyInvalidClone { ty: String },
750 InvalidToString { class: String },
754 CircularInheritance { class: String },
757
758 InvalidTraitUse { trait_name: String, reason: String },
762 ForbiddenCode { message: String },
765
766 InvalidAttribute { message: String },
770 UndefinedAttributeClass { name: String },
773
774 WrongCaseFunction { used: String, canonical: String },
778 WrongCaseMethod {
781 class: String,
782 used: String,
783 canonical: String,
784 },
785 WrongCaseClass { used: String, canonical: String },
788 DuplicateClass { name: String },
791 DuplicateInterface { name: String },
794 DuplicateTrait { name: String },
797 DuplicateEnum { name: String },
800 DuplicateFunction { name: String },
803
804 PluginIssue { name: String, message: String },
808}
809
810fn append_deprecation_message(base: String, message: &Option<Arc<str>>) -> String {
811 match message.as_deref().filter(|m| !m.is_empty()) {
812 Some(msg) => format!("{base}: {msg}"),
813 None => base,
814 }
815}
816
817impl IssueKind {
818 pub fn default_severity(&self) -> Severity {
820 match self {
821 IssueKind::NonStaticSelfCall { .. }
823 | IssueKind::DirectConstructorCall { .. }
824 | IssueKind::InvalidScope { .. }
825 | IssueKind::UndefinedVariable { .. }
826 | IssueKind::UndefinedFunction { .. }
827 | IssueKind::UndefinedMethod { .. }
828 | IssueKind::UndefinedClass { .. }
829 | IssueKind::NotAnInterface { .. }
830 | IssueKind::UndefinedConstant { .. }
831 | IssueKind::InaccessibleClassConstant { .. }
832 | IssueKind::InaccessibleProperty { .. }
833 | IssueKind::InvalidReturnType { .. }
834 | IssueKind::InvalidArgument { .. }
835 | IssueKind::TooFewArguments { .. }
836 | IssueKind::TooManyArguments { .. }
837 | IssueKind::InvalidNamedArgument { .. }
838 | IssueKind::InvalidNamedArguments { .. }
839 | IssueKind::InvalidPassByReference { .. }
840 | IssueKind::InvalidThrow { .. }
841 | IssueKind::InvalidCatch { .. }
842 | IssueKind::InvalidStaticInvocation { .. }
843 | IssueKind::UnimplementedAbstractMethod { .. }
844 | IssueKind::UnimplementedInterfaceMethod { .. }
845 | IssueKind::MethodSignatureMismatch { .. }
846 | IssueKind::InvalidExtendClass { .. }
847 | IssueKind::FinalMethodOverridden { .. }
848 | IssueKind::AbstractInstantiation { .. }
849 | IssueKind::AbstractMethodCall { .. }
850 | IssueKind::InterfaceInstantiation { .. }
851 | IssueKind::InvalidOverride { .. }
852 | IssueKind::InvalidTemplateParam { .. }
853 | IssueKind::ReadonlyPropertyAssignment { .. }
854 | IssueKind::ReadonlyPropertyAlreadyInitialized { .. }
855 | IssueKind::ParseError { .. }
856 | IssueKind::TaintedInput { .. }
857 | IssueKind::TaintedHtml
858 | IssueKind::TaintedSql
859 | IssueKind::TaintedShell
860 | IssueKind::TaintedLlmPrompt
861 | IssueKind::CircularInheritance { .. }
862 | IssueKind::InvalidTraitUse { .. }
863 | IssueKind::UndefinedTrait { .. }
864 | IssueKind::TraitConstantAccessedDirectly { .. }
865 | IssueKind::UndefinedTraitAliasMethod { .. }
866 | IssueKind::InvalidClone { .. }
867 | IssueKind::InvalidToString { .. }
868 | IssueKind::TypeCheckMismatch { .. }
869 | IssueKind::PropertyTypeRedeclarationMismatch { .. }
870 | IssueKind::ReadonlyPropertyRedeclarationMismatch { .. }
871 | IssueKind::StaticPropertyRedeclarationMismatch { .. }
872 | IssueKind::ReadonlyClassExtendsMismatch { .. }
873 | IssueKind::InvalidReadonlyPropertyDeclaration { .. }
874 | IssueKind::BackedEnumCaseTypeMismatch { .. }
875 | IssueKind::DivisionByZero { .. }
876 | IssueKind::ParentNotFound => Severity::Error,
877 IssueKind::Trace { .. } => Severity::Info,
878
879 IssueKind::NullArgument { .. }
881 | IssueKind::NullPropertyFetch { .. }
882 | IssueKind::NullMethodCall { .. }
883 | IssueKind::NullArrayAccess
884 | IssueKind::NullableReturnStatement { .. }
885 | IssueKind::InvalidPropertyFetch { .. }
886 | IssueKind::InvalidArrayAccess { .. }
887 | IssueKind::InvalidArrayAssignment { .. }
888 | IssueKind::InvalidPropertyAssignment { .. }
889 | IssueKind::InvalidArrayOffset { .. }
890 | IssueKind::NonExistentArrayOffset { .. }
891 | IssueKind::PossiblyInvalidArrayOffset { .. }
892 | IssueKind::UndefinedProperty { .. }
893 | IssueKind::InvalidOperand { .. }
894 | IssueKind::OverriddenMethodAccess { .. }
895 | IssueKind::OverriddenPropertyAccess { .. }
896 | IssueKind::ImplicitToStringCast { .. }
897 | IssueKind::ImplicitFloatToIntCast { .. }
898 | IssueKind::UnusedVariable { .. }
899 | IssueKind::UnusedForeachValue { .. }
900 | IssueKind::ImpurePropertyAssignment { .. }
901 | IssueKind::ImpureMethodCall { .. }
902 | IssueKind::ImpureGlobalVariable { .. }
903 | IssueKind::ImpureStaticVariable { .. }
904 | IssueKind::ImpureStaticPropertyAssignment { .. }
905 | IssueKind::ImpureStaticPropertyAccess { .. }
906 | IssueKind::ImpureFunctionCall { .. }
907 | IssueKind::ImpureByRefAssignment { .. }
908 | IssueKind::ImmutablePropertyModification { .. }
909 | IssueKind::UnsupportedReferenceUsage
910 | IssueKind::ParadoxicalCondition { .. }
911 | IssueKind::UnhandledMatchCondition { .. }
912 | IssueKind::InvalidStringClass { .. }
913 | IssueKind::ImpossibleIdenticalComparison { .. }
914 | IssueKind::ImpossibleLooseComparison { .. }
915 | IssueKind::DuplicateArrayKey { .. }
916 | IssueKind::ForbiddenCode { .. }
917 | IssueKind::PropertyPossiblyUninitialized { .. }
918 | IssueKind::UndefinedThrowsDocblock { .. }
919 | IssueKind::InvalidDocblockType { .. } => Severity::Warning,
920
921 IssueKind::PossiblyUndefinedVariable { .. } => Severity::Warning,
923
924 IssueKind::PossiblyNullArgument { .. }
926 | IssueKind::PossiblyInvalidArgument { .. }
927 | IssueKind::PossiblyNullPropertyFetch { .. }
928 | IssueKind::PossiblyNullMethodCall { .. }
929 | IssueKind::PossiblyNullArrayAccess
930 | IssueKind::PossiblyInvalidArrayAccess { .. }
931 | IssueKind::PossiblyInvalidClone { .. }
932 | IssueKind::PossiblyInvalidOperand { .. }
933 | IssueKind::PossiblyNullOperand { .. }
934 | IssueKind::PossiblyRawObjectIteration { .. } => Severity::Info,
935
936 IssueKind::RawObjectIteration { .. } => Severity::Warning,
937
938 IssueKind::RedundantCondition { .. }
940 | IssueKind::RedundantCast { .. }
941 | IssueKind::UnnecessaryVarAnnotation { .. }
942 | IssueKind::TypeDoesNotContainType { .. }
943 | IssueKind::DocblockTypeContradiction { .. }
944 | IssueKind::UnevaluatedCode { .. }
945 | IssueKind::IfThisIsMismatch { .. }
946 | IssueKind::UnusedParam { .. }
947 | IssueKind::UnreachableCode
948 | IssueKind::UnreachableCatch { .. }
949 | IssueKind::UnusedMethod { .. }
950 | IssueKind::UnusedProperty { .. }
951 | IssueKind::UnusedFunction { .. }
952 | IssueKind::UnusedClass { .. }
953 | IssueKind::UnusedSuppress { .. }
954 | IssueKind::ArgumentTypeCoercion { .. }
955 | IssueKind::PropertyTypeCoercion { .. }
956 | IssueKind::DeprecatedCall { .. }
957 | IssueKind::DeprecatedProperty { .. }
958 | IssueKind::DeprecatedConstant { .. }
959 | IssueKind::DeprecatedInterface { .. }
960 | IssueKind::DeprecatedTrait { .. }
961 | IssueKind::DeprecatedMethodCall { .. }
962 | IssueKind::DeprecatedMethod { .. }
963 | IssueKind::DeprecatedClass { .. }
964 | IssueKind::InternalMethod { .. }
965 | IssueKind::MissingReturnType { .. }
966 | IssueKind::MissingClosureReturnType
967 | IssueKind::MissingParamType { .. }
968 | IssueKind::MissingPropertyType { .. }
969 | IssueKind::MismatchingDocblockReturnType { .. }
970 | IssueKind::MismatchingDocblockParamType { .. }
971 | IssueKind::InvalidDocblock { .. }
972 | IssueKind::InvalidCast { .. }
973 | IssueKind::MixedArgument { .. }
974 | IssueKind::MixedAssignment { .. }
975 | IssueKind::MixedMethodCall { .. }
976 | IssueKind::NoInterfaceProperties { .. }
977 | IssueKind::UndefinedDocblockClass { .. }
978 | IssueKind::MissingConstructor { .. }
979 | IssueKind::MixedFunctionCall
980 | IssueKind::MixedReturnStatement { .. }
981 | IssueKind::MixedPropertyFetch { .. }
982 | IssueKind::MixedPropertyAssignment { .. }
983 | IssueKind::MixedArrayAccess
984 | IssueKind::MixedArrayOffset
985 | IssueKind::MixedClone
986 | IssueKind::ShadowedTemplateParam { .. }
987 | IssueKind::MissingThrowsDocblock { .. }
988 | IssueKind::WrongCaseFunction { .. }
989 | IssueKind::WrongCaseMethod { .. }
990 | IssueKind::WrongCaseClass { .. }
991 | IssueKind::InvalidAttribute { .. }
992 | IssueKind::UndefinedAttributeClass { .. } => Severity::Info,
993 IssueKind::DuplicateClass { .. }
994 | IssueKind::DuplicateInterface { .. }
995 | IssueKind::DuplicateTrait { .. }
996 | IssueKind::DuplicateEnum { .. }
997 | IssueKind::DuplicateFunction { .. } => Severity::Error,
998 IssueKind::PluginIssue { .. } => Severity::Error,
1001 }
1002 }
1003
1004 pub fn code(&self) -> &'static str {
1034 match self {
1035 IssueKind::NonStaticSelfCall { .. } => "MIR0216",
1037 IssueKind::DirectConstructorCall { .. } => "MIR0217",
1038 IssueKind::InvalidScope { .. } => "MIR0001",
1039 IssueKind::UndefinedVariable { .. } => "MIR0002",
1040 IssueKind::UndefinedFunction { .. } => "MIR0003",
1041 IssueKind::UndefinedMethod { .. } => "MIR0004",
1042 IssueKind::UndefinedClass { .. } => "MIR0005",
1043 IssueKind::UndefinedProperty { .. } => "MIR0006",
1044 IssueKind::UndefinedConstant { .. } => "MIR0007",
1045 IssueKind::InaccessibleClassConstant { .. } => "MIR0011",
1046 IssueKind::InaccessibleProperty { .. } => "MIR0014",
1047 IssueKind::TraitConstantAccessedDirectly { .. } => "MIR0012",
1048 IssueKind::UndefinedTraitAliasMethod { .. } => "MIR0013",
1049 IssueKind::PossiblyUndefinedVariable { .. } => "MIR0008",
1050 IssueKind::UndefinedTrait { .. } => "MIR0009",
1051 IssueKind::ParentNotFound => "MIR0010",
1052
1053 IssueKind::NullArgument { .. } => "MIR0100",
1055 IssueKind::NullPropertyFetch { .. } => "MIR0101",
1056 IssueKind::NullMethodCall { .. } => "MIR0102",
1057 IssueKind::NullArrayAccess => "MIR0103",
1058 IssueKind::PossiblyNullArgument { .. } => "MIR0104",
1059 IssueKind::PossiblyInvalidArgument { .. } => "MIR0105",
1060 IssueKind::PossiblyNullPropertyFetch { .. } => "MIR0106",
1061 IssueKind::PossiblyNullMethodCall { .. } => "MIR0107",
1062 IssueKind::PossiblyNullArrayAccess => "MIR0108",
1063 IssueKind::NullableReturnStatement { .. } => "MIR0109",
1064
1065 IssueKind::InvalidReturnType { .. } => "MIR0200",
1067 IssueKind::InvalidArgument { .. } => "MIR0201",
1068 IssueKind::TooFewArguments { .. } => "MIR0202",
1069 IssueKind::TooManyArguments { .. } => "MIR0203",
1070 IssueKind::InvalidNamedArgument { .. } => "MIR0204",
1071 IssueKind::InvalidNamedArguments { .. } => "MIR0224",
1072 IssueKind::InvalidPassByReference { .. } => "MIR0205",
1073 IssueKind::InvalidPropertyFetch { .. } => "MIR0218",
1074 IssueKind::InvalidArrayAccess { .. } => "MIR0219",
1075 IssueKind::PossiblyInvalidArrayAccess { .. } => "MIR0227",
1076 IssueKind::InvalidArrayAssignment { .. } => "MIR0220",
1077 IssueKind::InvalidPropertyAssignment { .. } => "MIR0206",
1078 IssueKind::InvalidCast { .. } => "MIR0207",
1079 IssueKind::InvalidStaticInvocation { .. } => "MIR0215",
1080 IssueKind::InvalidOperand { .. } => "MIR0208",
1081 IssueKind::PossiblyInvalidOperand { .. } => "MIR0213",
1082 IssueKind::PossiblyNullOperand { .. } => "MIR0214",
1083 IssueKind::DivisionByZero { .. } => "MIR0229",
1084 IssueKind::RawObjectIteration { .. } => "MIR0222",
1085 IssueKind::PossiblyRawObjectIteration { .. } => "MIR0223",
1086 IssueKind::MismatchingDocblockReturnType { .. } => "MIR0209",
1087 IssueKind::MismatchingDocblockParamType { .. } => "MIR0210",
1088 IssueKind::InvalidStringClass { .. } => "MIR0211",
1089 IssueKind::NotAnInterface { .. } => "MIR0228",
1090 IssueKind::TypeCheckMismatch { .. } => "MIR0212",
1091 IssueKind::Trace { .. } => "MIR0221",
1092 IssueKind::ArgumentTypeCoercion { .. } => "MIR0225",
1093 IssueKind::PropertyTypeCoercion { .. } => "MIR0226",
1094
1095 IssueKind::InvalidArrayOffset { .. } => "MIR0300",
1097 IssueKind::NonExistentArrayOffset { .. } => "MIR0301",
1098 IssueKind::PossiblyInvalidArrayOffset { .. } => "MIR0302",
1099 IssueKind::DuplicateArrayKey { .. } => "MIR0303",
1100
1101 IssueKind::RedundantCondition { .. } => "MIR0400",
1103 IssueKind::RedundantCast { .. } => "MIR0401",
1104 IssueKind::UnnecessaryVarAnnotation { .. } => "MIR0402",
1105 IssueKind::TypeDoesNotContainType { .. } => "MIR0403",
1106 IssueKind::ParadoxicalCondition { .. } => "MIR0404",
1107 IssueKind::UnhandledMatchCondition { .. } => "MIR0405",
1108 IssueKind::DocblockTypeContradiction { .. } => "MIR0406",
1109 IssueKind::UnevaluatedCode { .. } => "MIR0407",
1110 IssueKind::ImpossibleIdenticalComparison { .. } => "MIR0408",
1111 IssueKind::ImpossibleLooseComparison { .. } => "MIR0409",
1112
1113 IssueKind::UnusedVariable { .. } => "MIR0500",
1115 IssueKind::UnusedParam { .. } => "MIR0501",
1116 IssueKind::UnreachableCode => "MIR0502",
1117 IssueKind::UnusedMethod { .. } => "MIR0503",
1118 IssueKind::UnusedProperty { .. } => "MIR0504",
1119 IssueKind::UnusedFunction { .. } => "MIR0505",
1120 IssueKind::UnusedForeachValue { .. } => "MIR0506",
1121 IssueKind::UnusedClass { .. } => "MIR0507",
1122 IssueKind::UnusedSuppress { .. } => "MIR0508",
1123
1124 IssueKind::ImpurePropertyAssignment { .. } => "MIR1700",
1126 IssueKind::ImpureMethodCall { .. } => "MIR1701",
1127 IssueKind::ImpureGlobalVariable { .. } => "MIR1702",
1128 IssueKind::ImpureStaticVariable { .. } => "MIR1703",
1129 IssueKind::ImpureStaticPropertyAssignment { .. } => "MIR1706",
1130 IssueKind::ImpureByRefAssignment { .. } => "MIR1707",
1131 IssueKind::ImpureStaticPropertyAccess { .. } => "MIR1708",
1132 IssueKind::ImpureFunctionCall { .. } => "MIR1704",
1133 IssueKind::ImmutablePropertyModification { .. } => "MIR1705",
1134 IssueKind::UnsupportedReferenceUsage => "MIR1506",
1135 IssueKind::NoInterfaceProperties { .. } => "MIR1504",
1136 IssueKind::UndefinedDocblockClass { .. } => "MIR1505",
1137 IssueKind::MissingConstructor { .. } => "MIR1507",
1138 IssueKind::PropertyPossiblyUninitialized { .. } => "MIR1510",
1139 IssueKind::MixedFunctionCall => "MIR1211",
1140 IssueKind::MixedReturnStatement { .. } => "MIR1212",
1141
1142 IssueKind::ReadonlyPropertyAssignment { .. } => "MIR0600",
1144 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => "MIR0601",
1145
1146 IssueKind::UnimplementedAbstractMethod { .. } => "MIR0700",
1148 IssueKind::UnimplementedInterfaceMethod { .. } => "MIR0701",
1149 IssueKind::MethodSignatureMismatch { .. } => "MIR0702",
1150 IssueKind::OverriddenMethodAccess { .. } => "MIR0703",
1151 IssueKind::OverriddenPropertyAccess { .. } => "MIR0710",
1152 IssueKind::PropertyTypeRedeclarationMismatch { .. } => "MIR0712",
1153 IssueKind::BackedEnumCaseTypeMismatch { .. } => "MIR0713",
1154 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => "MIR0714",
1155 IssueKind::StaticPropertyRedeclarationMismatch { .. } => "MIR0715",
1156 IssueKind::ReadonlyClassExtendsMismatch { .. } => "MIR0716",
1157 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => "MIR0717",
1158 IssueKind::InvalidExtendClass { .. } => "MIR0704",
1159 IssueKind::FinalMethodOverridden { .. } => "MIR0705",
1160 IssueKind::AbstractInstantiation { .. } => "MIR0706",
1161 IssueKind::AbstractMethodCall { .. } => "MIR0711",
1162 IssueKind::InterfaceInstantiation { .. } => "MIR0709",
1163 IssueKind::CircularInheritance { .. } => "MIR0707",
1164 IssueKind::InvalidOverride { .. } => "MIR0708",
1165
1166 IssueKind::TaintedInput { .. } => "MIR0800",
1168 IssueKind::TaintedHtml => "MIR0801",
1169 IssueKind::TaintedSql => "MIR0802",
1170 IssueKind::TaintedShell => "MIR0803",
1171 IssueKind::TaintedLlmPrompt => "MIR0804",
1172
1173 IssueKind::InvalidTemplateParam { .. } => "MIR0900",
1175 IssueKind::ShadowedTemplateParam { .. } => "MIR0901",
1176 IssueKind::IfThisIsMismatch { .. } => "MIR0902",
1177
1178 IssueKind::DeprecatedCall { .. } => "MIR1000",
1180 IssueKind::WrongCaseFunction { .. } => "MIR1009",
1181 IssueKind::WrongCaseMethod { .. } => "MIR1010",
1182 IssueKind::WrongCaseClass { .. } => "MIR1011",
1183 IssueKind::DeprecatedProperty { .. } => "MIR1005",
1184 IssueKind::DeprecatedInterface { .. } => "MIR1006",
1185 IssueKind::DeprecatedTrait { .. } => "MIR1007",
1186 IssueKind::DeprecatedConstant { .. } => "MIR1008",
1187 IssueKind::DeprecatedMethodCall { .. } => "MIR1001",
1188 IssueKind::DeprecatedMethod { .. } => "MIR1002",
1189 IssueKind::DeprecatedClass { .. } => "MIR1003",
1190 IssueKind::InternalMethod { .. } => "MIR1004",
1191
1192 IssueKind::MissingReturnType { .. } => "MIR1100",
1194 IssueKind::MissingParamType { .. } => "MIR1101",
1195 IssueKind::MissingPropertyType { .. } => "MIR1104",
1196 IssueKind::MissingClosureReturnType => "MIR1105",
1197 IssueKind::MissingThrowsDocblock { .. } => "MIR1102",
1198 IssueKind::InvalidDocblock { .. } => "MIR1103",
1199 IssueKind::UndefinedThrowsDocblock { .. } => "MIR1106",
1200 IssueKind::InvalidDocblockType { .. } => "MIR1107",
1201
1202 IssueKind::MixedArgument { .. } => "MIR1200",
1204 IssueKind::MixedAssignment { .. } => "MIR1201",
1205 IssueKind::MixedMethodCall { .. } => "MIR1202",
1206 IssueKind::MixedPropertyFetch { .. } => "MIR1203",
1207 IssueKind::MixedPropertyAssignment { .. } => "MIR1208",
1208 IssueKind::MixedArrayAccess => "MIR1209",
1209 IssueKind::MixedArrayOffset => "MIR1210",
1210 IssueKind::MixedClone => "MIR1204",
1211 IssueKind::InvalidClone { .. } => "MIR1205",
1212 IssueKind::PossiblyInvalidClone { .. } => "MIR1206",
1213 IssueKind::InvalidToString { .. } => "MIR1207",
1214
1215 IssueKind::InvalidTraitUse { .. } => "MIR1300",
1217 IssueKind::ForbiddenCode { .. } => "MIR1301",
1218
1219 IssueKind::ParseError { .. } => "MIR1400",
1221
1222 IssueKind::InvalidAttribute { .. } => "MIR1600",
1224 IssueKind::UndefinedAttributeClass { .. } => "MIR1601",
1225 IssueKind::DuplicateClass { .. } => "MIR1602",
1226 IssueKind::DuplicateInterface { .. } => "MIR1603",
1227 IssueKind::DuplicateTrait { .. } => "MIR1604",
1228 IssueKind::DuplicateEnum { .. } => "MIR1605",
1229 IssueKind::DuplicateFunction { .. } => "MIR1606",
1230
1231 IssueKind::InvalidThrow { .. } => "MIR1500",
1233 IssueKind::InvalidCatch { .. } => "MIR1503",
1234 IssueKind::UnreachableCatch { .. } => "MIR1508",
1235 IssueKind::PluginIssue { .. } => "MIR1509",
1236 IssueKind::ImplicitToStringCast { .. } => "MIR1501",
1237 IssueKind::ImplicitFloatToIntCast { .. } => "MIR1502",
1238 }
1239 }
1240
1241 pub fn default_severity_for_code(code: &str) -> Option<Severity> {
1247 match code {
1248 "MIR0001" | "MIR0002" | "MIR0003" | "MIR0004" | "MIR0005" | "MIR0007" | "MIR0009"
1250 | "MIR0010" | "MIR0011" | "MIR0012" | "MIR0013" | "MIR0014" | "MIR0200" | "MIR0201"
1251 | "MIR0202" | "MIR0203" | "MIR0204" | "MIR0205" | "MIR0212" | "MIR0215" | "MIR0216"
1252 | "MIR0217" | "MIR0224" | "MIR0600" | "MIR0601" | "MIR0700" | "MIR0701" | "MIR0702"
1253 | "MIR0704" | "MIR0705" | "MIR0706" | "MIR0707" | "MIR0708" | "MIR0709" | "MIR0711"
1254 | "MIR0712" | "MIR0713" | "MIR0714" | "MIR0715" | "MIR0716" | "MIR0717" | "MIR0228"
1255 | "MIR0229" | "MIR0800" | "MIR0801" | "MIR0802" | "MIR0803" | "MIR0804" | "MIR0900"
1256 | "MIR1205" | "MIR1207" | "MIR1300" | "MIR1400" | "MIR1500" | "MIR1503" | "MIR1602"
1257 | "MIR1603" | "MIR1604" | "MIR1605" | "MIR1606" => Some(Severity::Error),
1258
1259 "MIR0006" | "MIR0008" | "MIR0100" | "MIR0101" | "MIR0102" | "MIR0103" | "MIR0109"
1261 | "MIR0206" | "MIR0208" | "MIR0211" | "MIR0218" | "MIR0219" | "MIR0220" | "MIR0222"
1262 | "MIR0300" | "MIR0301" | "MIR0302" | "MIR0303" | "MIR0404" | "MIR0405" | "MIR0408"
1263 | "MIR0500" | "MIR0506" | "MIR0703" | "MIR0710" | "MIR1301" | "MIR1501" | "MIR1502"
1264 | "MIR1700" | "MIR1701" | "MIR1702" | "MIR1703" | "MIR1704" | "MIR1705" | "MIR1706"
1265 | "MIR1707" | "MIR1708" | "MIR1506" | "MIR1510" | "MIR1106" | "MIR1107" => {
1266 Some(Severity::Warning)
1267 }
1268
1269 "MIR0104" | "MIR0105" | "MIR0106" | "MIR0107" | "MIR0108" | "MIR0207" | "MIR0209"
1271 | "MIR0210" | "MIR0213" | "MIR0214" | "MIR0221" | "MIR0223" | "MIR0400" | "MIR0401"
1272 | "MIR0402" | "MIR0403" | "MIR0501" | "MIR0502" | "MIR0503" | "MIR0504" | "MIR0505"
1273 | "MIR0507" | "MIR0508" | "MIR0901" | "MIR1000" | "MIR1001" | "MIR1002" | "MIR1003"
1274 | "MIR1004" | "MIR1005" | "MIR1006" | "MIR1007" | "MIR1008" | "MIR1009" | "MIR1010"
1275 | "MIR1011" | "MIR1100" | "MIR1101" | "MIR1102" | "MIR1103" | "MIR1104" | "MIR1105"
1276 | "MIR1200" | "MIR1201" | "MIR1202" | "MIR1203" | "MIR1204" | "MIR1206" | "MIR1208"
1277 | "MIR1209" | "MIR1210" | "MIR1211" | "MIR1212" | "MIR1504" | "MIR1505" | "MIR1507"
1278 | "MIR1508" | "MIR1600" | "MIR1601" | "MIR0225" | "MIR0226" | "MIR0227" | "MIR0406"
1279 | "MIR0407" | "MIR0902" => Some(Severity::Info),
1280
1281 _ => None,
1282 }
1283 }
1284
1285 pub fn name(&self) -> &'static str {
1287 match self {
1288 IssueKind::NonStaticSelfCall { .. } => "NonStaticSelfCall",
1289 IssueKind::DirectConstructorCall { .. } => "DirectConstructorCall",
1290 IssueKind::InvalidScope { .. } => "InvalidScope",
1291 IssueKind::UndefinedVariable { .. } => "UndefinedVariable",
1292 IssueKind::UndefinedFunction { .. } => "UndefinedFunction",
1293 IssueKind::UndefinedMethod { .. } => "UndefinedMethod",
1294 IssueKind::UndefinedClass { .. } => "UndefinedClass",
1295 IssueKind::UndefinedProperty { .. } => "UndefinedProperty",
1296 IssueKind::UndefinedConstant { .. } => "UndefinedConstant",
1297 IssueKind::InaccessibleClassConstant { .. } => "InaccessibleClassConstant",
1298 IssueKind::InaccessibleProperty { .. } => "InaccessibleProperty",
1299 IssueKind::TraitConstantAccessedDirectly { .. } => "TraitConstantAccessedDirectly",
1300 IssueKind::UndefinedTraitAliasMethod { .. } => "UndefinedTraitAliasMethod",
1301 IssueKind::PossiblyUndefinedVariable { .. } => "PossiblyUndefinedVariable",
1302 IssueKind::UndefinedTrait { .. } => "UndefinedTrait",
1303 IssueKind::ParentNotFound => "ParentNotFound",
1304 IssueKind::InvalidStringClass { .. } => "InvalidStringClass",
1305 IssueKind::NotAnInterface { .. } => "NotAnInterface",
1306 IssueKind::NullArgument { .. } => "NullArgument",
1307 IssueKind::NullPropertyFetch { .. } => "NullPropertyFetch",
1308 IssueKind::NullMethodCall { .. } => "NullMethodCall",
1309 IssueKind::NullArrayAccess => "NullArrayAccess",
1310 IssueKind::PossiblyNullArgument { .. } => "PossiblyNullArgument",
1311 IssueKind::PossiblyInvalidArgument { .. } => "PossiblyInvalidArgument",
1312 IssueKind::PossiblyNullPropertyFetch { .. } => "PossiblyNullPropertyFetch",
1313 IssueKind::PossiblyNullMethodCall { .. } => "PossiblyNullMethodCall",
1314 IssueKind::PossiblyNullArrayAccess => "PossiblyNullArrayAccess",
1315 IssueKind::NullableReturnStatement { .. } => "NullableReturnStatement",
1316 IssueKind::InvalidReturnType { .. } => "InvalidReturnType",
1317 IssueKind::InvalidArgument { .. } => "InvalidArgument",
1318 IssueKind::TooFewArguments { .. } => "TooFewArguments",
1319 IssueKind::TooManyArguments { .. } => "TooManyArguments",
1320 IssueKind::InvalidNamedArgument { .. } => "InvalidNamedArgument",
1321 IssueKind::InvalidNamedArguments { .. } => "InvalidNamedArguments",
1322 IssueKind::InvalidPassByReference { .. } => "InvalidPassByReference",
1323 IssueKind::InvalidPropertyFetch { .. } => "InvalidPropertyFetch",
1324 IssueKind::InvalidArrayAccess { .. } => "InvalidArrayAccess",
1325 IssueKind::PossiblyInvalidArrayAccess { .. } => "PossiblyInvalidArrayAccess",
1326 IssueKind::InvalidArrayAssignment { .. } => "InvalidArrayAssignment",
1327 IssueKind::InvalidPropertyAssignment { .. } => "InvalidPropertyAssignment",
1328 IssueKind::InvalidCast { .. } => "InvalidCast",
1329 IssueKind::InvalidStaticInvocation { .. } => "InvalidStaticInvocation",
1330 IssueKind::InvalidOperand { .. } => "InvalidOperand",
1331 IssueKind::PossiblyInvalidOperand { .. } => "PossiblyInvalidOperand",
1332 IssueKind::PossiblyNullOperand { .. } => "PossiblyNullOperand",
1333 IssueKind::DivisionByZero { .. } => "DivisionByZero",
1334 IssueKind::RawObjectIteration { .. } => "RawObjectIteration",
1335 IssueKind::PossiblyRawObjectIteration { .. } => "PossiblyRawObjectIteration",
1336 IssueKind::MismatchingDocblockReturnType { .. } => "MismatchingDocblockReturnType",
1337 IssueKind::MismatchingDocblockParamType { .. } => "MismatchingDocblockParamType",
1338 IssueKind::TypeCheckMismatch { .. } => "TypeCheckMismatch",
1339 IssueKind::DocblockTypeContradiction { .. } => "DocblockTypeContradiction",
1340 IssueKind::ImpossibleIdenticalComparison { .. } => "ImpossibleIdenticalComparison",
1341 IssueKind::ImpossibleLooseComparison { .. } => "ImpossibleLooseComparison",
1342 IssueKind::UnevaluatedCode { .. } => "UnevaluatedCode",
1343 IssueKind::IfThisIsMismatch { .. } => "IfThisIsMismatch",
1344 IssueKind::Trace { .. } => "Trace",
1345 IssueKind::InvalidArrayOffset { .. } => "InvalidArrayOffset",
1346 IssueKind::NonExistentArrayOffset { .. } => "NonExistentArrayOffset",
1347 IssueKind::PossiblyInvalidArrayOffset { .. } => "PossiblyInvalidArrayOffset",
1348 IssueKind::DuplicateArrayKey { .. } => "DuplicateArrayKey",
1349 IssueKind::RedundantCondition { .. } => "RedundantCondition",
1350 IssueKind::RedundantCast { .. } => "RedundantCast",
1351 IssueKind::UnnecessaryVarAnnotation { .. } => "UnnecessaryVarAnnotation",
1352 IssueKind::TypeDoesNotContainType { .. } => "TypeDoesNotContainType",
1353 IssueKind::ParadoxicalCondition { .. } => "ParadoxicalCondition",
1354 IssueKind::UnhandledMatchCondition { .. } => "UnhandledMatchCondition",
1355 IssueKind::UnusedVariable { .. } => "UnusedVariable",
1356 IssueKind::UnusedParam { .. } => "UnusedParam",
1357 IssueKind::UnreachableCode => "UnreachableCode",
1358 IssueKind::UnusedMethod { .. } => "UnusedMethod",
1359 IssueKind::UnusedProperty { .. } => "UnusedProperty",
1360 IssueKind::UnusedFunction { .. } => "UnusedFunction",
1361 IssueKind::UnusedForeachValue { .. } => "UnusedForeachValue",
1362 IssueKind::UnusedClass { .. } => "UnusedClass",
1363 IssueKind::UnusedSuppress { .. } => "UnusedSuppress",
1364 IssueKind::ArgumentTypeCoercion { .. } => "ArgumentTypeCoercion",
1365 IssueKind::PropertyTypeCoercion { .. } => "PropertyTypeCoercion",
1366 IssueKind::ImpurePropertyAssignment { .. } => "ImpurePropertyAssignment",
1367 IssueKind::ImpureMethodCall { .. } => "ImpureMethodCall",
1368 IssueKind::ImpureGlobalVariable { .. } => "ImpureGlobalVariable",
1369 IssueKind::ImpureStaticVariable { .. } => "ImpureStaticVariable",
1370 IssueKind::ImpureByRefAssignment { .. } => "ImpureByRefAssignment",
1371 IssueKind::ImpureStaticPropertyAssignment { .. } => "ImpureStaticPropertyAssignment",
1372 IssueKind::ImpureStaticPropertyAccess { .. } => "ImpureStaticPropertyAccess",
1373 IssueKind::ImpureFunctionCall { .. } => "ImpureFunctionCall",
1374 IssueKind::ImmutablePropertyModification { .. } => "ImmutablePropertyModification",
1375 IssueKind::UnsupportedReferenceUsage => "UnsupportedReferenceUsage",
1376 IssueKind::NoInterfaceProperties { .. } => "NoInterfaceProperties",
1377 IssueKind::UndefinedDocblockClass { .. } => "UndefinedDocblockClass",
1378 IssueKind::MissingConstructor { .. } => "MissingConstructor",
1379 IssueKind::PropertyPossiblyUninitialized { .. } => "PropertyPossiblyUninitialized",
1380 IssueKind::MixedFunctionCall => "MixedFunctionCall",
1381 IssueKind::MixedReturnStatement { .. } => "MixedReturnStatement",
1382 IssueKind::UnimplementedAbstractMethod { .. } => "UnimplementedAbstractMethod",
1383 IssueKind::UnimplementedInterfaceMethod { .. } => "UnimplementedInterfaceMethod",
1384 IssueKind::MethodSignatureMismatch { .. } => "MethodSignatureMismatch",
1385 IssueKind::OverriddenMethodAccess { .. } => "OverriddenMethodAccess",
1386 IssueKind::OverriddenPropertyAccess { .. } => "OverriddenPropertyAccess",
1387 IssueKind::PropertyTypeRedeclarationMismatch { .. } => {
1388 "PropertyTypeRedeclarationMismatch"
1389 }
1390 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => {
1391 "ReadonlyPropertyRedeclarationMismatch"
1392 }
1393 IssueKind::StaticPropertyRedeclarationMismatch { .. } => {
1394 "StaticPropertyRedeclarationMismatch"
1395 }
1396 IssueKind::ReadonlyClassExtendsMismatch { .. } => "ReadonlyClassExtendsMismatch",
1397 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => {
1398 "InvalidReadonlyPropertyDeclaration"
1399 }
1400 IssueKind::BackedEnumCaseTypeMismatch { .. } => "BackedEnumCaseTypeMismatch",
1401 IssueKind::InvalidExtendClass { .. } => "InvalidExtendClass",
1402 IssueKind::FinalMethodOverridden { .. } => "FinalMethodOverridden",
1403 IssueKind::AbstractInstantiation { .. } => "AbstractInstantiation",
1404 IssueKind::AbstractMethodCall { .. } => "AbstractMethodCall",
1405 IssueKind::InterfaceInstantiation { .. } => "InterfaceInstantiation",
1406 IssueKind::InvalidOverride { .. } => "InvalidOverride",
1407 IssueKind::ReadonlyPropertyAssignment { .. } => "ReadonlyPropertyAssignment",
1408 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => {
1409 "ReadonlyPropertyAlreadyInitialized"
1410 }
1411 IssueKind::InvalidTemplateParam { .. } => "InvalidTemplateParam",
1412 IssueKind::ShadowedTemplateParam { .. } => "ShadowedTemplateParam",
1413 IssueKind::TaintedInput { .. } => "TaintedInput",
1414 IssueKind::TaintedHtml => "TaintedHtml",
1415 IssueKind::TaintedSql => "TaintedSql",
1416 IssueKind::TaintedShell => "TaintedShell",
1417 IssueKind::TaintedLlmPrompt => "TaintedLlmPrompt",
1418 IssueKind::DeprecatedCall { .. } => "DeprecatedCall",
1419 IssueKind::DeprecatedProperty { .. } => "DeprecatedProperty",
1420 IssueKind::DeprecatedConstant { .. } => "DeprecatedConstant",
1421 IssueKind::DeprecatedInterface { .. } => "DeprecatedInterface",
1422 IssueKind::DeprecatedTrait { .. } => "DeprecatedTrait",
1423 IssueKind::DeprecatedMethodCall { .. } => "DeprecatedMethodCall",
1424 IssueKind::DeprecatedMethod { .. } => "DeprecatedMethod",
1425 IssueKind::DeprecatedClass { .. } => "DeprecatedClass",
1426 IssueKind::InternalMethod { .. } => "InternalMethod",
1427 IssueKind::MissingReturnType { .. } => "MissingReturnType",
1428 IssueKind::MissingClosureReturnType => "MissingClosureReturnType",
1429 IssueKind::MissingParamType { .. } => "MissingParamType",
1430 IssueKind::MissingPropertyType { .. } => "MissingPropertyType",
1431 IssueKind::InvalidThrow { .. } => "InvalidThrow",
1432 IssueKind::InvalidCatch { .. } => "InvalidCatch",
1433 IssueKind::UnreachableCatch { .. } => "UnreachableCatch",
1434 IssueKind::MissingThrowsDocblock { .. } => "MissingThrowsDocblock",
1435 IssueKind::ImplicitToStringCast { .. } => "ImplicitToStringCast",
1436 IssueKind::ImplicitFloatToIntCast { .. } => "ImplicitFloatToIntCast",
1437 IssueKind::ParseError { .. } => "ParseError",
1438 IssueKind::InvalidDocblock { .. } => "InvalidDocblock",
1439 IssueKind::UndefinedThrowsDocblock { .. } => "UndefinedThrowsDocblock",
1440 IssueKind::InvalidDocblockType { .. } => "InvalidDocblockType",
1441 IssueKind::MixedArgument { .. } => "MixedArgument",
1442 IssueKind::MixedAssignment { .. } => "MixedAssignment",
1443 IssueKind::MixedMethodCall { .. } => "MixedMethodCall",
1444 IssueKind::MixedPropertyFetch { .. } => "MixedPropertyFetch",
1445 IssueKind::MixedPropertyAssignment { .. } => "MixedPropertyAssignment",
1446 IssueKind::MixedArrayAccess => "MixedArrayAccess",
1447 IssueKind::MixedArrayOffset => "MixedArrayOffset",
1448 IssueKind::MixedClone => "MixedClone",
1449 IssueKind::InvalidClone { .. } => "InvalidClone",
1450 IssueKind::PossiblyInvalidClone { .. } => "PossiblyInvalidClone",
1451 IssueKind::InvalidToString { .. } => "InvalidToString",
1452 IssueKind::CircularInheritance { .. } => "CircularInheritance",
1453 IssueKind::InvalidTraitUse { .. } => "InvalidTraitUse",
1454 IssueKind::ForbiddenCode { .. } => "ForbiddenCode",
1455 IssueKind::WrongCaseFunction { .. } => "WrongCaseFunction",
1456 IssueKind::WrongCaseMethod { .. } => "WrongCaseMethod",
1457 IssueKind::WrongCaseClass { .. } => "WrongCaseClass",
1458 IssueKind::InvalidAttribute { .. } => "InvalidAttribute",
1459 IssueKind::UndefinedAttributeClass { .. } => "UndefinedAttributeClass",
1460 IssueKind::DuplicateClass { .. } => "DuplicateClass",
1461 IssueKind::DuplicateInterface { .. } => "DuplicateInterface",
1462 IssueKind::DuplicateTrait { .. } => "DuplicateTrait",
1463 IssueKind::DuplicateEnum { .. } => "DuplicateEnum",
1464 IssueKind::DuplicateFunction { .. } => "DuplicateFunction",
1465 IssueKind::PluginIssue { .. } => "PluginIssue",
1466 }
1467 }
1468
1469 pub fn display_name(&self) -> &str {
1473 match self {
1474 IssueKind::PluginIssue { name, .. } => name,
1475 other => other.name(),
1476 }
1477 }
1478
1479 pub fn message(&self) -> String {
1481 match self {
1482 IssueKind::NonStaticSelfCall { class, method } => {
1483 format!("Non-static method {class}::{method}() cannot be called statically")
1484 }
1485 IssueKind::DirectConstructorCall { class } => {
1486 format!("Cannot call constructor of {class} directly")
1487 }
1488 IssueKind::InvalidScope { in_class } => {
1489 if *in_class {
1490 "$this cannot be used in a static method".to_string()
1491 } else {
1492 "$this cannot be used outside of a class".to_string()
1493 }
1494 }
1495 IssueKind::UndefinedVariable { name } => format!("Variable ${name} is not defined"),
1496 IssueKind::UndefinedFunction { name } => format!("Function {name}() is not defined"),
1497 IssueKind::UndefinedMethod { class, method } => {
1498 format!("Method {class}::{method}() does not exist")
1499 }
1500 IssueKind::UndefinedClass { name } => format!("Class {name} does not exist"),
1501 IssueKind::UndefinedProperty { class, property } => {
1502 format!("Property {class}::${property} does not exist")
1503 }
1504 IssueKind::UndefinedConstant { name } => format!("Constant {name} is not defined"),
1505 IssueKind::InaccessibleClassConstant { class, constant } => {
1506 format!("Cannot access constant {class}::{constant}")
1507 }
1508 IssueKind::InaccessibleProperty { class, property } => {
1509 format!("Cannot access property {class}::${property}")
1510 }
1511 IssueKind::PossiblyUndefinedVariable { name } => {
1512 format!("Variable ${name} might not be defined")
1513 }
1514 IssueKind::UndefinedTrait { name } => format!("Trait {name} does not exist"),
1515 IssueKind::TraitConstantAccessedDirectly {
1516 trait_name,
1517 constant,
1518 } => {
1519 format!("Cannot access trait constant {trait_name}::{constant} directly")
1520 }
1521 IssueKind::UndefinedTraitAliasMethod { trait_name, method } => match trait_name {
1522 Some(t) => {
1523 format!("An alias was defined for {t}::{method} but this method does not exist")
1524 }
1525 None => format!("An alias was defined for {method} but this method does not exist"),
1526 },
1527 IssueKind::ParentNotFound => {
1528 "Cannot use parent:: when current class has no parent".to_string()
1529 }
1530 IssueKind::InvalidStringClass { actual } => {
1531 format!("Dynamic class instantiation requires string or class-string type, got '{actual}'")
1532 }
1533 IssueKind::NotAnInterface { name } => {
1534 format!("{name} is not an interface")
1535 }
1536
1537 IssueKind::NullArgument { param, fn_name } => {
1538 format!("Argument ${param} of {fn_name}() cannot be null")
1539 }
1540 IssueKind::NullPropertyFetch { property } => {
1541 format!("Cannot access property ${property} on null")
1542 }
1543 IssueKind::NullMethodCall { method } => {
1544 format!("Cannot call method {method}() on null")
1545 }
1546 IssueKind::NullArrayAccess => "Cannot access array on null".to_string(),
1547 IssueKind::PossiblyNullArgument { param, fn_name } => {
1548 format!("Argument ${param} of {fn_name}() might be null")
1549 }
1550 IssueKind::PossiblyInvalidArgument {
1551 param,
1552 fn_name,
1553 expected,
1554 actual,
1555 } => {
1556 format!("Argument ${param} of {fn_name}() expects '{expected}', possibly different type '{actual}' provided")
1557 }
1558 IssueKind::PossiblyNullPropertyFetch { property } => {
1559 format!("Cannot access property ${property} on possibly null value")
1560 }
1561 IssueKind::PossiblyNullMethodCall { method } => {
1562 format!("Cannot call method {method}() on possibly null value")
1563 }
1564 IssueKind::PossiblyNullArrayAccess => {
1565 "Cannot access array on possibly null value".to_string()
1566 }
1567 IssueKind::NullableReturnStatement { expected, actual } => {
1568 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1569 }
1570
1571 IssueKind::InvalidReturnType { expected, actual } => {
1572 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1573 }
1574 IssueKind::InvalidArgument {
1575 param,
1576 fn_name,
1577 expected,
1578 actual,
1579 } => {
1580 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}'")
1581 }
1582 IssueKind::TooFewArguments {
1583 fn_name,
1584 expected,
1585 actual,
1586 } => {
1587 format!(
1588 "Too few arguments for {}(): expected {}, got {}",
1589 fn_name, expected, actual
1590 )
1591 }
1592 IssueKind::TooManyArguments {
1593 fn_name,
1594 expected,
1595 actual,
1596 } => {
1597 format!(
1598 "Too many arguments for {}(): expected {}, got {}",
1599 fn_name, expected, actual
1600 )
1601 }
1602 IssueKind::InvalidNamedArgument { fn_name, name } => {
1603 format!("{}() has no parameter named ${}", fn_name, name)
1604 }
1605 IssueKind::InvalidNamedArguments { fn_name } => {
1606 format!("{}() does not accept named arguments", fn_name)
1607 }
1608 IssueKind::InvalidPassByReference { fn_name, param } => {
1609 format!(
1610 "Argument ${} of {}() must be passed by reference",
1611 param, fn_name
1612 )
1613 }
1614 IssueKind::InvalidPropertyFetch { ty } => {
1615 format!("Cannot fetch property on non-object type '{ty}'")
1616 }
1617 IssueKind::InvalidArrayAccess { ty } => {
1618 format!("Cannot use [] operator on non-array type '{ty}'")
1619 }
1620 IssueKind::PossiblyInvalidArrayAccess { ty } => {
1621 format!("Possibly invalid array access: '{ty}' might not support []")
1622 }
1623 IssueKind::InvalidArrayAssignment { ty } => {
1624 format!("Cannot use [] assignment on non-array type '{ty}'")
1625 }
1626 IssueKind::InvalidPropertyAssignment {
1627 property,
1628 expected,
1629 actual,
1630 } => {
1631 format!("Property ${property} expects '{expected}', cannot assign '{actual}'")
1632 }
1633 IssueKind::InvalidCast { from, to } => {
1634 format!("Cannot cast '{from}' to '{to}'")
1635 }
1636 IssueKind::InvalidStaticInvocation { class, method } => {
1637 format!("Non-static method {class}::{method}() cannot be called statically")
1638 }
1639 IssueKind::InvalidOperand { op, left, right } => {
1640 if right.is_empty() {
1641 format!("Operator '{op}' not supported for operand of type '{left}'")
1642 } else {
1643 format!("Operator '{op}' not supported between '{left}' and '{right}'")
1644 }
1645 }
1646 IssueKind::PossiblyInvalidOperand { op, left, right } => {
1647 format!("Operator '{op}' might not be supported between '{left}' and '{right}'")
1648 }
1649 IssueKind::PossiblyNullOperand { op, ty } => {
1650 format!("Operator '{op}' operand '{ty}' might be null")
1651 }
1652 IssueKind::DivisionByZero { op } => {
1653 format!("Division by zero: right operand of '{op}' is always 0")
1654 }
1655 IssueKind::RawObjectIteration { ty } => {
1656 format!("Cannot iterate over non-iterable object '{ty}'")
1657 }
1658 IssueKind::PossiblyRawObjectIteration { ty } => {
1659 format!("Cannot iterate over possibly non-iterable object '{ty}'")
1660 }
1661 IssueKind::MismatchingDocblockReturnType { declared, inferred } => {
1662 format!("Docblock return type '{declared}' does not match inferred '{inferred}'")
1663 }
1664 IssueKind::MismatchingDocblockParamType {
1665 param,
1666 declared,
1667 inferred,
1668 } => {
1669 format!(
1670 "Docblock type '{declared}' for ${param} does not match inferred '{inferred}'"
1671 )
1672 }
1673 IssueKind::TypeCheckMismatch {
1674 var,
1675 expected,
1676 actual,
1677 } => {
1678 format!("Type of {var} is expected to be {expected}, got {actual}")
1679 }
1680 IssueKind::Trace {
1681 variable,
1682 type_info,
1683 } => {
1684 format!("Type of ${variable} is {type_info}")
1685 }
1686
1687 IssueKind::InvalidArrayOffset { expected, actual } => {
1688 format!("Array offset expects '{expected}', got '{actual}'")
1689 }
1690 IssueKind::NonExistentArrayOffset { key } => {
1691 format!("Array offset '{key}' does not exist")
1692 }
1693 IssueKind::PossiblyInvalidArrayOffset { expected, actual } => {
1694 format!("Array offset might be invalid: expects '{expected}', got '{actual}'")
1695 }
1696 IssueKind::DuplicateArrayKey { key } => {
1697 format!("Array key {key} is duplicated — the earlier entry is silently overwritten")
1698 }
1699
1700 IssueKind::RedundantCondition { ty } => {
1701 format!("Condition is always true/false for type '{ty}'")
1702 }
1703 IssueKind::RedundantCast { from, to } => {
1704 format!("Casting '{from}' to '{to}' is redundant")
1705 }
1706 IssueKind::UnnecessaryVarAnnotation { var } => {
1707 format!("@var annotation for ${var} is unnecessary")
1708 }
1709 IssueKind::TypeDoesNotContainType { left, right } => {
1710 format!("Type '{left}' can never contain type '{right}'")
1711 }
1712 IssueKind::ParadoxicalCondition { value } => {
1713 format!("Value {value} is duplicated; this branch can never be reached")
1714 }
1715 IssueKind::UnhandledMatchCondition { detail } => {
1716 format!("Unhandled match condition: {detail}")
1717 }
1718 IssueKind::DocblockTypeContradiction { expr, declared } => {
1719 format!("Type '{declared}' makes '{expr}' impossible — this can never hold")
1720 }
1721 IssueKind::ImpossibleIdenticalComparison { op, left, right } => {
1722 let result = if op == "===" { "false" } else { "true" };
1723 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be identical")
1724 }
1725 IssueKind::ImpossibleLooseComparison { op, left, right } => {
1726 let result = if op == "==" { "false" } else { "true" };
1727 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be loosely equal")
1728 }
1729 IssueKind::UnevaluatedCode { reason } => {
1730 format!("Unevaluated code: {reason}")
1731 }
1732 IssueKind::IfThisIsMismatch {
1733 class,
1734 method,
1735 expected,
1736 actual,
1737 } => {
1738 format!(
1739 "Cannot call {class}::{method}() — @if-this-is requires $this to be '{expected}', but it is '{actual}'"
1740 )
1741 }
1742
1743 IssueKind::UnusedVariable { name } => format!("Variable ${name} is never read"),
1744 IssueKind::UnusedParam { name } => format!("Parameter ${name} is never used"),
1745 IssueKind::UnreachableCode => "Unreachable code detected".to_string(),
1746 IssueKind::UnusedMethod { class, method } => {
1747 format!("Private method {class}::{method}() is never called")
1748 }
1749 IssueKind::UnusedProperty { class, property } => {
1750 format!("Private property {class}::${property} is never read")
1751 }
1752 IssueKind::UnusedFunction { name } => {
1753 format!("Function {name}() is never called")
1754 }
1755 IssueKind::UnusedForeachValue { name } => {
1756 format!("Foreach value ${name} is never read")
1757 }
1758 IssueKind::UnusedClass { class } => {
1759 format!("Class {class} is never referenced")
1760 }
1761 IssueKind::UnusedSuppress { kind } => {
1762 format!("Suppress annotation for '{kind}' is never used")
1763 }
1764 IssueKind::ArgumentTypeCoercion {
1765 param,
1766 fn_name,
1767 expected,
1768 actual,
1769 } => {
1770 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}' — coercion may fail at runtime")
1771 }
1772 IssueKind::PropertyTypeCoercion {
1773 property,
1774 expected,
1775 actual,
1776 } => {
1777 format!("Property ${property} expects '{expected}', cannot assign '{actual}' — coercion may fail at runtime")
1778 }
1779 IssueKind::ImpurePropertyAssignment { property } => {
1780 format!("Assigning to property {property} of a parameter in a pure or external-mutation-free context")
1781 }
1782 IssueKind::ImpureMethodCall { method } => {
1783 format!("Calling impure method {method}() in a pure or immutable context")
1784 }
1785 IssueKind::ImpureGlobalVariable { variable } => {
1786 format!("Using global variable ${variable} in a @pure function")
1787 }
1788 IssueKind::ImpureStaticVariable { variable } => {
1789 format!("Using static variable ${variable} in a @pure function")
1790 }
1791 IssueKind::ImpureByRefAssignment { variable } => {
1792 format!("Assigning to by-reference parameter ${variable} in a @pure function")
1793 }
1794 IssueKind::ImpureStaticPropertyAssignment { class, property } => {
1795 format!("Assigning to static property {class}::${property} in a @pure function")
1796 }
1797 IssueKind::ImpureStaticPropertyAccess { class, property } => {
1798 format!("Reading static property {class}::${property} in a @pure function")
1799 }
1800 IssueKind::ImpureFunctionCall { fn_name } => {
1801 format!("Calling impure function {fn_name}() in a @pure function")
1802 }
1803 IssueKind::ImmutablePropertyModification { receiver, property } => {
1804 format!("Assigning to property {property} of {receiver} in an immutable context (@psalm-immutable class or @psalm-mutation-free method)")
1805 }
1806
1807 IssueKind::UnimplementedAbstractMethod { class, method } => {
1808 format!("Class {class} must implement abstract method {method}()")
1809 }
1810 IssueKind::UnimplementedInterfaceMethod {
1811 class,
1812 interface,
1813 method,
1814 } => {
1815 format!("Class {class} must implement {interface}::{method}() from interface")
1816 }
1817 IssueKind::MethodSignatureMismatch {
1818 class,
1819 method,
1820 detail,
1821 } => {
1822 format!("Method {class}::{method}() signature mismatch: {detail}")
1823 }
1824 IssueKind::OverriddenMethodAccess { class, method } => {
1825 format!("Method {class}::{method}() overrides with less visibility")
1826 }
1827 IssueKind::OverriddenPropertyAccess { class, property } => {
1828 format!("Property {class}::${property} overrides with less visibility")
1829 }
1830 IssueKind::PropertyTypeRedeclarationMismatch {
1831 class,
1832 property,
1833 expected,
1834 actual,
1835 } => {
1836 format!(
1837 "Type of {class}::${property} must be {expected} (as in parent class), {actual} given"
1838 )
1839 }
1840 IssueKind::ReadonlyPropertyRedeclarationMismatch {
1841 parent_class,
1842 class,
1843 property,
1844 parent_readonly,
1845 } => {
1846 if *parent_readonly {
1847 format!(
1848 "Cannot redeclare readonly property {parent_class}::${property} as non-readonly {class}::${property}"
1849 )
1850 } else {
1851 format!(
1852 "Cannot redeclare non-readonly property {parent_class}::${property} as readonly {class}::${property}"
1853 )
1854 }
1855 }
1856 IssueKind::StaticPropertyRedeclarationMismatch {
1857 parent_class,
1858 class,
1859 property,
1860 parent_static,
1861 } => {
1862 if *parent_static {
1863 format!(
1864 "Cannot redeclare static property {parent_class}::${property} as non-static {class}::${property}"
1865 )
1866 } else {
1867 format!(
1868 "Cannot redeclare non-static property {parent_class}::${property} as static {class}::${property}"
1869 )
1870 }
1871 }
1872 IssueKind::BackedEnumCaseTypeMismatch {
1873 enum_name,
1874 case_name,
1875 expected,
1876 actual,
1877 } => {
1878 format!(
1879 "Backed enum case {enum_name}::{case_name} has value of type {actual}, but backing type is {expected}"
1880 )
1881 }
1882 IssueKind::ReadonlyClassExtendsMismatch {
1883 parent,
1884 child,
1885 parent_readonly,
1886 } => {
1887 if *parent_readonly {
1888 format!("Non-readonly class {child} cannot extend readonly class {parent}")
1889 } else {
1890 format!("Readonly class {child} cannot extend non-readonly class {parent}")
1891 }
1892 }
1893 IssueKind::InvalidReadonlyPropertyDeclaration { class, property } => {
1894 format!("Readonly property {class}::${property} cannot have a default value")
1895 }
1896 IssueKind::ReadonlyPropertyAssignment { class, property } => {
1897 format!(
1898 "Cannot assign to readonly property {class}::${property} outside of constructor"
1899 )
1900 }
1901 IssueKind::ReadonlyPropertyAlreadyInitialized { class, property } => {
1902 format!(
1903 "Cannot modify readonly property {class}::${property} — already initialized"
1904 )
1905 }
1906 IssueKind::InvalidExtendClass { parent, child } => {
1907 format!("Class {child} cannot extend final class {parent}")
1908 }
1909 IssueKind::InvalidTemplateParam {
1910 name,
1911 expected_bound,
1912 actual,
1913 } => {
1914 if actual.split('|').any(|part| part == "false") {
1915 format!(
1916 "Template type '{name}' inferred as '{actual}' can be false and therefore does not satisfy bound '{expected_bound}'"
1917 )
1918 } else {
1919 format!(
1920 "Template type '{name}' inferred as '{actual}' does not satisfy bound '{expected_bound}'"
1921 )
1922 }
1923 }
1924 IssueKind::ShadowedTemplateParam { name } => {
1925 format!(
1926 "Method template parameter '{name}' shadows class-level template parameter with the same name"
1927 )
1928 }
1929 IssueKind::FinalMethodOverridden {
1930 class,
1931 method,
1932 parent,
1933 } => {
1934 format!("Method {class}::{method}() cannot override final method from {parent}")
1935 }
1936 IssueKind::AbstractInstantiation { class } => {
1937 format!("Cannot instantiate abstract class {class}")
1938 }
1939 IssueKind::AbstractMethodCall { class, method } => {
1940 format!("Cannot call abstract method {class}::{method}()")
1941 }
1942 IssueKind::InterfaceInstantiation { class } => {
1943 format!("Cannot instantiate interface {class}")
1944 }
1945 IssueKind::InvalidOverride {
1946 class,
1947 method,
1948 detail,
1949 } => {
1950 format!("Method {class}::{method}() has #[Override] but {detail}")
1951 }
1952
1953 IssueKind::TaintedInput { sink } => format!("Tainted input reaching sink '{sink}'"),
1954 IssueKind::TaintedHtml => "Tainted HTML output — possible XSS".to_string(),
1955 IssueKind::TaintedSql => "Tainted SQL query — possible SQL injection".to_string(),
1956 IssueKind::TaintedShell => {
1957 "Tainted shell command — possible command injection".to_string()
1958 }
1959 IssueKind::TaintedLlmPrompt => {
1960 "Tainted LLM prompt — possible prompt injection".to_string()
1961 }
1962
1963 IssueKind::DeprecatedCall { name, message } => {
1964 let base = format!("Call to deprecated function {name}");
1965 append_deprecation_message(base, message)
1966 }
1967 IssueKind::DeprecatedProperty {
1968 class,
1969 property,
1970 message,
1971 } => {
1972 let base = format!("Property {class}::${property} is deprecated");
1973 append_deprecation_message(base, message)
1974 }
1975 IssueKind::DeprecatedConstant {
1976 class,
1977 constant,
1978 message,
1979 } => {
1980 let base = format!("Constant {class}::{constant} is deprecated");
1981 append_deprecation_message(base, message)
1982 }
1983 IssueKind::DeprecatedInterface { name, message } => {
1984 let base = format!("Interface {name} is deprecated");
1985 append_deprecation_message(base, message)
1986 }
1987 IssueKind::DeprecatedTrait { name, message } => {
1988 let base = format!("Trait {name} is deprecated");
1989 append_deprecation_message(base, message)
1990 }
1991 IssueKind::DeprecatedMethodCall {
1992 class,
1993 method,
1994 message,
1995 } => {
1996 let base = format!("Call to deprecated method {class}::{method}");
1997 append_deprecation_message(base, message)
1998 }
1999 IssueKind::DeprecatedMethod {
2000 class,
2001 method,
2002 message,
2003 } => {
2004 let base = format!("Method {class}::{method}() is deprecated");
2005 append_deprecation_message(base, message)
2006 }
2007 IssueKind::DeprecatedClass { name, message } => {
2008 let base = format!("Class {name} is deprecated");
2009 append_deprecation_message(base, message)
2010 }
2011 IssueKind::InternalMethod { class, method } => {
2012 format!("Method {class}::{method}() is marked @internal")
2013 }
2014 IssueKind::MissingReturnType { fn_name } => {
2015 format!("Function {fn_name}() has no return type annotation")
2016 }
2017 IssueKind::MissingClosureReturnType => {
2018 "Closure has no return type annotation".to_string()
2019 }
2020 IssueKind::MissingParamType { fn_name, param } => {
2021 format!("Parameter ${param} of {fn_name}() has no type annotation")
2022 }
2023 IssueKind::MissingPropertyType { class, property } => {
2024 format!("Property {class}::${property} has no type annotation")
2025 }
2026 IssueKind::InvalidThrow { ty } => {
2027 format!("Thrown type '{ty}' does not extend Throwable")
2028 }
2029 IssueKind::InvalidCatch { ty } => {
2030 format!("Caught type '{ty}' does not extend Throwable")
2031 }
2032 IssueKind::UnreachableCatch { ty, shadowed_by } => {
2033 format!("Catch block for '{ty}' is unreachable — already caught by '{shadowed_by}'")
2034 }
2035 IssueKind::MissingThrowsDocblock { class } => {
2036 format!("Exception {class} is thrown but not declared in @throws")
2037 }
2038 IssueKind::ImplicitToStringCast { class } => {
2039 format!("Class {class} is implicitly cast to string")
2040 }
2041 IssueKind::ImplicitFloatToIntCast { from } => {
2042 format!("Implicit cast from {from} to int truncates the fractional part")
2043 }
2044 IssueKind::ParseError { message } => format!("Parse error: {message}"),
2045 IssueKind::InvalidDocblock { message } => format!("Invalid docblock: {message}"),
2046 IssueKind::UndefinedThrowsDocblock { name } => {
2047 format!("@throws class '{name}' does not exist")
2048 }
2049 IssueKind::InvalidDocblockType { message } => {
2050 format!("Invalid docblock type: {message}")
2051 }
2052 IssueKind::MixedArgument { param, fn_name } => {
2053 format!("Argument ${param} of {fn_name}() is mixed")
2054 }
2055 IssueKind::MixedAssignment { var } => {
2056 format!("Variable ${var} is assigned a mixed type")
2057 }
2058 IssueKind::MixedMethodCall { method } => {
2059 format!("Method {method}() called on mixed type")
2060 }
2061 IssueKind::UnsupportedReferenceUsage => {
2062 "Reference assignment is not supported".to_string()
2063 }
2064 IssueKind::NoInterfaceProperties { property } => {
2065 format!("Property ${property} is not defined on this interface")
2066 }
2067 IssueKind::UndefinedDocblockClass { name } => {
2068 format!("Docblock type '{name}' does not exist")
2069 }
2070 IssueKind::MissingConstructor { class } => {
2071 format!("Class {class} has uninitialized properties but no constructor")
2072 }
2073 IssueKind::PropertyPossiblyUninitialized { class, property } => {
2074 format!(
2075 "Property {class}::${property} may be left uninitialized by the constructor"
2076 )
2077 }
2078 IssueKind::MixedFunctionCall => "Cannot call mixed type as a function".to_string(),
2079 IssueKind::MixedReturnStatement { declared } => {
2080 format!("Cannot return a mixed type from function with declared return type '{declared}'")
2081 }
2082 IssueKind::MixedPropertyFetch { property } => {
2083 format!("Property ${property} fetched on mixed type")
2084 }
2085 IssueKind::MixedPropertyAssignment { property } => {
2086 format!("Property ${property} assigned on mixed type")
2087 }
2088 IssueKind::MixedArrayAccess => "Array access on mixed type".to_string(),
2089 IssueKind::MixedArrayOffset => "Mixed type used as array offset".to_string(),
2090 IssueKind::MixedClone => "cannot clone mixed".to_string(),
2091 IssueKind::InvalidClone { ty } => format!("cannot clone non-object {ty}"),
2092 IssueKind::PossiblyInvalidClone { ty } => {
2093 format!("cannot clone possibly non-object {ty}")
2094 }
2095 IssueKind::InvalidToString { class } => {
2096 format!("Method {class}::__toString() must return a string")
2097 }
2098 IssueKind::CircularInheritance { class } => {
2099 format!("Class {class} has a circular inheritance chain")
2100 }
2101 IssueKind::InvalidTraitUse { trait_name, reason } => {
2102 format!("Trait {trait_name} used incorrectly: {reason}")
2103 }
2104 IssueKind::WrongCaseFunction { used, canonical } => {
2105 format!("Function name '{used}' has incorrect casing; use '{canonical}'")
2106 }
2107 IssueKind::WrongCaseMethod {
2108 class,
2109 used,
2110 canonical,
2111 } => {
2112 format!("Method name '{class}::{used}' has incorrect casing; use '{canonical}'")
2113 }
2114 IssueKind::WrongCaseClass { used, canonical } => {
2115 format!("Class name '{used}' has incorrect casing; use '{canonical}'")
2116 }
2117 IssueKind::InvalidAttribute { message } => message.clone(),
2118 IssueKind::UndefinedAttributeClass { name } => {
2119 format!("Attribute class {name} does not exist")
2120 }
2121 IssueKind::ForbiddenCode { message } => message.clone(),
2122 IssueKind::DuplicateClass { name } => {
2123 format!("Class {name} has already been defined")
2124 }
2125 IssueKind::DuplicateInterface { name } => {
2126 format!("Interface {name} has already been defined")
2127 }
2128 IssueKind::DuplicateTrait { name } => {
2129 format!("Trait {name} has already been defined")
2130 }
2131 IssueKind::DuplicateEnum { name } => {
2132 format!("Enum {name} has already been defined")
2133 }
2134 IssueKind::DuplicateFunction { name } => {
2135 format!("Function {name}() has already been defined")
2136 }
2137 IssueKind::PluginIssue { message, .. } => message.clone(),
2138 }
2139 }
2140}
2141
2142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2147pub struct Issue {
2148 pub kind: IssueKind,
2149 pub severity: Severity,
2150 pub location: Location,
2151 pub snippet: Option<String>,
2152 pub suppressed: bool,
2153}
2154
2155impl Issue {
2156 pub fn new(kind: IssueKind, location: Location) -> Self {
2157 let severity = kind.default_severity();
2158 Self {
2159 severity,
2160 kind,
2161 location,
2162 snippet: None,
2163 suppressed: false,
2164 }
2165 }
2166
2167 pub fn with_snippet(mut self, snippet: impl Into<String>) -> Self {
2168 self.snippet = Some(snippet.into());
2169 self
2170 }
2171
2172 pub fn suppress(mut self) -> Self {
2173 self.suppressed = true;
2174 self
2175 }
2176}
2177
2178#[derive(Debug, Default)]
2183pub struct IssueBuffer {
2184 issues: Vec<Issue>,
2185 seen: HashSet<(&'static str, Arc<str>, u32, u16)>,
2186 file_suppressions: Vec<String>,
2188 discard: bool,
2189}
2190
2191impl IssueBuffer {
2192 pub fn new() -> Self {
2193 Self::default()
2194 }
2195
2196 pub fn discarding(mut self) -> Self {
2197 self.discard = true;
2198 self
2199 }
2200
2201 pub fn add(&mut self, issue: Issue) {
2202 if self.discard {
2203 return;
2204 }
2205 let key = (
2206 issue.kind.name(),
2207 issue.location.file.clone(),
2208 issue.location.line,
2209 issue.location.col_start,
2210 );
2211 if self.seen.insert(key) {
2212 self.issues.push(issue);
2213 }
2214 }
2215
2216 pub fn add_suppression(&mut self, name: impl Into<String>) {
2217 self.file_suppressions.push(name.into());
2218 }
2219
2220 pub fn into_issues(self) -> Vec<Issue> {
2222 self.issues
2223 .into_iter()
2224 .filter(|i| !i.suppressed)
2225 .filter(|i| {
2226 !self
2227 .file_suppressions
2228 .iter()
2229 .any(|s| s == i.kind.display_name())
2230 })
2231 .collect()
2232 }
2233
2234 pub fn into_all_issues(self) -> Vec<Issue> {
2239 self.issues
2240 .into_iter()
2241 .map(|mut i| {
2242 if self
2243 .file_suppressions
2244 .iter()
2245 .any(|s| s == i.kind.display_name())
2246 {
2247 i.suppressed = true;
2248 }
2249 i
2250 })
2251 .collect()
2252 }
2253
2254 pub fn suppress_range(&mut self, from: usize, suppressions: &[String]) {
2257 if suppressions.is_empty() {
2258 return;
2259 }
2260 for issue in self.issues[from..].iter_mut() {
2261 if suppressions.iter().any(|s| s == issue.kind.display_name()) {
2262 issue.suppressed = true;
2263 }
2264 }
2265 }
2266
2267 pub fn issue_count(&self) -> usize {
2270 self.issues.len()
2271 }
2272
2273 pub fn truncate_to(&mut self, mark: usize) {
2279 for issue in self.issues.drain(mark..) {
2280 let key = (
2281 issue.kind.name(),
2282 issue.location.file.clone(),
2283 issue.location.line,
2284 issue.location.col_start,
2285 );
2286 self.seen.remove(&key);
2287 }
2288 }
2289
2290 pub fn is_empty(&self) -> bool {
2291 self.issues.is_empty()
2292 }
2293
2294 pub fn len(&self) -> usize {
2295 self.issues.len()
2296 }
2297
2298 pub fn error_count(&self) -> usize {
2299 self.issues
2300 .iter()
2301 .filter(|i| !i.suppressed && i.severity == Severity::Error)
2302 .count()
2303 }
2304
2305 pub fn warning_count(&self) -> usize {
2306 self.issues
2307 .iter()
2308 .filter(|i| !i.suppressed && i.severity == Severity::Warning)
2309 .count()
2310 }
2311}
2312
2313#[cfg(test)]
2314mod code_tests {
2315 use super::*;
2316 use std::collections::HashSet;
2317
2318 fn one_of_each() -> Vec<IssueKind> {
2324 let s = || String::new();
2325 vec![
2326 IssueKind::InvalidScope { in_class: false },
2327 IssueKind::NonStaticSelfCall {
2328 class: s(),
2329 method: s(),
2330 },
2331 IssueKind::DirectConstructorCall { class: s() },
2332 IssueKind::UndefinedVariable { name: s() },
2333 IssueKind::UndefinedFunction { name: s() },
2334 IssueKind::UndefinedMethod {
2335 class: s(),
2336 method: s(),
2337 },
2338 IssueKind::UndefinedClass { name: s() },
2339 IssueKind::NotAnInterface { name: s() },
2340 IssueKind::UndefinedProperty {
2341 class: s(),
2342 property: s(),
2343 },
2344 IssueKind::UndefinedConstant { name: s() },
2345 IssueKind::InaccessibleClassConstant {
2346 class: s(),
2347 constant: s(),
2348 },
2349 IssueKind::InaccessibleProperty {
2350 class: s(),
2351 property: s(),
2352 },
2353 IssueKind::PossiblyUndefinedVariable { name: s() },
2354 IssueKind::UndefinedTrait { name: s() },
2355 IssueKind::TraitConstantAccessedDirectly {
2356 trait_name: s(),
2357 constant: s(),
2358 },
2359 IssueKind::UndefinedTraitAliasMethod {
2360 trait_name: Some(s()),
2361 method: s(),
2362 },
2363 IssueKind::ParentNotFound,
2364 IssueKind::NullArgument {
2365 param: s(),
2366 fn_name: s(),
2367 },
2368 IssueKind::NullPropertyFetch { property: s() },
2369 IssueKind::NullMethodCall { method: s() },
2370 IssueKind::NullArrayAccess,
2371 IssueKind::PossiblyNullArgument {
2372 param: s(),
2373 fn_name: s(),
2374 },
2375 IssueKind::PossiblyInvalidArgument {
2376 param: s(),
2377 fn_name: s(),
2378 expected: s(),
2379 actual: s(),
2380 },
2381 IssueKind::PossiblyNullPropertyFetch { property: s() },
2382 IssueKind::PossiblyNullMethodCall { method: s() },
2383 IssueKind::PossiblyNullArrayAccess,
2384 IssueKind::NullableReturnStatement {
2385 expected: s(),
2386 actual: s(),
2387 },
2388 IssueKind::InvalidReturnType {
2389 expected: s(),
2390 actual: s(),
2391 },
2392 IssueKind::InvalidArgument {
2393 param: s(),
2394 fn_name: s(),
2395 expected: s(),
2396 actual: s(),
2397 },
2398 IssueKind::TooFewArguments {
2399 fn_name: s(),
2400 expected: 0,
2401 actual: 0,
2402 },
2403 IssueKind::TooManyArguments {
2404 fn_name: s(),
2405 expected: 0,
2406 actual: 0,
2407 },
2408 IssueKind::InvalidNamedArgument {
2409 fn_name: s(),
2410 name: s(),
2411 },
2412 IssueKind::InvalidNamedArguments { fn_name: s() },
2413 IssueKind::InvalidPassByReference {
2414 fn_name: s(),
2415 param: s(),
2416 },
2417 IssueKind::InvalidPropertyFetch { ty: s() },
2418 IssueKind::InvalidArrayAccess { ty: s() },
2419 IssueKind::PossiblyInvalidArrayAccess { ty: s() },
2420 IssueKind::InvalidArrayAssignment { ty: s() },
2421 IssueKind::InvalidPropertyAssignment {
2422 property: s(),
2423 expected: s(),
2424 actual: s(),
2425 },
2426 IssueKind::InvalidCast { from: s(), to: s() },
2427 IssueKind::InvalidStaticInvocation {
2428 class: s(),
2429 method: s(),
2430 },
2431 IssueKind::InvalidOperand {
2432 op: s(),
2433 left: s(),
2434 right: s(),
2435 },
2436 IssueKind::PossiblyInvalidOperand {
2437 op: s(),
2438 left: s(),
2439 right: s(),
2440 },
2441 IssueKind::PossiblyNullOperand { op: s(), ty: s() },
2442 IssueKind::DivisionByZero { op: s() },
2443 IssueKind::RawObjectIteration { ty: s() },
2444 IssueKind::PossiblyRawObjectIteration { ty: s() },
2445 IssueKind::MismatchingDocblockReturnType {
2446 declared: s(),
2447 inferred: s(),
2448 },
2449 IssueKind::MismatchingDocblockParamType {
2450 param: s(),
2451 declared: s(),
2452 inferred: s(),
2453 },
2454 IssueKind::TypeCheckMismatch {
2455 var: s(),
2456 expected: s(),
2457 actual: s(),
2458 },
2459 IssueKind::Trace {
2460 variable: s(),
2461 type_info: s(),
2462 },
2463 IssueKind::InvalidArrayOffset {
2464 expected: s(),
2465 actual: s(),
2466 },
2467 IssueKind::NonExistentArrayOffset { key: s() },
2468 IssueKind::PossiblyInvalidArrayOffset {
2469 expected: s(),
2470 actual: s(),
2471 },
2472 IssueKind::DuplicateArrayKey { key: s() },
2473 IssueKind::RedundantCondition { ty: s() },
2474 IssueKind::RedundantCast { from: s(), to: s() },
2475 IssueKind::UnnecessaryVarAnnotation { var: s() },
2476 IssueKind::TypeDoesNotContainType {
2477 left: s(),
2478 right: s(),
2479 },
2480 IssueKind::UnusedVariable { name: s() },
2481 IssueKind::UnusedParam { name: s() },
2482 IssueKind::UnreachableCode,
2483 IssueKind::UnhandledMatchCondition { detail: s() },
2484 IssueKind::UnusedMethod {
2485 class: s(),
2486 method: s(),
2487 },
2488 IssueKind::UnusedProperty {
2489 class: s(),
2490 property: s(),
2491 },
2492 IssueKind::UnusedFunction { name: s() },
2493 IssueKind::UnusedForeachValue { name: s() },
2494 IssueKind::UnusedClass { class: s() },
2495 IssueKind::UnusedSuppress { kind: s() },
2496 IssueKind::ArgumentTypeCoercion {
2497 param: s(),
2498 fn_name: s(),
2499 expected: s(),
2500 actual: s(),
2501 },
2502 IssueKind::PropertyTypeCoercion {
2503 property: s(),
2504 expected: s(),
2505 actual: s(),
2506 },
2507 IssueKind::ImpurePropertyAssignment { property: s() },
2508 IssueKind::ImpureMethodCall { method: s() },
2509 IssueKind::ImpureGlobalVariable { variable: s() },
2510 IssueKind::ImpureStaticVariable { variable: s() },
2511 IssueKind::ImpureByRefAssignment { variable: s() },
2512 IssueKind::ImpureStaticPropertyAssignment {
2513 class: s(),
2514 property: s(),
2515 },
2516 IssueKind::ImpureStaticPropertyAccess {
2517 class: s(),
2518 property: s(),
2519 },
2520 IssueKind::ImpureFunctionCall { fn_name: s() },
2521 IssueKind::ImmutablePropertyModification {
2522 receiver: s(),
2523 property: s(),
2524 },
2525 IssueKind::ReadonlyPropertyAssignment {
2526 class: s(),
2527 property: s(),
2528 },
2529 IssueKind::ReadonlyPropertyAlreadyInitialized {
2530 class: s(),
2531 property: s(),
2532 },
2533 IssueKind::UnimplementedAbstractMethod {
2534 class: s(),
2535 method: s(),
2536 },
2537 IssueKind::UnimplementedInterfaceMethod {
2538 class: s(),
2539 interface: s(),
2540 method: s(),
2541 },
2542 IssueKind::MethodSignatureMismatch {
2543 class: s(),
2544 method: s(),
2545 detail: s(),
2546 },
2547 IssueKind::OverriddenMethodAccess {
2548 class: s(),
2549 method: s(),
2550 },
2551 IssueKind::OverriddenPropertyAccess {
2552 class: s(),
2553 property: s(),
2554 },
2555 IssueKind::PropertyTypeRedeclarationMismatch {
2556 class: s(),
2557 property: s(),
2558 expected: s(),
2559 actual: s(),
2560 },
2561 IssueKind::ReadonlyPropertyRedeclarationMismatch {
2562 parent_class: s(),
2563 class: s(),
2564 property: s(),
2565 parent_readonly: true,
2566 },
2567 IssueKind::StaticPropertyRedeclarationMismatch {
2568 parent_class: s(),
2569 class: s(),
2570 property: s(),
2571 parent_static: true,
2572 },
2573 IssueKind::ReadonlyClassExtendsMismatch {
2574 parent: s(),
2575 child: s(),
2576 parent_readonly: true,
2577 },
2578 IssueKind::InvalidReadonlyPropertyDeclaration {
2579 class: s(),
2580 property: s(),
2581 },
2582 IssueKind::BackedEnumCaseTypeMismatch {
2583 enum_name: s(),
2584 case_name: s(),
2585 expected: s(),
2586 actual: s(),
2587 },
2588 IssueKind::InvalidExtendClass {
2589 parent: s(),
2590 child: s(),
2591 },
2592 IssueKind::FinalMethodOverridden {
2593 class: s(),
2594 method: s(),
2595 parent: s(),
2596 },
2597 IssueKind::AbstractInstantiation { class: s() },
2598 IssueKind::AbstractMethodCall {
2599 class: s(),
2600 method: s(),
2601 },
2602 IssueKind::InterfaceInstantiation { class: s() },
2603 IssueKind::InvalidOverride {
2604 class: s(),
2605 method: s(),
2606 detail: s(),
2607 },
2608 IssueKind::CircularInheritance { class: s() },
2609 IssueKind::TaintedInput { sink: s() },
2610 IssueKind::TaintedHtml,
2611 IssueKind::TaintedSql,
2612 IssueKind::TaintedShell,
2613 IssueKind::TaintedLlmPrompt,
2614 IssueKind::InvalidTemplateParam {
2615 name: s(),
2616 expected_bound: s(),
2617 actual: s(),
2618 },
2619 IssueKind::ShadowedTemplateParam { name: s() },
2620 IssueKind::DeprecatedCall {
2621 name: s(),
2622 message: None,
2623 },
2624 IssueKind::DeprecatedProperty {
2625 class: s(),
2626 property: s(),
2627 message: None,
2628 },
2629 IssueKind::DeprecatedConstant {
2630 class: s(),
2631 constant: s(),
2632 message: None,
2633 },
2634 IssueKind::DeprecatedInterface {
2635 name: s(),
2636 message: None,
2637 },
2638 IssueKind::DeprecatedTrait {
2639 name: s(),
2640 message: None,
2641 },
2642 IssueKind::DeprecatedMethodCall {
2643 class: s(),
2644 method: s(),
2645 message: None,
2646 },
2647 IssueKind::DeprecatedMethod {
2648 class: s(),
2649 method: s(),
2650 message: None,
2651 },
2652 IssueKind::DeprecatedClass {
2653 name: s(),
2654 message: None,
2655 },
2656 IssueKind::InternalMethod {
2657 class: s(),
2658 method: s(),
2659 },
2660 IssueKind::MissingReturnType { fn_name: s() },
2661 IssueKind::MissingClosureReturnType,
2662 IssueKind::MissingParamType {
2663 fn_name: s(),
2664 param: s(),
2665 },
2666 IssueKind::MissingPropertyType {
2667 class: s(),
2668 property: s(),
2669 },
2670 IssueKind::MissingThrowsDocblock { class: s() },
2671 IssueKind::InvalidDocblock { message: s() },
2672 IssueKind::UndefinedThrowsDocblock { name: s() },
2673 IssueKind::InvalidDocblockType { message: s() },
2674 IssueKind::MixedArgument {
2675 param: s(),
2676 fn_name: s(),
2677 },
2678 IssueKind::MixedAssignment { var: s() },
2679 IssueKind::MixedMethodCall { method: s() },
2680 IssueKind::UnsupportedReferenceUsage,
2681 IssueKind::NoInterfaceProperties { property: s() },
2682 IssueKind::UndefinedDocblockClass { name: s() },
2683 IssueKind::MissingConstructor { class: s() },
2684 IssueKind::PropertyPossiblyUninitialized {
2685 class: s(),
2686 property: s(),
2687 },
2688 IssueKind::MixedFunctionCall,
2689 IssueKind::MixedReturnStatement { declared: s() },
2690 IssueKind::MixedPropertyFetch { property: s() },
2691 IssueKind::MixedPropertyAssignment { property: s() },
2692 IssueKind::MixedArrayAccess,
2693 IssueKind::MixedArrayOffset,
2694 IssueKind::MixedClone,
2695 IssueKind::InvalidClone { ty: s() },
2696 IssueKind::PossiblyInvalidClone { ty: s() },
2697 IssueKind::InvalidToString { class: s() },
2698 IssueKind::InvalidTraitUse {
2699 trait_name: s(),
2700 reason: s(),
2701 },
2702 IssueKind::ParseError { message: s() },
2703 IssueKind::InvalidThrow { ty: s() },
2704 IssueKind::InvalidCatch { ty: s() },
2705 IssueKind::UnreachableCatch {
2706 ty: s(),
2707 shadowed_by: s(),
2708 },
2709 IssueKind::ImplicitToStringCast { class: s() },
2710 IssueKind::ImplicitFloatToIntCast { from: s() },
2711 IssueKind::WrongCaseFunction {
2712 used: s(),
2713 canonical: s(),
2714 },
2715 IssueKind::WrongCaseMethod {
2716 class: s(),
2717 used: s(),
2718 canonical: s(),
2719 },
2720 IssueKind::WrongCaseClass {
2721 used: s(),
2722 canonical: s(),
2723 },
2724 IssueKind::InvalidAttribute { message: s() },
2725 IssueKind::UndefinedAttributeClass { name: s() },
2726 IssueKind::ForbiddenCode { message: s() },
2727 IssueKind::DuplicateClass { name: s() },
2728 IssueKind::DuplicateInterface { name: s() },
2729 IssueKind::DuplicateTrait { name: s() },
2730 IssueKind::DuplicateEnum { name: s() },
2731 IssueKind::DuplicateFunction { name: s() },
2732 ]
2733 }
2734
2735 #[test]
2736 fn codes_have_expected_shape() {
2737 for kind in one_of_each() {
2738 let code = kind.code();
2739 assert!(
2740 code.len() == 7
2741 && code.starts_with("MIR")
2742 && code[3..].chars().all(|c| c.is_ascii_digit()),
2743 "code {code:?} for {} does not match MIR####",
2744 kind.name(),
2745 );
2746 }
2747 }
2748
2749 #[test]
2750 fn codes_are_unique() {
2751 let kinds = one_of_each();
2752 let mut seen: HashSet<&'static str> = HashSet::new();
2753 for kind in &kinds {
2754 assert!(
2755 seen.insert(kind.code()),
2756 "duplicate code {} (variant {})",
2757 kind.code(),
2758 kind.name(),
2759 );
2760 }
2761 }
2762
2763 #[test]
2764 fn default_severity_for_code_round_trips() {
2765 for kind in one_of_each() {
2766 let code = kind.code();
2767 assert_eq!(
2768 IssueKind::default_severity_for_code(code),
2769 Some(kind.default_severity()),
2770 "severity mismatch for {code} (variant {})",
2771 kind.name(),
2772 );
2773 }
2774 }
2775
2776 #[test]
2777 fn default_severity_for_code_unknown_returns_none() {
2778 assert_eq!(IssueKind::default_severity_for_code("MIR9999"), None);
2779 assert_eq!(IssueKind::default_severity_for_code(""), None);
2780 assert_eq!(IssueKind::default_severity_for_code("mir0001"), None);
2781 }
2782
2783 #[test]
2786 fn one_of_each_has_every_variant() {
2787 assert_eq!(one_of_each().len(), 160);
2790 }
2791}