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