88use PHPStan \Rules \Properties \PropertyDescriptor ;
99use PHPStan \Rules \Properties \PropertyReflectionFinder ;
1010use PHPStan \Type \MixedType ;
11- use PHPStan \Type \NullType ;
1211use PHPStan \Type \Type ;
1312use PHPStan \Type \VerbosityLevel ;
1413
@@ -28,7 +27,10 @@ public function __construct(
2827 $ this ->propertyReflectionFinder = $ propertyReflectionFinder ;
2928 }
3029
31- public function check (Expr $ expr , Scope $ scope , string $ operatorDescription , ?RuleError $ error = null ): ?RuleError
30+ /**
31+ * @param callable(Type): ?string $typeMessageCallback
32+ */
33+ public function check (Expr $ expr , Scope $ scope , string $ operatorDescription , callable $ typeMessageCallback , ?RuleError $ error = null ): ?RuleError
3234 {
3335 if ($ expr instanceof Node \Expr \Variable && is_string ($ expr ->name )) {
3436 $ hasVariable = $ scope ->hasVariableType ($ expr ->name );
@@ -70,10 +72,10 @@ public function check(Expr $expr, Scope $scope, string $operatorDescription, ?Ru
7072 $ dimType ->describe (VerbosityLevel::value ()),
7173 $ type ->describe (VerbosityLevel::value ()),
7274 $ operatorDescription
73- ));
75+ ), $ typeMessageCallback );
7476
7577 if ($ error !== null ) {
76- return $ this ->check ($ expr ->var , $ scope , $ operatorDescription , $ error );
78+ return $ this ->check ($ expr ->var , $ scope , $ operatorDescription , $ typeMessageCallback , $ error );
7779 }
7880 }
7981
@@ -104,42 +106,39 @@ public function check(Expr $expr, Scope $scope, string $operatorDescription, ?Ru
104106
105107 $ error = $ error ?? $ this ->generateError (
106108 $ propertyReflection ->getWritableType (),
107- sprintf ('%s (%s) %s ' , $ propertyDescription , $ propertyType ->describe (VerbosityLevel::typeOnly ()), $ operatorDescription )
109+ sprintf ('%s (%s) %s ' , $ propertyDescription , $ propertyType ->describe (VerbosityLevel::typeOnly ()), $ operatorDescription ),
110+ $ typeMessageCallback
108111 );
109112
110113 if ($ error !== null ) {
111114 if ($ expr instanceof Node \Expr \PropertyFetch) {
112- return $ this ->check ($ expr ->var , $ scope , $ operatorDescription , $ error );
115+ return $ this ->check ($ expr ->var , $ scope , $ operatorDescription , $ typeMessageCallback , $ error );
113116 }
114117
115118 if ($ expr ->class instanceof Expr) {
116- return $ this ->check ($ expr ->class , $ scope , $ operatorDescription , $ error );
119+ return $ this ->check ($ expr ->class , $ scope , $ operatorDescription , $ typeMessageCallback , $ error );
117120 }
118121 }
119122
120123 return $ error ;
121124 }
122125
123- return $ error ?? $ this ->generateError ($ scope ->getType ($ expr ), sprintf ('Expression %s ' , $ operatorDescription ));
126+ return $ error ?? $ this ->generateError ($ scope ->getType ($ expr ), sprintf ('Expression %s ' , $ operatorDescription ), $ typeMessageCallback );
124127 }
125128
126- private function generateError (Type $ type , string $ message ): ?RuleError
129+ /**
130+ * @param callable(Type): ?string $typeMessageCallback
131+ */
132+ private function generateError (Type $ type , string $ message , callable $ typeMessageCallback ): ?RuleError
127133 {
128- $ nullType = new NullType ();
129-
130- if ($ type ->equals ($ nullType )) {
131- return RuleErrorBuilder::message (
132- sprintf ('%s is always null. ' , $ message )
133- )->build ();
134- }
135-
136- if ($ type ->isSuperTypeOf ($ nullType )->no ()) {
137- return RuleErrorBuilder::message (
138- sprintf ('%s is not nullable. ' , $ message )
139- )->build ();
134+ $ typeMessage = $ typeMessageCallback ($ type );
135+ if ($ typeMessage === null ) {
136+ return null ;
140137 }
141138
142- return null ;
139+ return RuleErrorBuilder::message (
140+ sprintf ('%s %s. ' , $ message , $ typeMessage )
141+ )->build ();
143142 }
144143
145144}
0 commit comments