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 UndefinedConstant { name: String },
72 InaccessibleClassConstant { class: String, constant: String },
75 PossiblyUndefinedVariable { name: String },
78 UndefinedTrait { name: String },
81 TraitConstantAccessedDirectly {
88 trait_name: String,
89 constant: String,
90 },
91 UndefinedTraitAliasMethod {
99 trait_name: Option<String>,
100 method: String,
101 },
102 ParentNotFound,
105 InvalidStringClass { actual: String },
108 NotAnInterface { name: String },
112
113 NullArgument { param: String, fn_name: String },
117 NullPropertyFetch { property: String },
120 NullMethodCall { method: String },
123 NullArrayAccess,
126 PossiblyNullArgument { param: String, fn_name: String },
129 PossiblyInvalidArgument {
132 param: String,
133 fn_name: String,
134 expected: String,
135 actual: String,
136 },
137 PossiblyNullPropertyFetch { property: String },
140 PossiblyNullMethodCall { method: String },
143 PossiblyNullArrayAccess,
146 NullableReturnStatement { expected: String, actual: String },
149
150 InvalidReturnType { expected: String, actual: String },
154 InvalidArgument {
157 param: String,
158 fn_name: String,
159 expected: String,
160 actual: String,
161 },
162 TooFewArguments {
165 fn_name: String,
166 expected: usize,
167 actual: usize,
168 },
169 TooManyArguments {
172 fn_name: String,
173 expected: usize,
174 actual: usize,
175 },
176 InvalidNamedArgument { fn_name: String, name: String },
179 InvalidNamedArguments { fn_name: String },
182 InvalidPassByReference { fn_name: String, param: String },
185 InvalidPropertyFetch { ty: String },
188 InvalidArrayAccess { ty: String },
191 PossiblyInvalidArrayAccess { ty: String },
194 InvalidArrayAssignment { ty: String },
197 InvalidPropertyAssignment {
200 property: String,
201 expected: String,
202 actual: String,
203 },
204 InvalidCast { from: String, to: String },
207 InvalidStaticInvocation { class: String, method: String },
210 InvalidOperand {
214 op: String,
215 left: String,
216 right: String,
217 },
218 PossiblyInvalidOperand {
221 op: String,
222 left: String,
223 right: String,
224 },
225 PossiblyNullOperand { op: String, ty: String },
228 DivisionByZero { op: String },
232 RawObjectIteration { ty: String },
235 PossiblyRawObjectIteration { ty: String },
238 MismatchingDocblockReturnType { declared: String, inferred: String },
241 MismatchingDocblockParamType {
244 param: String,
245 declared: String,
246 inferred: String,
247 },
248 TypeCheckMismatch {
254 var: String,
255 expected: String,
256 actual: String,
257 },
258
259 Trace { variable: String, type_info: String },
262
263 InvalidArrayOffset { expected: String, actual: String },
267 NonExistentArrayOffset { key: String },
271 PossiblyInvalidArrayOffset { expected: String, actual: String },
274 DuplicateArrayKey { key: String },
279
280 RedundantCondition { ty: String },
284 RedundantCast { from: String, to: String },
287 UnnecessaryVarAnnotation { var: String },
290 TypeDoesNotContainType { left: String, right: String },
293 ParadoxicalCondition { value: String },
296 DocblockTypeContradiction { expr: String, declared: String },
301 ImpossibleIdenticalComparison {
307 op: String,
308 left: String,
309 right: String,
310 },
311 ImpossibleLooseComparison {
318 op: String,
319 left: String,
320 right: String,
321 },
322 UnevaluatedCode { reason: String },
329
330 UnusedVariable { name: String },
334 UnusedParam { name: String },
337 UnreachableCode,
340 UnhandledMatchCondition { detail: String },
343 UnusedMethod { class: String, method: String },
346 UnusedProperty { class: String, property: String },
349 UnusedFunction { name: String },
352 UnusedForeachValue { name: String },
355 UnusedClass { class: String },
358 UnusedSuppress { kind: String },
362
363 ArgumentTypeCoercion {
366 param: String,
367 fn_name: String,
368 expected: String,
369 actual: String,
370 },
371
372 PropertyTypeCoercion {
375 property: String,
376 expected: String,
377 actual: String,
378 },
379
380 ImpurePropertyAssignment { property: String },
383 ImpureMethodCall { method: String },
385 ImpureGlobalVariable { variable: String },
387 ImpureStaticVariable { variable: String },
389 ImpureStaticPropertyAssignment { class: String, property: String },
393 ImpureStaticPropertyAccess { class: String, property: String },
398 ImpureByRefAssignment { variable: String },
403 ImpureFunctionCall { fn_name: String },
407 ImmutablePropertyModification { receiver: String, property: String },
413
414 ReadonlyPropertyAssignment { class: String, property: String },
418 ReadonlyPropertyAlreadyInitialized { class: String, property: String },
425
426 UnimplementedAbstractMethod { class: String, method: String },
430 UnimplementedInterfaceMethod {
433 class: String,
434 interface: String,
435 method: String,
436 },
437 MethodSignatureMismatch {
440 class: String,
441 method: String,
442 detail: String,
443 },
444 OverriddenMethodAccess { class: String, method: String },
447 OverriddenPropertyAccess { class: String, property: String },
450 PropertyTypeRedeclarationMismatch {
453 class: String,
454 property: String,
455 expected: String,
456 actual: String,
457 },
458 ReadonlyPropertyRedeclarationMismatch {
461 parent_class: String,
462 class: String,
463 property: String,
464 parent_readonly: bool,
467 },
468 StaticPropertyRedeclarationMismatch {
471 parent_class: String,
472 class: String,
473 property: String,
474 parent_static: bool,
477 },
478 ReadonlyClassExtendsMismatch {
484 parent: String,
485 child: String,
486 parent_readonly: bool,
489 },
490 InvalidReadonlyPropertyDeclaration { class: String, property: String },
496 BackedEnumCaseTypeMismatch {
499 enum_name: String,
500 case_name: String,
501 expected: String,
502 actual: String,
503 },
504 DirectConstructorCall { class: String },
507 InvalidExtendClass { parent: String, child: String },
510 FinalMethodOverridden {
513 class: String,
514 method: String,
515 parent: String,
516 },
517 AbstractInstantiation { class: String },
520 AbstractMethodCall { class: String, method: String },
523 InterfaceInstantiation { class: String },
526 InvalidOverride {
530 class: String,
531 method: String,
532 detail: String,
533 },
534
535 TaintedInput { sink: String },
539 TaintedHtml,
542 TaintedSql,
545 TaintedShell,
548 TaintedLlmPrompt,
552
553 InvalidTemplateParam {
557 name: String,
558 expected_bound: String,
559 actual: String,
560 },
561 ShadowedTemplateParam { name: String },
564 IfThisIsMismatch {
569 class: String,
570 method: String,
571 expected: String,
572 actual: String,
573 },
574
575 DeprecatedCall {
579 name: String,
580 message: Option<Arc<str>>,
581 },
582 DeprecatedProperty {
585 class: String,
586 property: String,
587 message: Option<Arc<str>>,
588 },
589 DeprecatedConstant {
592 class: String,
593 constant: String,
594 message: Option<Arc<str>>,
595 },
596 DeprecatedInterface {
599 name: String,
600 message: Option<Arc<str>>,
601 },
602 DeprecatedTrait {
605 name: String,
606 message: Option<Arc<str>>,
607 },
608 DeprecatedMethodCall {
611 class: String,
612 method: String,
613 message: Option<Arc<str>>,
614 },
615 DeprecatedMethod {
618 class: String,
619 method: String,
620 message: Option<Arc<str>>,
621 },
622 DeprecatedClass {
625 name: String,
626 message: Option<Arc<str>>,
627 },
628 InternalMethod { class: String, method: String },
631 MissingReturnType { fn_name: String },
634 MissingClosureReturnType,
637 MissingParamType { fn_name: String, param: String },
640 MissingPropertyType { class: String, property: String },
643 InvalidThrow { ty: String },
646 InvalidCatch { ty: String },
649 UnreachableCatch { ty: String, shadowed_by: String },
654 MissingThrowsDocblock { class: String },
657 ImplicitToStringCast { class: String },
660 ImplicitFloatToIntCast { from: String },
663 ParseError { message: String },
666 InvalidDocblock { message: String },
669 MixedArgument { param: String, fn_name: String },
672 MixedAssignment { var: String },
675 MixedMethodCall { method: String },
678 UnsupportedReferenceUsage,
681 NoInterfaceProperties { property: String },
685 UndefinedDocblockClass { name: String },
689 MissingConstructor { class: String },
692 PropertyPossiblyUninitialized { class: String, property: String },
704 MixedFunctionCall,
707 MixedReturnStatement { declared: String },
710 MixedPropertyFetch { property: String },
713 MixedPropertyAssignment { property: String },
716 MixedArrayAccess,
719 MixedArrayOffset,
722 MixedClone,
725 InvalidClone { ty: String },
729 PossiblyInvalidClone { ty: String },
733 InvalidToString { class: String },
737 CircularInheritance { class: String },
740
741 InvalidTraitUse { trait_name: String, reason: String },
745 ForbiddenCode { message: String },
748
749 InvalidAttribute { message: String },
753 UndefinedAttributeClass { name: String },
756
757 WrongCaseFunction { used: String, canonical: String },
761 WrongCaseMethod {
764 class: String,
765 used: String,
766 canonical: String,
767 },
768 WrongCaseClass { used: String, canonical: String },
771 DuplicateClass { name: String },
774 DuplicateInterface { name: String },
777 DuplicateTrait { name: String },
780 DuplicateEnum { name: String },
783 DuplicateFunction { name: String },
786
787 PluginIssue { name: String, message: String },
791}
792
793fn append_deprecation_message(base: String, message: &Option<Arc<str>>) -> String {
794 match message.as_deref().filter(|m| !m.is_empty()) {
795 Some(msg) => format!("{base}: {msg}"),
796 None => base,
797 }
798}
799
800impl IssueKind {
801 pub fn default_severity(&self) -> Severity {
803 match self {
804 IssueKind::NonStaticSelfCall { .. }
806 | IssueKind::DirectConstructorCall { .. }
807 | IssueKind::InvalidScope { .. }
808 | IssueKind::UndefinedVariable { .. }
809 | IssueKind::UndefinedFunction { .. }
810 | IssueKind::UndefinedMethod { .. }
811 | IssueKind::UndefinedClass { .. }
812 | IssueKind::NotAnInterface { .. }
813 | IssueKind::UndefinedConstant { .. }
814 | IssueKind::InaccessibleClassConstant { .. }
815 | IssueKind::InvalidReturnType { .. }
816 | IssueKind::InvalidArgument { .. }
817 | IssueKind::TooFewArguments { .. }
818 | IssueKind::TooManyArguments { .. }
819 | IssueKind::InvalidNamedArgument { .. }
820 | IssueKind::InvalidNamedArguments { .. }
821 | IssueKind::InvalidPassByReference { .. }
822 | IssueKind::InvalidThrow { .. }
823 | IssueKind::InvalidCatch { .. }
824 | IssueKind::InvalidStaticInvocation { .. }
825 | IssueKind::UnimplementedAbstractMethod { .. }
826 | IssueKind::UnimplementedInterfaceMethod { .. }
827 | IssueKind::MethodSignatureMismatch { .. }
828 | IssueKind::InvalidExtendClass { .. }
829 | IssueKind::FinalMethodOverridden { .. }
830 | IssueKind::AbstractInstantiation { .. }
831 | IssueKind::AbstractMethodCall { .. }
832 | IssueKind::InterfaceInstantiation { .. }
833 | IssueKind::InvalidOverride { .. }
834 | IssueKind::InvalidTemplateParam { .. }
835 | IssueKind::ReadonlyPropertyAssignment { .. }
836 | IssueKind::ReadonlyPropertyAlreadyInitialized { .. }
837 | IssueKind::ParseError { .. }
838 | IssueKind::TaintedInput { .. }
839 | IssueKind::TaintedHtml
840 | IssueKind::TaintedSql
841 | IssueKind::TaintedShell
842 | IssueKind::TaintedLlmPrompt
843 | IssueKind::CircularInheritance { .. }
844 | IssueKind::InvalidTraitUse { .. }
845 | IssueKind::UndefinedTrait { .. }
846 | IssueKind::TraitConstantAccessedDirectly { .. }
847 | IssueKind::UndefinedTraitAliasMethod { .. }
848 | IssueKind::InvalidClone { .. }
849 | IssueKind::InvalidToString { .. }
850 | IssueKind::TypeCheckMismatch { .. }
851 | IssueKind::PropertyTypeRedeclarationMismatch { .. }
852 | IssueKind::ReadonlyPropertyRedeclarationMismatch { .. }
853 | IssueKind::StaticPropertyRedeclarationMismatch { .. }
854 | IssueKind::ReadonlyClassExtendsMismatch { .. }
855 | IssueKind::InvalidReadonlyPropertyDeclaration { .. }
856 | IssueKind::BackedEnumCaseTypeMismatch { .. }
857 | IssueKind::DivisionByZero { .. }
858 | IssueKind::ParentNotFound => Severity::Error,
859 IssueKind::Trace { .. } => Severity::Info,
860
861 IssueKind::NullArgument { .. }
863 | IssueKind::NullPropertyFetch { .. }
864 | IssueKind::NullMethodCall { .. }
865 | IssueKind::NullArrayAccess
866 | IssueKind::NullableReturnStatement { .. }
867 | IssueKind::InvalidPropertyFetch { .. }
868 | IssueKind::InvalidArrayAccess { .. }
869 | IssueKind::InvalidArrayAssignment { .. }
870 | IssueKind::InvalidPropertyAssignment { .. }
871 | IssueKind::InvalidArrayOffset { .. }
872 | IssueKind::NonExistentArrayOffset { .. }
873 | IssueKind::PossiblyInvalidArrayOffset { .. }
874 | IssueKind::UndefinedProperty { .. }
875 | IssueKind::InvalidOperand { .. }
876 | IssueKind::OverriddenMethodAccess { .. }
877 | IssueKind::OverriddenPropertyAccess { .. }
878 | IssueKind::ImplicitToStringCast { .. }
879 | IssueKind::ImplicitFloatToIntCast { .. }
880 | IssueKind::UnusedVariable { .. }
881 | IssueKind::UnusedForeachValue { .. }
882 | IssueKind::ImpurePropertyAssignment { .. }
883 | IssueKind::ImpureMethodCall { .. }
884 | IssueKind::ImpureGlobalVariable { .. }
885 | IssueKind::ImpureStaticVariable { .. }
886 | IssueKind::ImpureStaticPropertyAssignment { .. }
887 | IssueKind::ImpureStaticPropertyAccess { .. }
888 | IssueKind::ImpureFunctionCall { .. }
889 | IssueKind::ImpureByRefAssignment { .. }
890 | IssueKind::ImmutablePropertyModification { .. }
891 | IssueKind::UnsupportedReferenceUsage
892 | IssueKind::ParadoxicalCondition { .. }
893 | IssueKind::UnhandledMatchCondition { .. }
894 | IssueKind::InvalidStringClass { .. }
895 | IssueKind::ImpossibleIdenticalComparison { .. }
896 | IssueKind::ImpossibleLooseComparison { .. }
897 | IssueKind::DuplicateArrayKey { .. }
898 | IssueKind::ForbiddenCode { .. }
899 | IssueKind::PropertyPossiblyUninitialized { .. } => Severity::Warning,
900
901 IssueKind::PossiblyUndefinedVariable { .. } => Severity::Warning,
903
904 IssueKind::PossiblyNullArgument { .. }
906 | IssueKind::PossiblyInvalidArgument { .. }
907 | IssueKind::PossiblyNullPropertyFetch { .. }
908 | IssueKind::PossiblyNullMethodCall { .. }
909 | IssueKind::PossiblyNullArrayAccess
910 | IssueKind::PossiblyInvalidArrayAccess { .. }
911 | IssueKind::PossiblyInvalidClone { .. }
912 | IssueKind::PossiblyInvalidOperand { .. }
913 | IssueKind::PossiblyNullOperand { .. }
914 | IssueKind::PossiblyRawObjectIteration { .. } => Severity::Info,
915
916 IssueKind::RawObjectIteration { .. } => Severity::Warning,
917
918 IssueKind::RedundantCondition { .. }
920 | IssueKind::RedundantCast { .. }
921 | IssueKind::UnnecessaryVarAnnotation { .. }
922 | IssueKind::TypeDoesNotContainType { .. }
923 | IssueKind::DocblockTypeContradiction { .. }
924 | IssueKind::UnevaluatedCode { .. }
925 | IssueKind::IfThisIsMismatch { .. }
926 | IssueKind::UnusedParam { .. }
927 | IssueKind::UnreachableCode
928 | IssueKind::UnreachableCatch { .. }
929 | IssueKind::UnusedMethod { .. }
930 | IssueKind::UnusedProperty { .. }
931 | IssueKind::UnusedFunction { .. }
932 | IssueKind::UnusedClass { .. }
933 | IssueKind::UnusedSuppress { .. }
934 | IssueKind::ArgumentTypeCoercion { .. }
935 | IssueKind::PropertyTypeCoercion { .. }
936 | IssueKind::DeprecatedCall { .. }
937 | IssueKind::DeprecatedProperty { .. }
938 | IssueKind::DeprecatedConstant { .. }
939 | IssueKind::DeprecatedInterface { .. }
940 | IssueKind::DeprecatedTrait { .. }
941 | IssueKind::DeprecatedMethodCall { .. }
942 | IssueKind::DeprecatedMethod { .. }
943 | IssueKind::DeprecatedClass { .. }
944 | IssueKind::InternalMethod { .. }
945 | IssueKind::MissingReturnType { .. }
946 | IssueKind::MissingClosureReturnType
947 | IssueKind::MissingParamType { .. }
948 | IssueKind::MissingPropertyType { .. }
949 | IssueKind::MismatchingDocblockReturnType { .. }
950 | IssueKind::MismatchingDocblockParamType { .. }
951 | IssueKind::InvalidDocblock { .. }
952 | IssueKind::InvalidCast { .. }
953 | IssueKind::MixedArgument { .. }
954 | IssueKind::MixedAssignment { .. }
955 | IssueKind::MixedMethodCall { .. }
956 | IssueKind::NoInterfaceProperties { .. }
957 | IssueKind::UndefinedDocblockClass { .. }
958 | IssueKind::MissingConstructor { .. }
959 | IssueKind::MixedFunctionCall
960 | IssueKind::MixedReturnStatement { .. }
961 | IssueKind::MixedPropertyFetch { .. }
962 | IssueKind::MixedPropertyAssignment { .. }
963 | IssueKind::MixedArrayAccess
964 | IssueKind::MixedArrayOffset
965 | IssueKind::MixedClone
966 | IssueKind::ShadowedTemplateParam { .. }
967 | IssueKind::MissingThrowsDocblock { .. }
968 | IssueKind::WrongCaseFunction { .. }
969 | IssueKind::WrongCaseMethod { .. }
970 | IssueKind::WrongCaseClass { .. }
971 | IssueKind::InvalidAttribute { .. }
972 | IssueKind::UndefinedAttributeClass { .. } => Severity::Info,
973 IssueKind::DuplicateClass { .. }
974 | IssueKind::DuplicateInterface { .. }
975 | IssueKind::DuplicateTrait { .. }
976 | IssueKind::DuplicateEnum { .. }
977 | IssueKind::DuplicateFunction { .. } => Severity::Error,
978 IssueKind::PluginIssue { .. } => Severity::Error,
981 }
982 }
983
984 pub fn code(&self) -> &'static str {
1014 match self {
1015 IssueKind::NonStaticSelfCall { .. } => "MIR0216",
1017 IssueKind::DirectConstructorCall { .. } => "MIR0217",
1018 IssueKind::InvalidScope { .. } => "MIR0001",
1019 IssueKind::UndefinedVariable { .. } => "MIR0002",
1020 IssueKind::UndefinedFunction { .. } => "MIR0003",
1021 IssueKind::UndefinedMethod { .. } => "MIR0004",
1022 IssueKind::UndefinedClass { .. } => "MIR0005",
1023 IssueKind::UndefinedProperty { .. } => "MIR0006",
1024 IssueKind::UndefinedConstant { .. } => "MIR0007",
1025 IssueKind::InaccessibleClassConstant { .. } => "MIR0011",
1026 IssueKind::TraitConstantAccessedDirectly { .. } => "MIR0012",
1027 IssueKind::UndefinedTraitAliasMethod { .. } => "MIR0013",
1028 IssueKind::PossiblyUndefinedVariable { .. } => "MIR0008",
1029 IssueKind::UndefinedTrait { .. } => "MIR0009",
1030 IssueKind::ParentNotFound => "MIR0010",
1031
1032 IssueKind::NullArgument { .. } => "MIR0100",
1034 IssueKind::NullPropertyFetch { .. } => "MIR0101",
1035 IssueKind::NullMethodCall { .. } => "MIR0102",
1036 IssueKind::NullArrayAccess => "MIR0103",
1037 IssueKind::PossiblyNullArgument { .. } => "MIR0104",
1038 IssueKind::PossiblyInvalidArgument { .. } => "MIR0105",
1039 IssueKind::PossiblyNullPropertyFetch { .. } => "MIR0106",
1040 IssueKind::PossiblyNullMethodCall { .. } => "MIR0107",
1041 IssueKind::PossiblyNullArrayAccess => "MIR0108",
1042 IssueKind::NullableReturnStatement { .. } => "MIR0109",
1043
1044 IssueKind::InvalidReturnType { .. } => "MIR0200",
1046 IssueKind::InvalidArgument { .. } => "MIR0201",
1047 IssueKind::TooFewArguments { .. } => "MIR0202",
1048 IssueKind::TooManyArguments { .. } => "MIR0203",
1049 IssueKind::InvalidNamedArgument { .. } => "MIR0204",
1050 IssueKind::InvalidNamedArguments { .. } => "MIR0224",
1051 IssueKind::InvalidPassByReference { .. } => "MIR0205",
1052 IssueKind::InvalidPropertyFetch { .. } => "MIR0218",
1053 IssueKind::InvalidArrayAccess { .. } => "MIR0219",
1054 IssueKind::PossiblyInvalidArrayAccess { .. } => "MIR0227",
1055 IssueKind::InvalidArrayAssignment { .. } => "MIR0220",
1056 IssueKind::InvalidPropertyAssignment { .. } => "MIR0206",
1057 IssueKind::InvalidCast { .. } => "MIR0207",
1058 IssueKind::InvalidStaticInvocation { .. } => "MIR0215",
1059 IssueKind::InvalidOperand { .. } => "MIR0208",
1060 IssueKind::PossiblyInvalidOperand { .. } => "MIR0213",
1061 IssueKind::PossiblyNullOperand { .. } => "MIR0214",
1062 IssueKind::DivisionByZero { .. } => "MIR0229",
1063 IssueKind::RawObjectIteration { .. } => "MIR0222",
1064 IssueKind::PossiblyRawObjectIteration { .. } => "MIR0223",
1065 IssueKind::MismatchingDocblockReturnType { .. } => "MIR0209",
1066 IssueKind::MismatchingDocblockParamType { .. } => "MIR0210",
1067 IssueKind::InvalidStringClass { .. } => "MIR0211",
1068 IssueKind::NotAnInterface { .. } => "MIR0228",
1069 IssueKind::TypeCheckMismatch { .. } => "MIR0212",
1070 IssueKind::Trace { .. } => "MIR0221",
1071 IssueKind::ArgumentTypeCoercion { .. } => "MIR0225",
1072 IssueKind::PropertyTypeCoercion { .. } => "MIR0226",
1073
1074 IssueKind::InvalidArrayOffset { .. } => "MIR0300",
1076 IssueKind::NonExistentArrayOffset { .. } => "MIR0301",
1077 IssueKind::PossiblyInvalidArrayOffset { .. } => "MIR0302",
1078 IssueKind::DuplicateArrayKey { .. } => "MIR0303",
1079
1080 IssueKind::RedundantCondition { .. } => "MIR0400",
1082 IssueKind::RedundantCast { .. } => "MIR0401",
1083 IssueKind::UnnecessaryVarAnnotation { .. } => "MIR0402",
1084 IssueKind::TypeDoesNotContainType { .. } => "MIR0403",
1085 IssueKind::ParadoxicalCondition { .. } => "MIR0404",
1086 IssueKind::UnhandledMatchCondition { .. } => "MIR0405",
1087 IssueKind::DocblockTypeContradiction { .. } => "MIR0406",
1088 IssueKind::UnevaluatedCode { .. } => "MIR0407",
1089 IssueKind::ImpossibleIdenticalComparison { .. } => "MIR0408",
1090 IssueKind::ImpossibleLooseComparison { .. } => "MIR0409",
1091
1092 IssueKind::UnusedVariable { .. } => "MIR0500",
1094 IssueKind::UnusedParam { .. } => "MIR0501",
1095 IssueKind::UnreachableCode => "MIR0502",
1096 IssueKind::UnusedMethod { .. } => "MIR0503",
1097 IssueKind::UnusedProperty { .. } => "MIR0504",
1098 IssueKind::UnusedFunction { .. } => "MIR0505",
1099 IssueKind::UnusedForeachValue { .. } => "MIR0506",
1100 IssueKind::UnusedClass { .. } => "MIR0507",
1101 IssueKind::UnusedSuppress { .. } => "MIR0508",
1102
1103 IssueKind::ImpurePropertyAssignment { .. } => "MIR1700",
1105 IssueKind::ImpureMethodCall { .. } => "MIR1701",
1106 IssueKind::ImpureGlobalVariable { .. } => "MIR1702",
1107 IssueKind::ImpureStaticVariable { .. } => "MIR1703",
1108 IssueKind::ImpureStaticPropertyAssignment { .. } => "MIR1706",
1109 IssueKind::ImpureByRefAssignment { .. } => "MIR1707",
1110 IssueKind::ImpureStaticPropertyAccess { .. } => "MIR1708",
1111 IssueKind::ImpureFunctionCall { .. } => "MIR1704",
1112 IssueKind::ImmutablePropertyModification { .. } => "MIR1705",
1113 IssueKind::UnsupportedReferenceUsage => "MIR1506",
1114 IssueKind::NoInterfaceProperties { .. } => "MIR1504",
1115 IssueKind::UndefinedDocblockClass { .. } => "MIR1505",
1116 IssueKind::MissingConstructor { .. } => "MIR1507",
1117 IssueKind::PropertyPossiblyUninitialized { .. } => "MIR1510",
1118 IssueKind::MixedFunctionCall => "MIR1211",
1119 IssueKind::MixedReturnStatement { .. } => "MIR1212",
1120
1121 IssueKind::ReadonlyPropertyAssignment { .. } => "MIR0600",
1123 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => "MIR0601",
1124
1125 IssueKind::UnimplementedAbstractMethod { .. } => "MIR0700",
1127 IssueKind::UnimplementedInterfaceMethod { .. } => "MIR0701",
1128 IssueKind::MethodSignatureMismatch { .. } => "MIR0702",
1129 IssueKind::OverriddenMethodAccess { .. } => "MIR0703",
1130 IssueKind::OverriddenPropertyAccess { .. } => "MIR0710",
1131 IssueKind::PropertyTypeRedeclarationMismatch { .. } => "MIR0712",
1132 IssueKind::BackedEnumCaseTypeMismatch { .. } => "MIR0713",
1133 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => "MIR0714",
1134 IssueKind::StaticPropertyRedeclarationMismatch { .. } => "MIR0715",
1135 IssueKind::ReadonlyClassExtendsMismatch { .. } => "MIR0716",
1136 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => "MIR0717",
1137 IssueKind::InvalidExtendClass { .. } => "MIR0704",
1138 IssueKind::FinalMethodOverridden { .. } => "MIR0705",
1139 IssueKind::AbstractInstantiation { .. } => "MIR0706",
1140 IssueKind::AbstractMethodCall { .. } => "MIR0711",
1141 IssueKind::InterfaceInstantiation { .. } => "MIR0709",
1142 IssueKind::CircularInheritance { .. } => "MIR0707",
1143 IssueKind::InvalidOverride { .. } => "MIR0708",
1144
1145 IssueKind::TaintedInput { .. } => "MIR0800",
1147 IssueKind::TaintedHtml => "MIR0801",
1148 IssueKind::TaintedSql => "MIR0802",
1149 IssueKind::TaintedShell => "MIR0803",
1150 IssueKind::TaintedLlmPrompt => "MIR0804",
1151
1152 IssueKind::InvalidTemplateParam { .. } => "MIR0900",
1154 IssueKind::ShadowedTemplateParam { .. } => "MIR0901",
1155 IssueKind::IfThisIsMismatch { .. } => "MIR0902",
1156
1157 IssueKind::DeprecatedCall { .. } => "MIR1000",
1159 IssueKind::WrongCaseFunction { .. } => "MIR1009",
1160 IssueKind::WrongCaseMethod { .. } => "MIR1010",
1161 IssueKind::WrongCaseClass { .. } => "MIR1011",
1162 IssueKind::DeprecatedProperty { .. } => "MIR1005",
1163 IssueKind::DeprecatedInterface { .. } => "MIR1006",
1164 IssueKind::DeprecatedTrait { .. } => "MIR1007",
1165 IssueKind::DeprecatedConstant { .. } => "MIR1008",
1166 IssueKind::DeprecatedMethodCall { .. } => "MIR1001",
1167 IssueKind::DeprecatedMethod { .. } => "MIR1002",
1168 IssueKind::DeprecatedClass { .. } => "MIR1003",
1169 IssueKind::InternalMethod { .. } => "MIR1004",
1170
1171 IssueKind::MissingReturnType { .. } => "MIR1100",
1173 IssueKind::MissingParamType { .. } => "MIR1101",
1174 IssueKind::MissingPropertyType { .. } => "MIR1104",
1175 IssueKind::MissingClosureReturnType => "MIR1105",
1176 IssueKind::MissingThrowsDocblock { .. } => "MIR1102",
1177 IssueKind::InvalidDocblock { .. } => "MIR1103",
1178
1179 IssueKind::MixedArgument { .. } => "MIR1200",
1181 IssueKind::MixedAssignment { .. } => "MIR1201",
1182 IssueKind::MixedMethodCall { .. } => "MIR1202",
1183 IssueKind::MixedPropertyFetch { .. } => "MIR1203",
1184 IssueKind::MixedPropertyAssignment { .. } => "MIR1208",
1185 IssueKind::MixedArrayAccess => "MIR1209",
1186 IssueKind::MixedArrayOffset => "MIR1210",
1187 IssueKind::MixedClone => "MIR1204",
1188 IssueKind::InvalidClone { .. } => "MIR1205",
1189 IssueKind::PossiblyInvalidClone { .. } => "MIR1206",
1190 IssueKind::InvalidToString { .. } => "MIR1207",
1191
1192 IssueKind::InvalidTraitUse { .. } => "MIR1300",
1194 IssueKind::ForbiddenCode { .. } => "MIR1301",
1195
1196 IssueKind::ParseError { .. } => "MIR1400",
1198
1199 IssueKind::InvalidAttribute { .. } => "MIR1600",
1201 IssueKind::UndefinedAttributeClass { .. } => "MIR1601",
1202 IssueKind::DuplicateClass { .. } => "MIR1602",
1203 IssueKind::DuplicateInterface { .. } => "MIR1603",
1204 IssueKind::DuplicateTrait { .. } => "MIR1604",
1205 IssueKind::DuplicateEnum { .. } => "MIR1605",
1206 IssueKind::DuplicateFunction { .. } => "MIR1606",
1207
1208 IssueKind::InvalidThrow { .. } => "MIR1500",
1210 IssueKind::InvalidCatch { .. } => "MIR1503",
1211 IssueKind::UnreachableCatch { .. } => "MIR1508",
1212 IssueKind::PluginIssue { .. } => "MIR1509",
1213 IssueKind::ImplicitToStringCast { .. } => "MIR1501",
1214 IssueKind::ImplicitFloatToIntCast { .. } => "MIR1502",
1215 }
1216 }
1217
1218 pub fn default_severity_for_code(code: &str) -> Option<Severity> {
1224 match code {
1225 "MIR0001" | "MIR0002" | "MIR0003" | "MIR0004" | "MIR0005" | "MIR0007" | "MIR0009"
1227 | "MIR0010" | "MIR0011" | "MIR0012" | "MIR0013" | "MIR0200" | "MIR0201" | "MIR0202"
1228 | "MIR0203" | "MIR0204" | "MIR0205" | "MIR0212" | "MIR0215" | "MIR0216" | "MIR0217"
1229 | "MIR0224" | "MIR0600" | "MIR0601" | "MIR0700" | "MIR0701" | "MIR0702" | "MIR0704"
1230 | "MIR0705" | "MIR0706" | "MIR0707" | "MIR0708" | "MIR0709" | "MIR0711" | "MIR0712"
1231 | "MIR0713" | "MIR0714" | "MIR0715" | "MIR0716" | "MIR0717" | "MIR0228" | "MIR0229"
1232 | "MIR0800" | "MIR0801" | "MIR0802" | "MIR0803" | "MIR0804" | "MIR0900" | "MIR1205"
1233 | "MIR1207" | "MIR1300" | "MIR1400" | "MIR1500" | "MIR1503" | "MIR1602" | "MIR1603"
1234 | "MIR1604" | "MIR1605" | "MIR1606" => Some(Severity::Error),
1235
1236 "MIR0006" | "MIR0008" | "MIR0100" | "MIR0101" | "MIR0102" | "MIR0103" | "MIR0109"
1238 | "MIR0206" | "MIR0208" | "MIR0211" | "MIR0218" | "MIR0219" | "MIR0220" | "MIR0222"
1239 | "MIR0300" | "MIR0301" | "MIR0302" | "MIR0303" | "MIR0404" | "MIR0405" | "MIR0408"
1240 | "MIR0500" | "MIR0506" | "MIR0703" | "MIR0710" | "MIR1301" | "MIR1501" | "MIR1502"
1241 | "MIR1700" | "MIR1701" | "MIR1702" | "MIR1703" | "MIR1704" | "MIR1705" | "MIR1706"
1242 | "MIR1707" | "MIR1708" | "MIR1506" | "MIR1510" => Some(Severity::Warning),
1243
1244 "MIR0104" | "MIR0105" | "MIR0106" | "MIR0107" | "MIR0108" | "MIR0207" | "MIR0209"
1246 | "MIR0210" | "MIR0213" | "MIR0214" | "MIR0221" | "MIR0223" | "MIR0400" | "MIR0401"
1247 | "MIR0402" | "MIR0403" | "MIR0501" | "MIR0502" | "MIR0503" | "MIR0504" | "MIR0505"
1248 | "MIR0507" | "MIR0508" | "MIR0901" | "MIR1000" | "MIR1001" | "MIR1002" | "MIR1003"
1249 | "MIR1004" | "MIR1005" | "MIR1006" | "MIR1007" | "MIR1008" | "MIR1009" | "MIR1010"
1250 | "MIR1011" | "MIR1100" | "MIR1101" | "MIR1102" | "MIR1103" | "MIR1104" | "MIR1105"
1251 | "MIR1200" | "MIR1201" | "MIR1202" | "MIR1203" | "MIR1204" | "MIR1206" | "MIR1208"
1252 | "MIR1209" | "MIR1210" | "MIR1211" | "MIR1212" | "MIR1504" | "MIR1505" | "MIR1507"
1253 | "MIR1508" | "MIR1600" | "MIR1601" | "MIR0225" | "MIR0226" | "MIR0227" | "MIR0406"
1254 | "MIR0407" | "MIR0902" => Some(Severity::Info),
1255
1256 _ => None,
1257 }
1258 }
1259
1260 pub fn name(&self) -> &'static str {
1262 match self {
1263 IssueKind::NonStaticSelfCall { .. } => "NonStaticSelfCall",
1264 IssueKind::DirectConstructorCall { .. } => "DirectConstructorCall",
1265 IssueKind::InvalidScope { .. } => "InvalidScope",
1266 IssueKind::UndefinedVariable { .. } => "UndefinedVariable",
1267 IssueKind::UndefinedFunction { .. } => "UndefinedFunction",
1268 IssueKind::UndefinedMethod { .. } => "UndefinedMethod",
1269 IssueKind::UndefinedClass { .. } => "UndefinedClass",
1270 IssueKind::UndefinedProperty { .. } => "UndefinedProperty",
1271 IssueKind::UndefinedConstant { .. } => "UndefinedConstant",
1272 IssueKind::InaccessibleClassConstant { .. } => "InaccessibleClassConstant",
1273 IssueKind::TraitConstantAccessedDirectly { .. } => "TraitConstantAccessedDirectly",
1274 IssueKind::UndefinedTraitAliasMethod { .. } => "UndefinedTraitAliasMethod",
1275 IssueKind::PossiblyUndefinedVariable { .. } => "PossiblyUndefinedVariable",
1276 IssueKind::UndefinedTrait { .. } => "UndefinedTrait",
1277 IssueKind::ParentNotFound => "ParentNotFound",
1278 IssueKind::InvalidStringClass { .. } => "InvalidStringClass",
1279 IssueKind::NotAnInterface { .. } => "NotAnInterface",
1280 IssueKind::NullArgument { .. } => "NullArgument",
1281 IssueKind::NullPropertyFetch { .. } => "NullPropertyFetch",
1282 IssueKind::NullMethodCall { .. } => "NullMethodCall",
1283 IssueKind::NullArrayAccess => "NullArrayAccess",
1284 IssueKind::PossiblyNullArgument { .. } => "PossiblyNullArgument",
1285 IssueKind::PossiblyInvalidArgument { .. } => "PossiblyInvalidArgument",
1286 IssueKind::PossiblyNullPropertyFetch { .. } => "PossiblyNullPropertyFetch",
1287 IssueKind::PossiblyNullMethodCall { .. } => "PossiblyNullMethodCall",
1288 IssueKind::PossiblyNullArrayAccess => "PossiblyNullArrayAccess",
1289 IssueKind::NullableReturnStatement { .. } => "NullableReturnStatement",
1290 IssueKind::InvalidReturnType { .. } => "InvalidReturnType",
1291 IssueKind::InvalidArgument { .. } => "InvalidArgument",
1292 IssueKind::TooFewArguments { .. } => "TooFewArguments",
1293 IssueKind::TooManyArguments { .. } => "TooManyArguments",
1294 IssueKind::InvalidNamedArgument { .. } => "InvalidNamedArgument",
1295 IssueKind::InvalidNamedArguments { .. } => "InvalidNamedArguments",
1296 IssueKind::InvalidPassByReference { .. } => "InvalidPassByReference",
1297 IssueKind::InvalidPropertyFetch { .. } => "InvalidPropertyFetch",
1298 IssueKind::InvalidArrayAccess { .. } => "InvalidArrayAccess",
1299 IssueKind::PossiblyInvalidArrayAccess { .. } => "PossiblyInvalidArrayAccess",
1300 IssueKind::InvalidArrayAssignment { .. } => "InvalidArrayAssignment",
1301 IssueKind::InvalidPropertyAssignment { .. } => "InvalidPropertyAssignment",
1302 IssueKind::InvalidCast { .. } => "InvalidCast",
1303 IssueKind::InvalidStaticInvocation { .. } => "InvalidStaticInvocation",
1304 IssueKind::InvalidOperand { .. } => "InvalidOperand",
1305 IssueKind::PossiblyInvalidOperand { .. } => "PossiblyInvalidOperand",
1306 IssueKind::PossiblyNullOperand { .. } => "PossiblyNullOperand",
1307 IssueKind::DivisionByZero { .. } => "DivisionByZero",
1308 IssueKind::RawObjectIteration { .. } => "RawObjectIteration",
1309 IssueKind::PossiblyRawObjectIteration { .. } => "PossiblyRawObjectIteration",
1310 IssueKind::MismatchingDocblockReturnType { .. } => "MismatchingDocblockReturnType",
1311 IssueKind::MismatchingDocblockParamType { .. } => "MismatchingDocblockParamType",
1312 IssueKind::TypeCheckMismatch { .. } => "TypeCheckMismatch",
1313 IssueKind::DocblockTypeContradiction { .. } => "DocblockTypeContradiction",
1314 IssueKind::ImpossibleIdenticalComparison { .. } => "ImpossibleIdenticalComparison",
1315 IssueKind::ImpossibleLooseComparison { .. } => "ImpossibleLooseComparison",
1316 IssueKind::UnevaluatedCode { .. } => "UnevaluatedCode",
1317 IssueKind::IfThisIsMismatch { .. } => "IfThisIsMismatch",
1318 IssueKind::Trace { .. } => "Trace",
1319 IssueKind::InvalidArrayOffset { .. } => "InvalidArrayOffset",
1320 IssueKind::NonExistentArrayOffset { .. } => "NonExistentArrayOffset",
1321 IssueKind::PossiblyInvalidArrayOffset { .. } => "PossiblyInvalidArrayOffset",
1322 IssueKind::DuplicateArrayKey { .. } => "DuplicateArrayKey",
1323 IssueKind::RedundantCondition { .. } => "RedundantCondition",
1324 IssueKind::RedundantCast { .. } => "RedundantCast",
1325 IssueKind::UnnecessaryVarAnnotation { .. } => "UnnecessaryVarAnnotation",
1326 IssueKind::TypeDoesNotContainType { .. } => "TypeDoesNotContainType",
1327 IssueKind::ParadoxicalCondition { .. } => "ParadoxicalCondition",
1328 IssueKind::UnhandledMatchCondition { .. } => "UnhandledMatchCondition",
1329 IssueKind::UnusedVariable { .. } => "UnusedVariable",
1330 IssueKind::UnusedParam { .. } => "UnusedParam",
1331 IssueKind::UnreachableCode => "UnreachableCode",
1332 IssueKind::UnusedMethod { .. } => "UnusedMethod",
1333 IssueKind::UnusedProperty { .. } => "UnusedProperty",
1334 IssueKind::UnusedFunction { .. } => "UnusedFunction",
1335 IssueKind::UnusedForeachValue { .. } => "UnusedForeachValue",
1336 IssueKind::UnusedClass { .. } => "UnusedClass",
1337 IssueKind::UnusedSuppress { .. } => "UnusedSuppress",
1338 IssueKind::ArgumentTypeCoercion { .. } => "ArgumentTypeCoercion",
1339 IssueKind::PropertyTypeCoercion { .. } => "PropertyTypeCoercion",
1340 IssueKind::ImpurePropertyAssignment { .. } => "ImpurePropertyAssignment",
1341 IssueKind::ImpureMethodCall { .. } => "ImpureMethodCall",
1342 IssueKind::ImpureGlobalVariable { .. } => "ImpureGlobalVariable",
1343 IssueKind::ImpureStaticVariable { .. } => "ImpureStaticVariable",
1344 IssueKind::ImpureByRefAssignment { .. } => "ImpureByRefAssignment",
1345 IssueKind::ImpureStaticPropertyAssignment { .. } => "ImpureStaticPropertyAssignment",
1346 IssueKind::ImpureStaticPropertyAccess { .. } => "ImpureStaticPropertyAccess",
1347 IssueKind::ImpureFunctionCall { .. } => "ImpureFunctionCall",
1348 IssueKind::ImmutablePropertyModification { .. } => "ImmutablePropertyModification",
1349 IssueKind::UnsupportedReferenceUsage => "UnsupportedReferenceUsage",
1350 IssueKind::NoInterfaceProperties { .. } => "NoInterfaceProperties",
1351 IssueKind::UndefinedDocblockClass { .. } => "UndefinedDocblockClass",
1352 IssueKind::MissingConstructor { .. } => "MissingConstructor",
1353 IssueKind::PropertyPossiblyUninitialized { .. } => "PropertyPossiblyUninitialized",
1354 IssueKind::MixedFunctionCall => "MixedFunctionCall",
1355 IssueKind::MixedReturnStatement { .. } => "MixedReturnStatement",
1356 IssueKind::UnimplementedAbstractMethod { .. } => "UnimplementedAbstractMethod",
1357 IssueKind::UnimplementedInterfaceMethod { .. } => "UnimplementedInterfaceMethod",
1358 IssueKind::MethodSignatureMismatch { .. } => "MethodSignatureMismatch",
1359 IssueKind::OverriddenMethodAccess { .. } => "OverriddenMethodAccess",
1360 IssueKind::OverriddenPropertyAccess { .. } => "OverriddenPropertyAccess",
1361 IssueKind::PropertyTypeRedeclarationMismatch { .. } => {
1362 "PropertyTypeRedeclarationMismatch"
1363 }
1364 IssueKind::ReadonlyPropertyRedeclarationMismatch { .. } => {
1365 "ReadonlyPropertyRedeclarationMismatch"
1366 }
1367 IssueKind::StaticPropertyRedeclarationMismatch { .. } => {
1368 "StaticPropertyRedeclarationMismatch"
1369 }
1370 IssueKind::ReadonlyClassExtendsMismatch { .. } => "ReadonlyClassExtendsMismatch",
1371 IssueKind::InvalidReadonlyPropertyDeclaration { .. } => {
1372 "InvalidReadonlyPropertyDeclaration"
1373 }
1374 IssueKind::BackedEnumCaseTypeMismatch { .. } => "BackedEnumCaseTypeMismatch",
1375 IssueKind::InvalidExtendClass { .. } => "InvalidExtendClass",
1376 IssueKind::FinalMethodOverridden { .. } => "FinalMethodOverridden",
1377 IssueKind::AbstractInstantiation { .. } => "AbstractInstantiation",
1378 IssueKind::AbstractMethodCall { .. } => "AbstractMethodCall",
1379 IssueKind::InterfaceInstantiation { .. } => "InterfaceInstantiation",
1380 IssueKind::InvalidOverride { .. } => "InvalidOverride",
1381 IssueKind::ReadonlyPropertyAssignment { .. } => "ReadonlyPropertyAssignment",
1382 IssueKind::ReadonlyPropertyAlreadyInitialized { .. } => {
1383 "ReadonlyPropertyAlreadyInitialized"
1384 }
1385 IssueKind::InvalidTemplateParam { .. } => "InvalidTemplateParam",
1386 IssueKind::ShadowedTemplateParam { .. } => "ShadowedTemplateParam",
1387 IssueKind::TaintedInput { .. } => "TaintedInput",
1388 IssueKind::TaintedHtml => "TaintedHtml",
1389 IssueKind::TaintedSql => "TaintedSql",
1390 IssueKind::TaintedShell => "TaintedShell",
1391 IssueKind::TaintedLlmPrompt => "TaintedLlmPrompt",
1392 IssueKind::DeprecatedCall { .. } => "DeprecatedCall",
1393 IssueKind::DeprecatedProperty { .. } => "DeprecatedProperty",
1394 IssueKind::DeprecatedConstant { .. } => "DeprecatedConstant",
1395 IssueKind::DeprecatedInterface { .. } => "DeprecatedInterface",
1396 IssueKind::DeprecatedTrait { .. } => "DeprecatedTrait",
1397 IssueKind::DeprecatedMethodCall { .. } => "DeprecatedMethodCall",
1398 IssueKind::DeprecatedMethod { .. } => "DeprecatedMethod",
1399 IssueKind::DeprecatedClass { .. } => "DeprecatedClass",
1400 IssueKind::InternalMethod { .. } => "InternalMethod",
1401 IssueKind::MissingReturnType { .. } => "MissingReturnType",
1402 IssueKind::MissingClosureReturnType => "MissingClosureReturnType",
1403 IssueKind::MissingParamType { .. } => "MissingParamType",
1404 IssueKind::MissingPropertyType { .. } => "MissingPropertyType",
1405 IssueKind::InvalidThrow { .. } => "InvalidThrow",
1406 IssueKind::InvalidCatch { .. } => "InvalidCatch",
1407 IssueKind::UnreachableCatch { .. } => "UnreachableCatch",
1408 IssueKind::MissingThrowsDocblock { .. } => "MissingThrowsDocblock",
1409 IssueKind::ImplicitToStringCast { .. } => "ImplicitToStringCast",
1410 IssueKind::ImplicitFloatToIntCast { .. } => "ImplicitFloatToIntCast",
1411 IssueKind::ParseError { .. } => "ParseError",
1412 IssueKind::InvalidDocblock { .. } => "InvalidDocblock",
1413 IssueKind::MixedArgument { .. } => "MixedArgument",
1414 IssueKind::MixedAssignment { .. } => "MixedAssignment",
1415 IssueKind::MixedMethodCall { .. } => "MixedMethodCall",
1416 IssueKind::MixedPropertyFetch { .. } => "MixedPropertyFetch",
1417 IssueKind::MixedPropertyAssignment { .. } => "MixedPropertyAssignment",
1418 IssueKind::MixedArrayAccess => "MixedArrayAccess",
1419 IssueKind::MixedArrayOffset => "MixedArrayOffset",
1420 IssueKind::MixedClone => "MixedClone",
1421 IssueKind::InvalidClone { .. } => "InvalidClone",
1422 IssueKind::PossiblyInvalidClone { .. } => "PossiblyInvalidClone",
1423 IssueKind::InvalidToString { .. } => "InvalidToString",
1424 IssueKind::CircularInheritance { .. } => "CircularInheritance",
1425 IssueKind::InvalidTraitUse { .. } => "InvalidTraitUse",
1426 IssueKind::ForbiddenCode { .. } => "ForbiddenCode",
1427 IssueKind::WrongCaseFunction { .. } => "WrongCaseFunction",
1428 IssueKind::WrongCaseMethod { .. } => "WrongCaseMethod",
1429 IssueKind::WrongCaseClass { .. } => "WrongCaseClass",
1430 IssueKind::InvalidAttribute { .. } => "InvalidAttribute",
1431 IssueKind::UndefinedAttributeClass { .. } => "UndefinedAttributeClass",
1432 IssueKind::DuplicateClass { .. } => "DuplicateClass",
1433 IssueKind::DuplicateInterface { .. } => "DuplicateInterface",
1434 IssueKind::DuplicateTrait { .. } => "DuplicateTrait",
1435 IssueKind::DuplicateEnum { .. } => "DuplicateEnum",
1436 IssueKind::DuplicateFunction { .. } => "DuplicateFunction",
1437 IssueKind::PluginIssue { .. } => "PluginIssue",
1438 }
1439 }
1440
1441 pub fn display_name(&self) -> &str {
1445 match self {
1446 IssueKind::PluginIssue { name, .. } => name,
1447 other => other.name(),
1448 }
1449 }
1450
1451 pub fn message(&self) -> String {
1453 match self {
1454 IssueKind::NonStaticSelfCall { class, method } => {
1455 format!("Non-static method {class}::{method}() cannot be called statically")
1456 }
1457 IssueKind::DirectConstructorCall { class } => {
1458 format!("Cannot call constructor of {class} directly")
1459 }
1460 IssueKind::InvalidScope { in_class } => {
1461 if *in_class {
1462 "$this cannot be used in a static method".to_string()
1463 } else {
1464 "$this cannot be used outside of a class".to_string()
1465 }
1466 }
1467 IssueKind::UndefinedVariable { name } => format!("Variable ${name} is not defined"),
1468 IssueKind::UndefinedFunction { name } => format!("Function {name}() is not defined"),
1469 IssueKind::UndefinedMethod { class, method } => {
1470 format!("Method {class}::{method}() does not exist")
1471 }
1472 IssueKind::UndefinedClass { name } => format!("Class {name} does not exist"),
1473 IssueKind::UndefinedProperty { class, property } => {
1474 format!("Property {class}::${property} does not exist")
1475 }
1476 IssueKind::UndefinedConstant { name } => format!("Constant {name} is not defined"),
1477 IssueKind::InaccessibleClassConstant { class, constant } => {
1478 format!("Cannot access constant {class}::{constant}")
1479 }
1480 IssueKind::PossiblyUndefinedVariable { name } => {
1481 format!("Variable ${name} might not be defined")
1482 }
1483 IssueKind::UndefinedTrait { name } => format!("Trait {name} does not exist"),
1484 IssueKind::TraitConstantAccessedDirectly {
1485 trait_name,
1486 constant,
1487 } => {
1488 format!("Cannot access trait constant {trait_name}::{constant} directly")
1489 }
1490 IssueKind::UndefinedTraitAliasMethod { trait_name, method } => match trait_name {
1491 Some(t) => {
1492 format!("An alias was defined for {t}::{method} but this method does not exist")
1493 }
1494 None => format!("An alias was defined for {method} but this method does not exist"),
1495 },
1496 IssueKind::ParentNotFound => {
1497 "Cannot use parent:: when current class has no parent".to_string()
1498 }
1499 IssueKind::InvalidStringClass { actual } => {
1500 format!("Dynamic class instantiation requires string or class-string type, got '{actual}'")
1501 }
1502 IssueKind::NotAnInterface { name } => {
1503 format!("{name} is not an interface")
1504 }
1505
1506 IssueKind::NullArgument { param, fn_name } => {
1507 format!("Argument ${param} of {fn_name}() cannot be null")
1508 }
1509 IssueKind::NullPropertyFetch { property } => {
1510 format!("Cannot access property ${property} on null")
1511 }
1512 IssueKind::NullMethodCall { method } => {
1513 format!("Cannot call method {method}() on null")
1514 }
1515 IssueKind::NullArrayAccess => "Cannot access array on null".to_string(),
1516 IssueKind::PossiblyNullArgument { param, fn_name } => {
1517 format!("Argument ${param} of {fn_name}() might be null")
1518 }
1519 IssueKind::PossiblyInvalidArgument {
1520 param,
1521 fn_name,
1522 expected,
1523 actual,
1524 } => {
1525 format!("Argument ${param} of {fn_name}() expects '{expected}', possibly different type '{actual}' provided")
1526 }
1527 IssueKind::PossiblyNullPropertyFetch { property } => {
1528 format!("Cannot access property ${property} on possibly null value")
1529 }
1530 IssueKind::PossiblyNullMethodCall { method } => {
1531 format!("Cannot call method {method}() on possibly null value")
1532 }
1533 IssueKind::PossiblyNullArrayAccess => {
1534 "Cannot access array on possibly null value".to_string()
1535 }
1536 IssueKind::NullableReturnStatement { expected, actual } => {
1537 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1538 }
1539
1540 IssueKind::InvalidReturnType { expected, actual } => {
1541 format!("Return type '{actual}' is not compatible with declared '{expected}'")
1542 }
1543 IssueKind::InvalidArgument {
1544 param,
1545 fn_name,
1546 expected,
1547 actual,
1548 } => {
1549 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}'")
1550 }
1551 IssueKind::TooFewArguments {
1552 fn_name,
1553 expected,
1554 actual,
1555 } => {
1556 format!(
1557 "Too few arguments for {}(): expected {}, got {}",
1558 fn_name, expected, actual
1559 )
1560 }
1561 IssueKind::TooManyArguments {
1562 fn_name,
1563 expected,
1564 actual,
1565 } => {
1566 format!(
1567 "Too many arguments for {}(): expected {}, got {}",
1568 fn_name, expected, actual
1569 )
1570 }
1571 IssueKind::InvalidNamedArgument { fn_name, name } => {
1572 format!("{}() has no parameter named ${}", fn_name, name)
1573 }
1574 IssueKind::InvalidNamedArguments { fn_name } => {
1575 format!("{}() does not accept named arguments", fn_name)
1576 }
1577 IssueKind::InvalidPassByReference { fn_name, param } => {
1578 format!(
1579 "Argument ${} of {}() must be passed by reference",
1580 param, fn_name
1581 )
1582 }
1583 IssueKind::InvalidPropertyFetch { ty } => {
1584 format!("Cannot fetch property on non-object type '{ty}'")
1585 }
1586 IssueKind::InvalidArrayAccess { ty } => {
1587 format!("Cannot use [] operator on non-array type '{ty}'")
1588 }
1589 IssueKind::PossiblyInvalidArrayAccess { ty } => {
1590 format!("Possibly invalid array access: '{ty}' might not support []")
1591 }
1592 IssueKind::InvalidArrayAssignment { ty } => {
1593 format!("Cannot use [] assignment on non-array type '{ty}'")
1594 }
1595 IssueKind::InvalidPropertyAssignment {
1596 property,
1597 expected,
1598 actual,
1599 } => {
1600 format!("Property ${property} expects '{expected}', cannot assign '{actual}'")
1601 }
1602 IssueKind::InvalidCast { from, to } => {
1603 format!("Cannot cast '{from}' to '{to}'")
1604 }
1605 IssueKind::InvalidStaticInvocation { class, method } => {
1606 format!("Non-static method {class}::{method}() cannot be called statically")
1607 }
1608 IssueKind::InvalidOperand { op, left, right } => {
1609 if right.is_empty() {
1610 format!("Operator '{op}' not supported for operand of type '{left}'")
1611 } else {
1612 format!("Operator '{op}' not supported between '{left}' and '{right}'")
1613 }
1614 }
1615 IssueKind::PossiblyInvalidOperand { op, left, right } => {
1616 format!("Operator '{op}' might not be supported between '{left}' and '{right}'")
1617 }
1618 IssueKind::PossiblyNullOperand { op, ty } => {
1619 format!("Operator '{op}' operand '{ty}' might be null")
1620 }
1621 IssueKind::DivisionByZero { op } => {
1622 format!("Division by zero: right operand of '{op}' is always 0")
1623 }
1624 IssueKind::RawObjectIteration { ty } => {
1625 format!("Cannot iterate over non-iterable object '{ty}'")
1626 }
1627 IssueKind::PossiblyRawObjectIteration { ty } => {
1628 format!("Cannot iterate over possibly non-iterable object '{ty}'")
1629 }
1630 IssueKind::MismatchingDocblockReturnType { declared, inferred } => {
1631 format!("Docblock return type '{declared}' does not match inferred '{inferred}'")
1632 }
1633 IssueKind::MismatchingDocblockParamType {
1634 param,
1635 declared,
1636 inferred,
1637 } => {
1638 format!(
1639 "Docblock type '{declared}' for ${param} does not match inferred '{inferred}'"
1640 )
1641 }
1642 IssueKind::TypeCheckMismatch {
1643 var,
1644 expected,
1645 actual,
1646 } => {
1647 format!("Type of {var} is expected to be {expected}, got {actual}")
1648 }
1649 IssueKind::Trace {
1650 variable,
1651 type_info,
1652 } => {
1653 format!("Type of ${variable} is {type_info}")
1654 }
1655
1656 IssueKind::InvalidArrayOffset { expected, actual } => {
1657 format!("Array offset expects '{expected}', got '{actual}'")
1658 }
1659 IssueKind::NonExistentArrayOffset { key } => {
1660 format!("Array offset '{key}' does not exist")
1661 }
1662 IssueKind::PossiblyInvalidArrayOffset { expected, actual } => {
1663 format!("Array offset might be invalid: expects '{expected}', got '{actual}'")
1664 }
1665 IssueKind::DuplicateArrayKey { key } => {
1666 format!("Array key {key} is duplicated — the earlier entry is silently overwritten")
1667 }
1668
1669 IssueKind::RedundantCondition { ty } => {
1670 format!("Condition is always true/false for type '{ty}'")
1671 }
1672 IssueKind::RedundantCast { from, to } => {
1673 format!("Casting '{from}' to '{to}' is redundant")
1674 }
1675 IssueKind::UnnecessaryVarAnnotation { var } => {
1676 format!("@var annotation for ${var} is unnecessary")
1677 }
1678 IssueKind::TypeDoesNotContainType { left, right } => {
1679 format!("Type '{left}' can never contain type '{right}'")
1680 }
1681 IssueKind::ParadoxicalCondition { value } => {
1682 format!("Value {value} is duplicated; this branch can never be reached")
1683 }
1684 IssueKind::UnhandledMatchCondition { detail } => {
1685 format!("Unhandled match condition: {detail}")
1686 }
1687 IssueKind::DocblockTypeContradiction { expr, declared } => {
1688 format!("Type '{declared}' makes '{expr}' impossible — this can never hold")
1689 }
1690 IssueKind::ImpossibleIdenticalComparison { op, left, right } => {
1691 let result = if op == "===" { "false" } else { "true" };
1692 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be identical")
1693 }
1694 IssueKind::ImpossibleLooseComparison { op, left, right } => {
1695 let result = if op == "==" { "false" } else { "true" };
1696 format!("'{op}' between '{left}' and '{right}' is always {result} — these types can never be loosely equal")
1697 }
1698 IssueKind::UnevaluatedCode { reason } => {
1699 format!("Unevaluated code: {reason}")
1700 }
1701 IssueKind::IfThisIsMismatch {
1702 class,
1703 method,
1704 expected,
1705 actual,
1706 } => {
1707 format!(
1708 "Cannot call {class}::{method}() — @if-this-is requires $this to be '{expected}', but it is '{actual}'"
1709 )
1710 }
1711
1712 IssueKind::UnusedVariable { name } => format!("Variable ${name} is never read"),
1713 IssueKind::UnusedParam { name } => format!("Parameter ${name} is never used"),
1714 IssueKind::UnreachableCode => "Unreachable code detected".to_string(),
1715 IssueKind::UnusedMethod { class, method } => {
1716 format!("Private method {class}::{method}() is never called")
1717 }
1718 IssueKind::UnusedProperty { class, property } => {
1719 format!("Private property {class}::${property} is never read")
1720 }
1721 IssueKind::UnusedFunction { name } => {
1722 format!("Function {name}() is never called")
1723 }
1724 IssueKind::UnusedForeachValue { name } => {
1725 format!("Foreach value ${name} is never read")
1726 }
1727 IssueKind::UnusedClass { class } => {
1728 format!("Class {class} is never referenced")
1729 }
1730 IssueKind::UnusedSuppress { kind } => {
1731 format!("Suppress annotation for '{kind}' is never used")
1732 }
1733 IssueKind::ArgumentTypeCoercion {
1734 param,
1735 fn_name,
1736 expected,
1737 actual,
1738 } => {
1739 format!("Argument ${param} of {fn_name}() expects '{expected}', got '{actual}' — coercion may fail at runtime")
1740 }
1741 IssueKind::PropertyTypeCoercion {
1742 property,
1743 expected,
1744 actual,
1745 } => {
1746 format!("Property ${property} expects '{expected}', cannot assign '{actual}' — coercion may fail at runtime")
1747 }
1748 IssueKind::ImpurePropertyAssignment { property } => {
1749 format!("Assigning to property {property} of a parameter in a pure or external-mutation-free context")
1750 }
1751 IssueKind::ImpureMethodCall { method } => {
1752 format!("Calling impure method {method}() in a pure or immutable context")
1753 }
1754 IssueKind::ImpureGlobalVariable { variable } => {
1755 format!("Using global variable ${variable} in a @pure function")
1756 }
1757 IssueKind::ImpureStaticVariable { variable } => {
1758 format!("Using static variable ${variable} in a @pure function")
1759 }
1760 IssueKind::ImpureByRefAssignment { variable } => {
1761 format!("Assigning to by-reference parameter ${variable} in a @pure function")
1762 }
1763 IssueKind::ImpureStaticPropertyAssignment { class, property } => {
1764 format!("Assigning to static property {class}::${property} in a @pure function")
1765 }
1766 IssueKind::ImpureStaticPropertyAccess { class, property } => {
1767 format!("Reading static property {class}::${property} in a @pure function")
1768 }
1769 IssueKind::ImpureFunctionCall { fn_name } => {
1770 format!("Calling impure function {fn_name}() in a @pure function")
1771 }
1772 IssueKind::ImmutablePropertyModification { receiver, property } => {
1773 format!("Assigning to property {property} of {receiver} in an immutable context (@psalm-immutable class or @psalm-mutation-free method)")
1774 }
1775
1776 IssueKind::UnimplementedAbstractMethod { class, method } => {
1777 format!("Class {class} must implement abstract method {method}()")
1778 }
1779 IssueKind::UnimplementedInterfaceMethod {
1780 class,
1781 interface,
1782 method,
1783 } => {
1784 format!("Class {class} must implement {interface}::{method}() from interface")
1785 }
1786 IssueKind::MethodSignatureMismatch {
1787 class,
1788 method,
1789 detail,
1790 } => {
1791 format!("Method {class}::{method}() signature mismatch: {detail}")
1792 }
1793 IssueKind::OverriddenMethodAccess { class, method } => {
1794 format!("Method {class}::{method}() overrides with less visibility")
1795 }
1796 IssueKind::OverriddenPropertyAccess { class, property } => {
1797 format!("Property {class}::${property} overrides with less visibility")
1798 }
1799 IssueKind::PropertyTypeRedeclarationMismatch {
1800 class,
1801 property,
1802 expected,
1803 actual,
1804 } => {
1805 format!(
1806 "Type of {class}::${property} must be {expected} (as in parent class), {actual} given"
1807 )
1808 }
1809 IssueKind::ReadonlyPropertyRedeclarationMismatch {
1810 parent_class,
1811 class,
1812 property,
1813 parent_readonly,
1814 } => {
1815 if *parent_readonly {
1816 format!(
1817 "Cannot redeclare readonly property {parent_class}::${property} as non-readonly {class}::${property}"
1818 )
1819 } else {
1820 format!(
1821 "Cannot redeclare non-readonly property {parent_class}::${property} as readonly {class}::${property}"
1822 )
1823 }
1824 }
1825 IssueKind::StaticPropertyRedeclarationMismatch {
1826 parent_class,
1827 class,
1828 property,
1829 parent_static,
1830 } => {
1831 if *parent_static {
1832 format!(
1833 "Cannot redeclare static property {parent_class}::${property} as non-static {class}::${property}"
1834 )
1835 } else {
1836 format!(
1837 "Cannot redeclare non-static property {parent_class}::${property} as static {class}::${property}"
1838 )
1839 }
1840 }
1841 IssueKind::BackedEnumCaseTypeMismatch {
1842 enum_name,
1843 case_name,
1844 expected,
1845 actual,
1846 } => {
1847 format!(
1848 "Backed enum case {enum_name}::{case_name} has value of type {actual}, but backing type is {expected}"
1849 )
1850 }
1851 IssueKind::ReadonlyClassExtendsMismatch {
1852 parent,
1853 child,
1854 parent_readonly,
1855 } => {
1856 if *parent_readonly {
1857 format!("Non-readonly class {child} cannot extend readonly class {parent}")
1858 } else {
1859 format!("Readonly class {child} cannot extend non-readonly class {parent}")
1860 }
1861 }
1862 IssueKind::InvalidReadonlyPropertyDeclaration { class, property } => {
1863 format!("Readonly property {class}::${property} cannot have a default value")
1864 }
1865 IssueKind::ReadonlyPropertyAssignment { class, property } => {
1866 format!(
1867 "Cannot assign to readonly property {class}::${property} outside of constructor"
1868 )
1869 }
1870 IssueKind::ReadonlyPropertyAlreadyInitialized { class, property } => {
1871 format!(
1872 "Cannot modify readonly property {class}::${property} — already initialized"
1873 )
1874 }
1875 IssueKind::InvalidExtendClass { parent, child } => {
1876 format!("Class {child} cannot extend final class {parent}")
1877 }
1878 IssueKind::InvalidTemplateParam {
1879 name,
1880 expected_bound,
1881 actual,
1882 } => {
1883 format!(
1884 "Template type '{name}' inferred as '{actual}' does not satisfy bound '{expected_bound}'"
1885 )
1886 }
1887 IssueKind::ShadowedTemplateParam { name } => {
1888 format!(
1889 "Method template parameter '{name}' shadows class-level template parameter with the same name"
1890 )
1891 }
1892 IssueKind::FinalMethodOverridden {
1893 class,
1894 method,
1895 parent,
1896 } => {
1897 format!("Method {class}::{method}() cannot override final method from {parent}")
1898 }
1899 IssueKind::AbstractInstantiation { class } => {
1900 format!("Cannot instantiate abstract class {class}")
1901 }
1902 IssueKind::AbstractMethodCall { class, method } => {
1903 format!("Cannot call abstract method {class}::{method}()")
1904 }
1905 IssueKind::InterfaceInstantiation { class } => {
1906 format!("Cannot instantiate interface {class}")
1907 }
1908 IssueKind::InvalidOverride {
1909 class,
1910 method,
1911 detail,
1912 } => {
1913 format!("Method {class}::{method}() has #[Override] but {detail}")
1914 }
1915
1916 IssueKind::TaintedInput { sink } => format!("Tainted input reaching sink '{sink}'"),
1917 IssueKind::TaintedHtml => "Tainted HTML output — possible XSS".to_string(),
1918 IssueKind::TaintedSql => "Tainted SQL query — possible SQL injection".to_string(),
1919 IssueKind::TaintedShell => {
1920 "Tainted shell command — possible command injection".to_string()
1921 }
1922 IssueKind::TaintedLlmPrompt => {
1923 "Tainted LLM prompt — possible prompt injection".to_string()
1924 }
1925
1926 IssueKind::DeprecatedCall { name, message } => {
1927 let base = format!("Call to deprecated function {name}");
1928 append_deprecation_message(base, message)
1929 }
1930 IssueKind::DeprecatedProperty {
1931 class,
1932 property,
1933 message,
1934 } => {
1935 let base = format!("Property {class}::${property} is deprecated");
1936 append_deprecation_message(base, message)
1937 }
1938 IssueKind::DeprecatedConstant {
1939 class,
1940 constant,
1941 message,
1942 } => {
1943 let base = format!("Constant {class}::{constant} is deprecated");
1944 append_deprecation_message(base, message)
1945 }
1946 IssueKind::DeprecatedInterface { name, message } => {
1947 let base = format!("Interface {name} is deprecated");
1948 append_deprecation_message(base, message)
1949 }
1950 IssueKind::DeprecatedTrait { name, message } => {
1951 let base = format!("Trait {name} is deprecated");
1952 append_deprecation_message(base, message)
1953 }
1954 IssueKind::DeprecatedMethodCall {
1955 class,
1956 method,
1957 message,
1958 } => {
1959 let base = format!("Call to deprecated method {class}::{method}");
1960 append_deprecation_message(base, message)
1961 }
1962 IssueKind::DeprecatedMethod {
1963 class,
1964 method,
1965 message,
1966 } => {
1967 let base = format!("Method {class}::{method}() is deprecated");
1968 append_deprecation_message(base, message)
1969 }
1970 IssueKind::DeprecatedClass { name, message } => {
1971 let base = format!("Class {name} is deprecated");
1972 append_deprecation_message(base, message)
1973 }
1974 IssueKind::InternalMethod { class, method } => {
1975 format!("Method {class}::{method}() is marked @internal")
1976 }
1977 IssueKind::MissingReturnType { fn_name } => {
1978 format!("Function {fn_name}() has no return type annotation")
1979 }
1980 IssueKind::MissingClosureReturnType => {
1981 "Closure has no return type annotation".to_string()
1982 }
1983 IssueKind::MissingParamType { fn_name, param } => {
1984 format!("Parameter ${param} of {fn_name}() has no type annotation")
1985 }
1986 IssueKind::MissingPropertyType { class, property } => {
1987 format!("Property {class}::${property} has no type annotation")
1988 }
1989 IssueKind::InvalidThrow { ty } => {
1990 format!("Thrown type '{ty}' does not extend Throwable")
1991 }
1992 IssueKind::InvalidCatch { ty } => {
1993 format!("Caught type '{ty}' does not extend Throwable")
1994 }
1995 IssueKind::UnreachableCatch { ty, shadowed_by } => {
1996 format!("Catch block for '{ty}' is unreachable — already caught by '{shadowed_by}'")
1997 }
1998 IssueKind::MissingThrowsDocblock { class } => {
1999 format!("Exception {class} is thrown but not declared in @throws")
2000 }
2001 IssueKind::ImplicitToStringCast { class } => {
2002 format!("Class {class} is implicitly cast to string")
2003 }
2004 IssueKind::ImplicitFloatToIntCast { from } => {
2005 format!("Implicit cast from {from} to int truncates the fractional part")
2006 }
2007 IssueKind::ParseError { message } => format!("Parse error: {message}"),
2008 IssueKind::InvalidDocblock { message } => format!("Invalid docblock: {message}"),
2009 IssueKind::MixedArgument { param, fn_name } => {
2010 format!("Argument ${param} of {fn_name}() is mixed")
2011 }
2012 IssueKind::MixedAssignment { var } => {
2013 format!("Variable ${var} is assigned a mixed type")
2014 }
2015 IssueKind::MixedMethodCall { method } => {
2016 format!("Method {method}() called on mixed type")
2017 }
2018 IssueKind::UnsupportedReferenceUsage => {
2019 "Reference assignment is not supported".to_string()
2020 }
2021 IssueKind::NoInterfaceProperties { property } => {
2022 format!("Property ${property} is not defined on this interface")
2023 }
2024 IssueKind::UndefinedDocblockClass { name } => {
2025 format!("Docblock type '{name}' does not exist")
2026 }
2027 IssueKind::MissingConstructor { class } => {
2028 format!("Class {class} has uninitialized properties but no constructor")
2029 }
2030 IssueKind::PropertyPossiblyUninitialized { class, property } => {
2031 format!(
2032 "Property {class}::${property} may be left uninitialized by the constructor"
2033 )
2034 }
2035 IssueKind::MixedFunctionCall => "Cannot call mixed type as a function".to_string(),
2036 IssueKind::MixedReturnStatement { declared } => {
2037 format!("Cannot return a mixed type from function with declared return type '{declared}'")
2038 }
2039 IssueKind::MixedPropertyFetch { property } => {
2040 format!("Property ${property} fetched on mixed type")
2041 }
2042 IssueKind::MixedPropertyAssignment { property } => {
2043 format!("Property ${property} assigned on mixed type")
2044 }
2045 IssueKind::MixedArrayAccess => "Array access on mixed type".to_string(),
2046 IssueKind::MixedArrayOffset => "Mixed type used as array offset".to_string(),
2047 IssueKind::MixedClone => "cannot clone mixed".to_string(),
2048 IssueKind::InvalidClone { ty } => format!("cannot clone non-object {ty}"),
2049 IssueKind::PossiblyInvalidClone { ty } => {
2050 format!("cannot clone possibly non-object {ty}")
2051 }
2052 IssueKind::InvalidToString { class } => {
2053 format!("Method {class}::__toString() must return a string")
2054 }
2055 IssueKind::CircularInheritance { class } => {
2056 format!("Class {class} has a circular inheritance chain")
2057 }
2058 IssueKind::InvalidTraitUse { trait_name, reason } => {
2059 format!("Trait {trait_name} used incorrectly: {reason}")
2060 }
2061 IssueKind::WrongCaseFunction { used, canonical } => {
2062 format!("Function name '{used}' has incorrect casing; use '{canonical}'")
2063 }
2064 IssueKind::WrongCaseMethod {
2065 class,
2066 used,
2067 canonical,
2068 } => {
2069 format!("Method name '{class}::{used}' has incorrect casing; use '{canonical}'")
2070 }
2071 IssueKind::WrongCaseClass { used, canonical } => {
2072 format!("Class name '{used}' has incorrect casing; use '{canonical}'")
2073 }
2074 IssueKind::InvalidAttribute { message } => message.clone(),
2075 IssueKind::UndefinedAttributeClass { name } => {
2076 format!("Attribute class {name} does not exist")
2077 }
2078 IssueKind::ForbiddenCode { message } => message.clone(),
2079 IssueKind::DuplicateClass { name } => {
2080 format!("Class {name} has already been defined")
2081 }
2082 IssueKind::DuplicateInterface { name } => {
2083 format!("Interface {name} has already been defined")
2084 }
2085 IssueKind::DuplicateTrait { name } => {
2086 format!("Trait {name} has already been defined")
2087 }
2088 IssueKind::DuplicateEnum { name } => {
2089 format!("Enum {name} has already been defined")
2090 }
2091 IssueKind::DuplicateFunction { name } => {
2092 format!("Function {name}() has already been defined")
2093 }
2094 IssueKind::PluginIssue { message, .. } => message.clone(),
2095 }
2096 }
2097}
2098
2099#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2104pub struct Issue {
2105 pub kind: IssueKind,
2106 pub severity: Severity,
2107 pub location: Location,
2108 pub snippet: Option<String>,
2109 pub suppressed: bool,
2110}
2111
2112impl Issue {
2113 pub fn new(kind: IssueKind, location: Location) -> Self {
2114 let severity = kind.default_severity();
2115 Self {
2116 severity,
2117 kind,
2118 location,
2119 snippet: None,
2120 suppressed: false,
2121 }
2122 }
2123
2124 pub fn with_snippet(mut self, snippet: impl Into<String>) -> Self {
2125 self.snippet = Some(snippet.into());
2126 self
2127 }
2128
2129 pub fn suppress(mut self) -> Self {
2130 self.suppressed = true;
2131 self
2132 }
2133}
2134
2135#[derive(Debug, Default)]
2140pub struct IssueBuffer {
2141 issues: Vec<Issue>,
2142 seen: HashSet<(&'static str, Arc<str>, u32, u16)>,
2143 file_suppressions: Vec<String>,
2145}
2146
2147impl IssueBuffer {
2148 pub fn new() -> Self {
2149 Self::default()
2150 }
2151
2152 pub fn add(&mut self, issue: Issue) {
2153 let key = (
2154 issue.kind.name(),
2155 issue.location.file.clone(),
2156 issue.location.line,
2157 issue.location.col_start,
2158 );
2159 if self.seen.insert(key) {
2160 self.issues.push(issue);
2161 }
2162 }
2163
2164 pub fn add_suppression(&mut self, name: impl Into<String>) {
2165 self.file_suppressions.push(name.into());
2166 }
2167
2168 pub fn into_issues(self) -> Vec<Issue> {
2170 self.issues
2171 .into_iter()
2172 .filter(|i| !i.suppressed)
2173 .filter(|i| {
2174 !self
2175 .file_suppressions
2176 .iter()
2177 .any(|s| s == i.kind.display_name())
2178 })
2179 .collect()
2180 }
2181
2182 pub fn into_all_issues(self) -> Vec<Issue> {
2187 self.issues
2188 .into_iter()
2189 .map(|mut i| {
2190 if self
2191 .file_suppressions
2192 .iter()
2193 .any(|s| s == i.kind.display_name())
2194 {
2195 i.suppressed = true;
2196 }
2197 i
2198 })
2199 .collect()
2200 }
2201
2202 pub fn suppress_range(&mut self, from: usize, suppressions: &[String]) {
2205 if suppressions.is_empty() {
2206 return;
2207 }
2208 for issue in self.issues[from..].iter_mut() {
2209 if suppressions.iter().any(|s| s == issue.kind.display_name()) {
2210 issue.suppressed = true;
2211 }
2212 }
2213 }
2214
2215 pub fn issue_count(&self) -> usize {
2218 self.issues.len()
2219 }
2220
2221 pub fn truncate_to(&mut self, mark: usize) {
2227 for issue in self.issues.drain(mark..) {
2228 let key = (
2229 issue.kind.name(),
2230 issue.location.file.clone(),
2231 issue.location.line,
2232 issue.location.col_start,
2233 );
2234 self.seen.remove(&key);
2235 }
2236 }
2237
2238 pub fn is_empty(&self) -> bool {
2239 self.issues.is_empty()
2240 }
2241
2242 pub fn len(&self) -> usize {
2243 self.issues.len()
2244 }
2245
2246 pub fn error_count(&self) -> usize {
2247 self.issues
2248 .iter()
2249 .filter(|i| !i.suppressed && i.severity == Severity::Error)
2250 .count()
2251 }
2252
2253 pub fn warning_count(&self) -> usize {
2254 self.issues
2255 .iter()
2256 .filter(|i| !i.suppressed && i.severity == Severity::Warning)
2257 .count()
2258 }
2259}
2260
2261#[cfg(test)]
2262mod code_tests {
2263 use super::*;
2264 use std::collections::HashSet;
2265
2266 fn one_of_each() -> Vec<IssueKind> {
2272 let s = || String::new();
2273 vec![
2274 IssueKind::InvalidScope { in_class: false },
2275 IssueKind::NonStaticSelfCall {
2276 class: s(),
2277 method: s(),
2278 },
2279 IssueKind::DirectConstructorCall { class: s() },
2280 IssueKind::UndefinedVariable { name: s() },
2281 IssueKind::UndefinedFunction { name: s() },
2282 IssueKind::UndefinedMethod {
2283 class: s(),
2284 method: s(),
2285 },
2286 IssueKind::UndefinedClass { name: s() },
2287 IssueKind::NotAnInterface { name: s() },
2288 IssueKind::UndefinedProperty {
2289 class: s(),
2290 property: s(),
2291 },
2292 IssueKind::UndefinedConstant { name: s() },
2293 IssueKind::InaccessibleClassConstant {
2294 class: s(),
2295 constant: s(),
2296 },
2297 IssueKind::PossiblyUndefinedVariable { name: s() },
2298 IssueKind::UndefinedTrait { name: s() },
2299 IssueKind::TraitConstantAccessedDirectly {
2300 trait_name: s(),
2301 constant: s(),
2302 },
2303 IssueKind::UndefinedTraitAliasMethod {
2304 trait_name: Some(s()),
2305 method: s(),
2306 },
2307 IssueKind::ParentNotFound,
2308 IssueKind::NullArgument {
2309 param: s(),
2310 fn_name: s(),
2311 },
2312 IssueKind::NullPropertyFetch { property: s() },
2313 IssueKind::NullMethodCall { method: s() },
2314 IssueKind::NullArrayAccess,
2315 IssueKind::PossiblyNullArgument {
2316 param: s(),
2317 fn_name: s(),
2318 },
2319 IssueKind::PossiblyInvalidArgument {
2320 param: s(),
2321 fn_name: s(),
2322 expected: s(),
2323 actual: s(),
2324 },
2325 IssueKind::PossiblyNullPropertyFetch { property: s() },
2326 IssueKind::PossiblyNullMethodCall { method: s() },
2327 IssueKind::PossiblyNullArrayAccess,
2328 IssueKind::NullableReturnStatement {
2329 expected: s(),
2330 actual: s(),
2331 },
2332 IssueKind::InvalidReturnType {
2333 expected: s(),
2334 actual: s(),
2335 },
2336 IssueKind::InvalidArgument {
2337 param: s(),
2338 fn_name: s(),
2339 expected: s(),
2340 actual: s(),
2341 },
2342 IssueKind::TooFewArguments {
2343 fn_name: s(),
2344 expected: 0,
2345 actual: 0,
2346 },
2347 IssueKind::TooManyArguments {
2348 fn_name: s(),
2349 expected: 0,
2350 actual: 0,
2351 },
2352 IssueKind::InvalidNamedArgument {
2353 fn_name: s(),
2354 name: s(),
2355 },
2356 IssueKind::InvalidNamedArguments { fn_name: s() },
2357 IssueKind::InvalidPassByReference {
2358 fn_name: s(),
2359 param: s(),
2360 },
2361 IssueKind::InvalidPropertyFetch { ty: s() },
2362 IssueKind::InvalidArrayAccess { ty: s() },
2363 IssueKind::PossiblyInvalidArrayAccess { ty: s() },
2364 IssueKind::InvalidArrayAssignment { ty: s() },
2365 IssueKind::InvalidPropertyAssignment {
2366 property: s(),
2367 expected: s(),
2368 actual: s(),
2369 },
2370 IssueKind::InvalidCast { from: s(), to: s() },
2371 IssueKind::InvalidStaticInvocation {
2372 class: s(),
2373 method: s(),
2374 },
2375 IssueKind::InvalidOperand {
2376 op: s(),
2377 left: s(),
2378 right: s(),
2379 },
2380 IssueKind::PossiblyInvalidOperand {
2381 op: s(),
2382 left: s(),
2383 right: s(),
2384 },
2385 IssueKind::PossiblyNullOperand { op: s(), ty: s() },
2386 IssueKind::DivisionByZero { op: s() },
2387 IssueKind::RawObjectIteration { ty: s() },
2388 IssueKind::PossiblyRawObjectIteration { ty: s() },
2389 IssueKind::MismatchingDocblockReturnType {
2390 declared: s(),
2391 inferred: s(),
2392 },
2393 IssueKind::MismatchingDocblockParamType {
2394 param: s(),
2395 declared: s(),
2396 inferred: s(),
2397 },
2398 IssueKind::TypeCheckMismatch {
2399 var: s(),
2400 expected: s(),
2401 actual: s(),
2402 },
2403 IssueKind::Trace {
2404 variable: s(),
2405 type_info: s(),
2406 },
2407 IssueKind::InvalidArrayOffset {
2408 expected: s(),
2409 actual: s(),
2410 },
2411 IssueKind::NonExistentArrayOffset { key: s() },
2412 IssueKind::PossiblyInvalidArrayOffset {
2413 expected: s(),
2414 actual: s(),
2415 },
2416 IssueKind::DuplicateArrayKey { key: s() },
2417 IssueKind::RedundantCondition { ty: s() },
2418 IssueKind::RedundantCast { from: s(), to: s() },
2419 IssueKind::UnnecessaryVarAnnotation { var: s() },
2420 IssueKind::TypeDoesNotContainType {
2421 left: s(),
2422 right: s(),
2423 },
2424 IssueKind::UnusedVariable { name: s() },
2425 IssueKind::UnusedParam { name: s() },
2426 IssueKind::UnreachableCode,
2427 IssueKind::UnhandledMatchCondition { detail: s() },
2428 IssueKind::UnusedMethod {
2429 class: s(),
2430 method: s(),
2431 },
2432 IssueKind::UnusedProperty {
2433 class: s(),
2434 property: s(),
2435 },
2436 IssueKind::UnusedFunction { name: s() },
2437 IssueKind::UnusedForeachValue { name: s() },
2438 IssueKind::UnusedClass { class: s() },
2439 IssueKind::UnusedSuppress { kind: s() },
2440 IssueKind::ArgumentTypeCoercion {
2441 param: s(),
2442 fn_name: s(),
2443 expected: s(),
2444 actual: s(),
2445 },
2446 IssueKind::PropertyTypeCoercion {
2447 property: s(),
2448 expected: s(),
2449 actual: s(),
2450 },
2451 IssueKind::ImpurePropertyAssignment { property: s() },
2452 IssueKind::ImpureMethodCall { method: s() },
2453 IssueKind::ImpureGlobalVariable { variable: s() },
2454 IssueKind::ImpureStaticVariable { variable: s() },
2455 IssueKind::ImpureByRefAssignment { variable: s() },
2456 IssueKind::ImpureStaticPropertyAssignment {
2457 class: s(),
2458 property: s(),
2459 },
2460 IssueKind::ImpureStaticPropertyAccess {
2461 class: s(),
2462 property: s(),
2463 },
2464 IssueKind::ImpureFunctionCall { fn_name: s() },
2465 IssueKind::ImmutablePropertyModification {
2466 receiver: s(),
2467 property: s(),
2468 },
2469 IssueKind::ReadonlyPropertyAssignment {
2470 class: s(),
2471 property: s(),
2472 },
2473 IssueKind::ReadonlyPropertyAlreadyInitialized {
2474 class: s(),
2475 property: s(),
2476 },
2477 IssueKind::UnimplementedAbstractMethod {
2478 class: s(),
2479 method: s(),
2480 },
2481 IssueKind::UnimplementedInterfaceMethod {
2482 class: s(),
2483 interface: s(),
2484 method: s(),
2485 },
2486 IssueKind::MethodSignatureMismatch {
2487 class: s(),
2488 method: s(),
2489 detail: s(),
2490 },
2491 IssueKind::OverriddenMethodAccess {
2492 class: s(),
2493 method: s(),
2494 },
2495 IssueKind::OverriddenPropertyAccess {
2496 class: s(),
2497 property: s(),
2498 },
2499 IssueKind::PropertyTypeRedeclarationMismatch {
2500 class: s(),
2501 property: s(),
2502 expected: s(),
2503 actual: s(),
2504 },
2505 IssueKind::ReadonlyPropertyRedeclarationMismatch {
2506 parent_class: s(),
2507 class: s(),
2508 property: s(),
2509 parent_readonly: true,
2510 },
2511 IssueKind::StaticPropertyRedeclarationMismatch {
2512 parent_class: s(),
2513 class: s(),
2514 property: s(),
2515 parent_static: true,
2516 },
2517 IssueKind::ReadonlyClassExtendsMismatch {
2518 parent: s(),
2519 child: s(),
2520 parent_readonly: true,
2521 },
2522 IssueKind::InvalidReadonlyPropertyDeclaration {
2523 class: s(),
2524 property: s(),
2525 },
2526 IssueKind::BackedEnumCaseTypeMismatch {
2527 enum_name: s(),
2528 case_name: s(),
2529 expected: s(),
2530 actual: s(),
2531 },
2532 IssueKind::InvalidExtendClass {
2533 parent: s(),
2534 child: s(),
2535 },
2536 IssueKind::FinalMethodOverridden {
2537 class: s(),
2538 method: s(),
2539 parent: s(),
2540 },
2541 IssueKind::AbstractInstantiation { class: s() },
2542 IssueKind::AbstractMethodCall {
2543 class: s(),
2544 method: s(),
2545 },
2546 IssueKind::InterfaceInstantiation { class: s() },
2547 IssueKind::InvalidOverride {
2548 class: s(),
2549 method: s(),
2550 detail: s(),
2551 },
2552 IssueKind::CircularInheritance { class: s() },
2553 IssueKind::TaintedInput { sink: s() },
2554 IssueKind::TaintedHtml,
2555 IssueKind::TaintedSql,
2556 IssueKind::TaintedShell,
2557 IssueKind::TaintedLlmPrompt,
2558 IssueKind::InvalidTemplateParam {
2559 name: s(),
2560 expected_bound: s(),
2561 actual: s(),
2562 },
2563 IssueKind::ShadowedTemplateParam { name: s() },
2564 IssueKind::DeprecatedCall {
2565 name: s(),
2566 message: None,
2567 },
2568 IssueKind::DeprecatedProperty {
2569 class: s(),
2570 property: s(),
2571 message: None,
2572 },
2573 IssueKind::DeprecatedConstant {
2574 class: s(),
2575 constant: s(),
2576 message: None,
2577 },
2578 IssueKind::DeprecatedInterface {
2579 name: s(),
2580 message: None,
2581 },
2582 IssueKind::DeprecatedTrait {
2583 name: s(),
2584 message: None,
2585 },
2586 IssueKind::DeprecatedMethodCall {
2587 class: s(),
2588 method: s(),
2589 message: None,
2590 },
2591 IssueKind::DeprecatedMethod {
2592 class: s(),
2593 method: s(),
2594 message: None,
2595 },
2596 IssueKind::DeprecatedClass {
2597 name: s(),
2598 message: None,
2599 },
2600 IssueKind::InternalMethod {
2601 class: s(),
2602 method: s(),
2603 },
2604 IssueKind::MissingReturnType { fn_name: s() },
2605 IssueKind::MissingClosureReturnType,
2606 IssueKind::MissingParamType {
2607 fn_name: s(),
2608 param: s(),
2609 },
2610 IssueKind::MissingPropertyType {
2611 class: s(),
2612 property: s(),
2613 },
2614 IssueKind::MissingThrowsDocblock { class: s() },
2615 IssueKind::InvalidDocblock { message: s() },
2616 IssueKind::MixedArgument {
2617 param: s(),
2618 fn_name: s(),
2619 },
2620 IssueKind::MixedAssignment { var: s() },
2621 IssueKind::MixedMethodCall { method: s() },
2622 IssueKind::UnsupportedReferenceUsage,
2623 IssueKind::NoInterfaceProperties { property: s() },
2624 IssueKind::UndefinedDocblockClass { name: s() },
2625 IssueKind::MissingConstructor { class: s() },
2626 IssueKind::PropertyPossiblyUninitialized {
2627 class: s(),
2628 property: s(),
2629 },
2630 IssueKind::MixedFunctionCall,
2631 IssueKind::MixedReturnStatement { declared: s() },
2632 IssueKind::MixedPropertyFetch { property: s() },
2633 IssueKind::MixedPropertyAssignment { property: s() },
2634 IssueKind::MixedArrayAccess,
2635 IssueKind::MixedArrayOffset,
2636 IssueKind::MixedClone,
2637 IssueKind::InvalidClone { ty: s() },
2638 IssueKind::PossiblyInvalidClone { ty: s() },
2639 IssueKind::InvalidToString { class: s() },
2640 IssueKind::InvalidTraitUse {
2641 trait_name: s(),
2642 reason: s(),
2643 },
2644 IssueKind::ParseError { message: s() },
2645 IssueKind::InvalidThrow { ty: s() },
2646 IssueKind::InvalidCatch { ty: s() },
2647 IssueKind::UnreachableCatch {
2648 ty: s(),
2649 shadowed_by: s(),
2650 },
2651 IssueKind::ImplicitToStringCast { class: s() },
2652 IssueKind::ImplicitFloatToIntCast { from: s() },
2653 IssueKind::WrongCaseFunction {
2654 used: s(),
2655 canonical: s(),
2656 },
2657 IssueKind::WrongCaseMethod {
2658 class: s(),
2659 used: s(),
2660 canonical: s(),
2661 },
2662 IssueKind::WrongCaseClass {
2663 used: s(),
2664 canonical: s(),
2665 },
2666 IssueKind::InvalidAttribute { message: s() },
2667 IssueKind::UndefinedAttributeClass { name: s() },
2668 IssueKind::ForbiddenCode { message: s() },
2669 IssueKind::DuplicateClass { name: s() },
2670 IssueKind::DuplicateInterface { name: s() },
2671 IssueKind::DuplicateTrait { name: s() },
2672 IssueKind::DuplicateEnum { name: s() },
2673 IssueKind::DuplicateFunction { name: s() },
2674 ]
2675 }
2676
2677 #[test]
2678 fn codes_have_expected_shape() {
2679 for kind in one_of_each() {
2680 let code = kind.code();
2681 assert!(
2682 code.len() == 7
2683 && code.starts_with("MIR")
2684 && code[3..].chars().all(|c| c.is_ascii_digit()),
2685 "code {code:?} for {} does not match MIR####",
2686 kind.name(),
2687 );
2688 }
2689 }
2690
2691 #[test]
2692 fn codes_are_unique() {
2693 let kinds = one_of_each();
2694 let mut seen: HashSet<&'static str> = HashSet::new();
2695 for kind in &kinds {
2696 assert!(
2697 seen.insert(kind.code()),
2698 "duplicate code {} (variant {})",
2699 kind.code(),
2700 kind.name(),
2701 );
2702 }
2703 }
2704
2705 #[test]
2706 fn default_severity_for_code_round_trips() {
2707 for kind in one_of_each() {
2708 let code = kind.code();
2709 assert_eq!(
2710 IssueKind::default_severity_for_code(code),
2711 Some(kind.default_severity()),
2712 "severity mismatch for {code} (variant {})",
2713 kind.name(),
2714 );
2715 }
2716 }
2717
2718 #[test]
2719 fn default_severity_for_code_unknown_returns_none() {
2720 assert_eq!(IssueKind::default_severity_for_code("MIR9999"), None);
2721 assert_eq!(IssueKind::default_severity_for_code(""), None);
2722 assert_eq!(IssueKind::default_severity_for_code("mir0001"), None);
2723 }
2724
2725 #[test]
2728 fn one_of_each_has_every_variant() {
2729 assert_eq!(one_of_each().len(), 157);
2732 }
2733}