5
5
use PhpParser \Node ;
6
6
use PHPStan \Analyser \Scope ;
7
7
use PHPStan \Node \InPropertyHookNode ;
8
+ use PHPStan \Rules \MissingTypehintCheck ;
8
9
use PHPStan \Rules \Rule ;
9
10
use PHPStan \Rules \RuleErrorBuilder ;
10
11
use PHPStan \ShouldNotHappenException ;
18
19
final class SetPropertyHookParameterRule implements Rule
19
20
{
20
21
21
- public function __construct (private bool $ checkPhpDocMethodSignatures )
22
+ public function __construct (
23
+ private MissingTypehintCheck $ missingTypehintCheck ,
24
+ private bool $ checkPhpDocMethodSignatures ,
25
+ private bool $ checkMissingTypehints ,
26
+ )
22
27
{
23
28
}
24
29
@@ -87,10 +92,12 @@ public function processNode(Node $node, Scope $scope): array
87
92
return $ errors ;
88
93
}
89
94
90
- if (!$ parameter ->getType ()->isSuperTypeOf ($ propertyReflection ->getReadableType ())->yes ()) {
95
+ $ parameterType = $ parameter ->getType ();
96
+
97
+ if (!$ parameterType ->isSuperTypeOf ($ propertyReflection ->getReadableType ())->yes ()) {
91
98
$ errors [] = RuleErrorBuilder::message (sprintf (
92
99
'Type %s of set hook parameter $%s is not contravariant with type %s of property %s::$%s. ' ,
93
- $ parameter -> getType () ->describe (VerbosityLevel::value ()),
100
+ $ parameterType ->describe (VerbosityLevel::value ()),
94
101
$ parameter ->getName (),
95
102
$ propertyReflection ->getReadableType ()->describe (VerbosityLevel::value ()),
96
103
$ classReflection ->getDisplayName (),
@@ -99,6 +106,51 @@ public function processNode(Node $node, Scope $scope): array
99
106
->build ();
100
107
}
101
108
109
+ if (!$ this ->checkMissingTypehints ) {
110
+ return $ errors ;
111
+ }
112
+
113
+ if ($ parameter ->getNativeType ()->equals ($ propertyReflection ->getReadableType ())) {
114
+ return $ errors ;
115
+ }
116
+
117
+ foreach ($ this ->missingTypehintCheck ->getIterableTypesWithMissingValueTypehint ($ parameterType ) as $ iterableType ) {
118
+ $ iterableTypeDescription = $ iterableType ->describe (VerbosityLevel::typeOnly ());
119
+ $ errors [] = RuleErrorBuilder::message (sprintf (
120
+ 'Set hook for property %s::$%s has parameter $%s with no value type specified in iterable type %s. ' ,
121
+ $ classReflection ->getDisplayName (),
122
+ $ hookReflection ->getHookedPropertyName (),
123
+ $ parameter ->getName (),
124
+ $ iterableTypeDescription ,
125
+ ))
126
+ ->tip (MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP )
127
+ ->identifier ('missingType.iterableValue ' )
128
+ ->build ();
129
+ }
130
+
131
+ foreach ($ this ->missingTypehintCheck ->getNonGenericObjectTypesWithGenericClass ($ parameterType ) as [$ name , $ genericTypeNames ]) {
132
+ $ errors [] = RuleErrorBuilder::message (sprintf (
133
+ 'Set hook for property %s::$%s has parameter $%s with generic %s but does not specify its types: %s ' ,
134
+ $ classReflection ->getDisplayName (),
135
+ $ hookReflection ->getHookedPropertyName (),
136
+ $ parameter ->getName (),
137
+ $ name ,
138
+ $ genericTypeNames ,
139
+ ))
140
+ ->identifier ('missingType.generics ' )
141
+ ->build ();
142
+ }
143
+
144
+ foreach ($ this ->missingTypehintCheck ->getCallablesWithMissingSignature ($ parameterType ) as $ callableType ) {
145
+ $ errors [] = RuleErrorBuilder::message (sprintf (
146
+ 'Set hook for property %s::$%s has parameter $%s with no signature specified for %s. ' ,
147
+ $ classReflection ->getDisplayName (),
148
+ $ hookReflection ->getHookedPropertyName (),
149
+ $ parameter ->getName (),
150
+ $ callableType ->describe (VerbosityLevel::typeOnly ()),
151
+ ))->identifier ('missingType.callable ' )->build ();
152
+ }
153
+
102
154
return $ errors ;
103
155
}
104
156
0 commit comments