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