55use ArrayAccess ;
66use Closure ;
77use Generator ;
8- use PhpParser \Comment \Doc ;
98use PhpParser \Node ;
109use PhpParser \Node \Expr ;
1110use PhpParser \Node \Expr \ArrayDimFetch ;
3029use PHPStan \Analyser \Generator \GeneratorScope ;
3130use PHPStan \Analyser \Generator \InternalThrowPoint ;
3231use PHPStan \Analyser \Generator \NodeCallbackRequest ;
32+ use PHPStan \Analyser \Generator \NodeHandler \VarAnnotationHelper ;
3333use PHPStan \Analyser \Generator \NoopNodeCallback ;
3434use PHPStan \Analyser \Generator \TypeExprRequest ;
3535use PHPStan \Analyser \ImpurePoint ;
4444use PHPStan \Node \Expr \SetOffsetValueTypeExpr ;
4545use PHPStan \Node \PropertyAssignNode ;
4646use PHPStan \Node \VariableAssignNode ;
47- use PHPStan \Node \VarTagChangedExpressionTypeNode ;
4847use PHPStan \Php \PhpVersion ;
4948use PHPStan \Reflection \Php \PhpMethodFromParserNodeReflection ;
5049use PHPStan \Reflection \Php \PhpPropertyReflection ;
5756use PHPStan \Type \Constant \ConstantArrayType ;
5857use PHPStan \Type \Constant \ConstantIntegerType ;
5958use PHPStan \Type \Constant \ConstantStringType ;
60- use PHPStan \Type \FileTypeMapper ;
6159use PHPStan \Type \MixedType ;
6260use PHPStan \Type \NullType ;
6361use PHPStan \Type \ObjectType ;
7472use function array_slice ;
7573use function count ;
7674use function in_array ;
77- use function is_int ;
7875use function is_string ;
7976
8077/**
@@ -88,8 +85,8 @@ final class AssignHandler implements ExprHandler
8885
8986 public function __construct (
9087 private PhpVersion $ phpVersion ,
91- private FileTypeMapper $ fileTypeMapper ,
9288 private AssignHelper $ assignHelper ,
89+ private VarAnnotationHelper $ varAnnotationHelper ,
9390 #[AutowiredParameter(ref: '%exceptions.implicitThrows% ' )]
9491 private readonly bool $ implicitThrows ,
9592 )
@@ -172,11 +169,11 @@ static function (GeneratorScope $scope) use ($stmt, $expr, $context, $alternativ
172169 $ vars = $ this ->getAssignedVariables ($ expr ->var );
173170 if (count ($ vars ) > 0 ) {
174171 $ varChangedScope = false ;
175- $ processVarGen = $ this ->processVarAnnotation ($ scope , $ vars , $ stmt , $ varChangedScope );
172+ $ processVarGen = $ this ->varAnnotationHelper -> processVarAnnotation ($ scope , $ vars , $ stmt , $ varChangedScope );
176173 yield from $ processVarGen ;
177174 $ scope = $ processVarGen ->getReturn ();
178175 if (!$ varChangedScope ) {
179- $ stmtVarGen = $ this ->processStmtVarAnnotation ($ scope , $ stmt , null , $ alternativeNodeCallback );
176+ $ stmtVarGen = $ this ->varAnnotationHelper -> processStmtVarAnnotation ($ scope , $ stmt , null , $ alternativeNodeCallback );
180177 yield from $ stmtVarGen ;
181178 $ scope = $ stmtVarGen ->getReturn ();
182179 }
@@ -228,146 +225,6 @@ private function getAssignedVariables(Expr $expr): array
228225 return [];
229226 }
230227
231- /**
232- * @param array<int, string> $variableNames
233- * @return Generator<int, GeneratorTValueType, GeneratorTSendType, GeneratorScope>
234- */
235- private function processVarAnnotation (GeneratorScope $ scope , array $ variableNames , Node \Stmt $ node , bool &$ changed = false ): Generator
236- {
237- $ function = $ scope ->getFunction ();
238- $ varTags = [];
239- foreach ($ node ->getComments () as $ comment ) {
240- if (!$ comment instanceof Doc) {
241- continue ;
242- }
243-
244- $ resolvedPhpDoc = $ this ->fileTypeMapper ->getResolvedPhpDoc (
245- $ scope ->getFile (),
246- $ scope ->isInClass () ? $ scope ->getClassReflection ()->getName () : null ,
247- $ scope ->isInTrait () ? $ scope ->getTraitReflection ()->getName () : null ,
248- $ function !== null ? $ function ->getName () : null ,
249- $ comment ->getText (),
250- );
251- foreach ($ resolvedPhpDoc ->getVarTags () as $ key => $ varTag ) {
252- $ varTags [$ key ] = $ varTag ;
253- }
254- }
255-
256- if (count ($ varTags ) === 0 ) {
257- return $ scope ;
258- }
259-
260- foreach ($ variableNames as $ variableName ) {
261- if (!isset ($ varTags [$ variableName ])) {
262- continue ;
263- }
264-
265- $ variableType = $ varTags [$ variableName ]->getType ();
266- $ changed = true ;
267- $ assignVarGen = $ scope ->assignVariable ($ variableName , $ variableType , new MixedType (), TrinaryLogic::createYes ());
268- yield from $ assignVarGen ;
269- $ scope = $ assignVarGen ->getReturn ();
270- }
271-
272- if (count ($ variableNames ) === 1 && count ($ varTags ) === 1 && isset ($ varTags [0 ])) {
273- $ variableType = $ varTags [0 ]->getType ();
274- $ changed = true ;
275- $ assignVarGen = $ scope ->assignVariable ($ variableNames [0 ], $ variableType , new MixedType (), TrinaryLogic::createYes ());
276- yield from $ assignVarGen ;
277- $ scope = $ assignVarGen ->getReturn ();
278- }
279-
280- return $ scope ;
281- }
282-
283- /**
284- * @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
285- * @return Generator<int, GeneratorTValueType, GeneratorTSendType, GeneratorScope>
286- */
287- private function processStmtVarAnnotation (GeneratorScope $ scope , Node \Stmt $ stmt , ?Expr $ defaultExpr , ?callable $ alternativeNodeCallback ): Generator
288- {
289- $ function = $ scope ->getFunction ();
290- $ variableLessTags = [];
291-
292- foreach ($ stmt ->getComments () as $ comment ) {
293- if (!$ comment instanceof Doc) {
294- continue ;
295- }
296-
297- $ resolvedPhpDoc = $ this ->fileTypeMapper ->getResolvedPhpDoc (
298- $ scope ->getFile (),
299- $ scope ->isInClass () ? $ scope ->getClassReflection ()->getName () : null ,
300- $ scope ->isInTrait () ? $ scope ->getTraitReflection ()->getName () : null ,
301- $ function !== null ? $ function ->getName () : null ,
302- $ comment ->getText (),
303- );
304-
305- $ assignedVariable = null ;
306- if (
307- $ stmt instanceof Node \Stmt \Expression
308- && ($ stmt ->expr instanceof Assign || $ stmt ->expr instanceof AssignRef)
309- && $ stmt ->expr ->var instanceof Variable
310- && is_string ($ stmt ->expr ->var ->name )
311- ) {
312- $ assignedVariable = $ stmt ->expr ->var ->name ;
313- }
314-
315- foreach ($ resolvedPhpDoc ->getVarTags () as $ name => $ varTag ) {
316- if (is_int ($ name )) {
317- $ variableLessTags [] = $ varTag ;
318- continue ;
319- }
320-
321- if ($ name === $ assignedVariable ) {
322- continue ;
323- }
324-
325- $ certainty = $ scope ->hasVariableType ($ name );
326- if ($ certainty ->no ()) {
327- continue ;
328- }
329-
330- if ($ scope ->isInClass () && $ scope ->getFunction () === null ) {
331- continue ;
332- }
333-
334- if ($ scope ->canAnyVariableExist ()) {
335- $ certainty = TrinaryLogic::createYes ();
336- }
337-
338- $ variableNode = new Variable ($ name , $ stmt ->getAttributes ());
339- $ originalType = $ scope ->getVariableType ($ name );
340- if (!$ originalType ->equals ($ varTag ->getType ())) {
341- yield new NodeCallbackRequest (new VarTagChangedExpressionTypeNode ($ varTag , $ variableNode ), $ scope , $ alternativeNodeCallback );
342- }
343-
344- $ variableNodeResult = yield new ExprAnalysisRequest ($ stmt , $ variableNode , $ scope , ExpressionContext::createDeep (), new NoopNodeCallback ());
345-
346- $ assignVarGen = $ scope ->assignVariable (
347- $ name ,
348- $ varTag ->getType (),
349- $ variableNodeResult ->nativeType ,
350- $ certainty ,
351- );
352- yield from $ assignVarGen ;
353- $ scope = $ assignVarGen ->getReturn ();
354- }
355- }
356-
357- if (count ($ variableLessTags ) === 1 && $ defaultExpr !== null ) {
358- //$originalType = $scope->getType($defaultExpr);
359- $ varTag = $ variableLessTags [0 ];
360- /*if (!$originalType->equals($varTag->getType())) {
361- yield new NodeCallbackRequest(new VarTagChangedExpressionTypeNode($varTag, $defaultExpr), $scope);
362- }*/
363- $ assignExprGen = $ scope ->assignExpression ($ defaultExpr , $ varTag ->getType (), new MixedType ());
364- yield from $ assignExprGen ;
365- $ scope = $ assignExprGen ->getReturn ();
366- }
367-
368- return $ scope ;
369- }
370-
371228 /**
372229 * @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
373230 * @param Closure(GeneratorScope $scope): Generator<int, GeneratorTValueType, GeneratorTSendType, ExprAnalysisResult> $processExprCallback
0 commit comments