@@ -42,20 +42,21 @@ public function __construct(
4242 /**
4343 * @param ErrorIdentifier $identifier
4444 * @param callable(Type): ?string $typeMessageCallback
45+ * @param int|null $line null reports the error on the line of the node the error is attached to
4546 */
46- public function check (ExpressionResult $ exprResult , Scope $ scope , string $ operatorDescription , string $ identifier , callable $ typeMessageCallback ): ?IdentifierRuleError
47+ public function check (ExpressionResult $ exprResult , Scope $ scope , string $ operatorDescription , string $ identifier , callable $ typeMessageCallback, ? int $ line = null ): ?IdentifierRuleError
4748 {
4849 $ walkScope = $ scope ->toWalkScope ();
4950 $ resolution = $ exprResult ->getIssetabilityResolution ($ walkScope , !$ this ->treatPhpDocTypesAsCertain , true );
5051
51- return $ this ->doCheck ($ resolution , $ walkScope , $ operatorDescription , $ identifier , $ typeMessageCallback , null );
52+ return $ this ->doCheck ($ resolution , $ walkScope , $ operatorDescription , $ identifier , $ typeMessageCallback , null , $ line );
5253 }
5354
5455 /**
5556 * @param ErrorIdentifier $identifier
5657 * @param callable(Type): ?string $typeMessageCallback
5758 */
58- private function doCheck (IssetabilityResolution $ resolution , MutatingScope $ scope , string $ operatorDescription , string $ identifier , callable $ typeMessageCallback , ?IdentifierRuleError $ error ): ?IdentifierRuleError
59+ private function doCheck (IssetabilityResolution $ resolution , MutatingScope $ scope , string $ operatorDescription , string $ identifier , callable $ typeMessageCallback , ?IdentifierRuleError $ error, ? int $ line ): ?IdentifierRuleError
5960 {
6061 $ link = $ resolution ->getLink ();
6162 $ inner = $ resolution ->getInner ();
@@ -80,11 +81,12 @@ private function doCheck(IssetabilityResolution $resolution, MutatingScope $scop
8081 $ typeMessageCallback ,
8182 $ identifier ,
8283 'variable ' ,
84+ $ line ,
8385 );
8486 }
8587 }
8688
87- return RuleErrorBuilder:: message (sprintf ('Variable $%s %s is never defined. ' , $ link ->getVariableName (), $ operatorDescription ))
89+ return $ this -> errorBuilder (sprintf ('Variable $%s %s is never defined. ' , $ link ->getVariableName (), $ operatorDescription ), $ line )
8890 ->identifier (sprintf ('%s.variable ' , $ identifier ))
8991 ->build ();
9092 }
@@ -95,7 +97,7 @@ private function doCheck(IssetabilityResolution $resolution, MutatingScope $scop
9597 if ($ link ->isOffset ()) {
9698 $ type = $ link ->getVarType ();
9799 if (!$ link ->getIsOffsetAccessible ()->yes ()) {
98- return $ error ?? $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier );
100+ return $ error ?? $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier, $ line );
99101 }
100102
101103 $ dimType = $ link ->getDimType ();
@@ -105,13 +107,14 @@ private function doCheck(IssetabilityResolution $resolution, MutatingScope $scop
105107 return null ;
106108 }
107109
108- return RuleErrorBuilder:: message (
110+ return $ this -> errorBuilder (
109111 sprintf (
110112 'Offset %s on %s %s does not exist. ' ,
111113 $ dimType ->describe (VerbosityLevel::value ()),
112114 $ type ->describe (VerbosityLevel::value ()),
113115 $ operatorDescription ,
114116 ),
117+ $ line ,
115118 )->identifier (sprintf ('%s.offset ' , $ identifier ))->build ();
116119 }
117120
@@ -127,10 +130,10 @@ private function doCheck(IssetabilityResolution $resolution, MutatingScope $scop
127130 $ dimType ->describe (VerbosityLevel::value ()),
128131 $ type ->describe (VerbosityLevel::value ()),
129132 $ operatorDescription ,
130- ), $ typeMessageCallback , $ identifier , 'offset ' );
133+ ), $ typeMessageCallback , $ identifier , 'offset ' , $ line );
131134
132135 if ($ error !== null ) {
133- return $ inner !== null ? $ this ->doCheck ($ inner , $ scope , $ operatorDescription , $ identifier , $ typeMessageCallback , $ error ) : $ error ;
136+ return $ inner !== null ? $ this ->doCheck ($ inner , $ scope , $ operatorDescription , $ identifier , $ typeMessageCallback , $ error, $ line ) : $ error ;
134137 }
135138 }
136139
@@ -143,7 +146,7 @@ private function doCheck(IssetabilityResolution $resolution, MutatingScope $scop
143146 $ propertyFetch = $ link ->getPropertyFetch ();
144147
145148 if ($ reflection === null || !$ link ->isReflectionNative ()) {
146- return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier );
149+ return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier, $ line );
147150 }
148151
149152 if ($ link ->hasNativeType () && !$ link ->isVirtual ()->yes ()) {
@@ -165,6 +168,7 @@ static function (Type $type) use ($typeMessageCallback): ?string {
165168 },
166169 $ identifier ,
167170 'initializedProperty ' ,
171+ $ line ,
168172 );
169173 }
170174
@@ -182,11 +186,11 @@ static function (Type $type) use ($typeMessageCallback): ?string {
182186 $ propertyType = $ reflection ->getWritableType ();
183187 if ($ error !== null ) {
184188 return $ inner !== null
185- ? $ this ->doCheck ($ inner , $ scope , $ operatorDescription , $ identifier , $ typeMessageCallback , $ error )
189+ ? $ this ->doCheck ($ inner , $ scope , $ operatorDescription , $ identifier , $ typeMessageCallback , $ error, $ line )
186190 : $ error ;
187191 }
188192 if (!$ this ->checkAdvancedIsset ) {
189- return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier );
193+ return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier, $ line );
190194 }
191195
192196 $ error = $ this ->generateError (
@@ -195,10 +199,11 @@ static function (Type $type) use ($typeMessageCallback): ?string {
195199 $ typeMessageCallback ,
196200 $ identifier ,
197201 'property ' ,
202+ $ line ,
198203 );
199204
200205 if ($ error !== null && $ inner !== null ) {
201- return $ this ->doCheck ($ inner , $ scope , $ operatorDescription , $ identifier , $ typeMessageCallback , $ error );
206+ return $ this ->doCheck ($ inner , $ scope , $ operatorDescription , $ identifier , $ typeMessageCallback , $ error, $ line );
202207 }
203208
204209 return $ error ;
@@ -219,6 +224,7 @@ static function (Type $type) use ($typeMessageCallback): ?string {
219224 $ typeMessageCallback ,
220225 $ identifier ,
221226 'expr ' ,
227+ $ line ,
222228 );
223229 if ($ error !== null ) {
224230 return $ error ;
@@ -227,12 +233,12 @@ static function (Type $type) use ($typeMessageCallback): ?string {
227233 if ($ link ->leafIsNullsafePropertyFetch ()) {
228234 $ leafExpr = $ link ->getLeafExpr ();
229235 if ($ leafExpr instanceof NullsafePropertyFetch && $ leafExpr ->name instanceof Identifier) {
230- return RuleErrorBuilder:: message (sprintf ('Using nullsafe property access "?->%s" %s is unnecessary. Use -> instead. ' , $ leafExpr ->name ->name , $ operatorDescription ))
236+ return $ this -> errorBuilder (sprintf ('Using nullsafe property access "?->%s" %s is unnecessary. Use -> instead. ' , $ leafExpr ->name ->name , $ operatorDescription ), $ line )
231237 ->identifier ('nullsafe.neverNull ' )
232238 ->build ();
233239 }
234240
235- return RuleErrorBuilder:: message (sprintf ('Using nullsafe property access "?->(Expression)" %s is unnecessary. Use -> instead. ' , $ operatorDescription ))
241+ return $ this -> errorBuilder (sprintf ('Using nullsafe property access "?->(Expression)" %s is unnecessary. Use -> instead. ' , $ operatorDescription ), $ line )
236242 ->identifier ('nullsafe.neverNull ' )
237243 ->build ();
238244 }
@@ -243,7 +249,7 @@ static function (Type $type) use ($typeMessageCallback): ?string {
243249 /**
244250 * @param ErrorIdentifier $identifier
245251 */
246- private function checkUndefinedInner (?IssetabilityResolution $ resolution , MutatingScope $ scope , string $ operatorDescription , string $ identifier ): ?IdentifierRuleError
252+ private function checkUndefinedInner (?IssetabilityResolution $ resolution , MutatingScope $ scope , string $ operatorDescription , string $ identifier, ? int $ line ): ?IdentifierRuleError
247253 {
248254 if ($ resolution === null ) {
249255 return null ;
@@ -257,32 +263,33 @@ private function checkUndefinedInner(?IssetabilityResolution $resolution, Mutati
257263 return null ;
258264 }
259265
260- return RuleErrorBuilder:: message (sprintf ('Variable $%s %s is never defined. ' , $ link ->getVariableName (), $ operatorDescription ))
266+ return $ this -> errorBuilder (sprintf ('Variable $%s %s is never defined. ' , $ link ->getVariableName (), $ operatorDescription ), $ line )
261267 ->identifier (sprintf ('%s.variable ' , $ identifier ))
262268 ->build ();
263269 }
264270
265271 if ($ link ->isOffset ()) {
266272 if (!$ link ->getIsOffsetAccessible ()->yes ()) {
267- return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier );
273+ return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier, $ line );
268274 }
269275
270276 if (!$ link ->getHasOffsetValue ()->no ()) {
271- return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier );
277+ return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier, $ line );
272278 }
273279
274- return RuleErrorBuilder:: message (
280+ return $ this -> errorBuilder (
275281 sprintf (
276282 'Offset %s on %s %s does not exist. ' ,
277283 $ link ->getDimType ()->describe (VerbosityLevel::value ()),
278284 $ link ->getVarType ()->describe (VerbosityLevel::value ()),
279285 $ operatorDescription ,
280286 ),
287+ $ line ,
281288 )->identifier (sprintf ('%s.offset ' , $ identifier ))->build ();
282289 }
283290
284291 if ($ link ->isProperty ()) {
285- return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier );
292+ return $ this ->checkUndefinedInner ($ inner , $ scope , $ operatorDescription , $ identifier, $ line );
286293 }
287294
288295 return null ;
@@ -293,16 +300,30 @@ private function checkUndefinedInner(?IssetabilityResolution $resolution, Mutati
293300 * @param ErrorIdentifier $identifier
294301 * @param 'variable'|'offset'|'property'|'expr'|'initializedProperty' $identifierSecondPart
295302 */
296- private function generateError (Type $ type , string $ message , callable $ typeMessageCallback , string $ identifier , string $ identifierSecondPart ): ?IdentifierRuleError
303+ private function generateError (Type $ type , string $ message , callable $ typeMessageCallback , string $ identifier , string $ identifierSecondPart, ? int $ line ): ?IdentifierRuleError
297304 {
298305 $ typeMessage = $ typeMessageCallback ($ type );
299306 if ($ typeMessage === null ) {
300307 return null ;
301308 }
302309
303- return RuleErrorBuilder:: message (
310+ return $ this -> errorBuilder (
304311 sprintf ('%s %s. ' , $ message , $ typeMessage ),
312+ $ line ,
305313 )->identifier (sprintf ('%s.%s ' , $ identifier , $ identifierSecondPart ))->build ();
306314 }
307315
316+ /**
317+ * @return RuleErrorBuilder<RuleError>
318+ */
319+ private function errorBuilder (string $ message , ?int $ line ): RuleErrorBuilder
320+ {
321+ $ builder = RuleErrorBuilder::message ($ message );
322+ if ($ line === null ) {
323+ return $ builder ;
324+ }
325+
326+ return $ builder ->line ($ line );
327+ }
328+
308329}
0 commit comments