InstancePropertyFetchAnalyzer   F
last analyzed

Complexity

Total Complexity 221

Size/Duplication

Total Lines 1126
Duplicated Lines 25.58 %

Coupling/Cohesion

Components 1
Dependencies 54

Importance

Changes 0
Metric Value
dl 288
loc 1126
rs 1.096
c 0
b 0
f 0
wmc 221
lcom 1
cbo 54

3 Methods

Rating   Name   Duplication   Size   Complexity  
F analyze() 288 944 194
C localizePropertyType() 0 74 14
C processTaints() 0 103 13

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like InstancePropertyFetchAnalyzer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use InstancePropertyFetchAnalyzer, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
3
4
use PhpParser;
5
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
6
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
7
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
8
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
9
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
10
use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
11
use Psalm\Internal\Analyzer\StatementsAnalyzer;
12
use Psalm\Internal\Type\TemplateResult;
13
use Psalm\CodeLocation;
14
use Psalm\Context;
15
use Psalm\Issue\DeprecatedProperty;
16
use Psalm\Issue\InvalidPropertyFetch;
17
use Psalm\Issue\InternalProperty;
18
use Psalm\Issue\MissingPropertyType;
19
use Psalm\Issue\MixedPropertyFetch;
20
use Psalm\Issue\NoInterfaceProperties;
21
use Psalm\Issue\NullPropertyFetch;
22
use Psalm\Issue\PossiblyInvalidPropertyFetch;
23
use Psalm\Issue\PossiblyNullPropertyFetch;
24
use Psalm\Issue\UndefinedClass;
25
use Psalm\Issue\UndefinedDocblockClass;
26
use Psalm\Issue\UndefinedMagicPropertyFetch;
27
use Psalm\Issue\UndefinedPropertyFetch;
28
use Psalm\Issue\UndefinedThisPropertyFetch;
29
use Psalm\Issue\UninitializedProperty;
30
use Psalm\IssueBuffer;
31
use Psalm\Type;
32
use Psalm\Storage\ClassLikeStorage;
33
use Psalm\Type\Atomic\TGenericObject;
34
use Psalm\Type\Atomic\TNamedObject;
35
use Psalm\Type\Atomic\TNull;
36
use Psalm\Type\Atomic\TObject;
37
use Psalm\Type\Atomic\TObjectWithProperties;
38
use function strtolower;
39
use function array_values;
40
use function in_array;
41
use function array_keys;
42
use Psalm\Internal\Taint\TaintNode;
43
44
/**
45
 * @internal
46
 */
47
class InstancePropertyFetchAnalyzer
48
{
49
    public static function analyze(
50
        StatementsAnalyzer $statements_analyzer,
51
        PhpParser\Node\Expr\PropertyFetch $stmt,
52
        Context $context,
53
        bool $in_assignment = false
54
    ) : bool {
55
        if (!$stmt->name instanceof PhpParser\Node\Identifier) {
56
            if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context) === false) {
57
                return false;
58
            }
59
        }
60
61
        if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) {
62
            return false;
63
        }
64
65 View Code Duplication
        if ($stmt->name instanceof PhpParser\Node\Identifier) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
66
            $prop_name = $stmt->name->name;
67
        } elseif (($stmt_name_type = $statements_analyzer->node_data->getType($stmt->name))
68
            && $stmt_name_type->isSingleStringLiteral()
69
        ) {
70
            $prop_name = $stmt_name_type->getSingleStringLiteral()->value;
71
        } else {
72
            $prop_name = null;
73
        }
74
75
        $codebase = $statements_analyzer->getCodebase();
76
77
        $stmt_var_id = ExpressionIdentifier::getArrayVarId(
78
            $stmt->var,
79
            $statements_analyzer->getFQCLN(),
80
            $statements_analyzer
81
        );
82
83
        $var_id = ExpressionIdentifier::getArrayVarId(
84
            $stmt,
85
            $statements_analyzer->getFQCLN(),
86
            $statements_analyzer
87
        );
88
89
        $stmt_type = null;
0 ignored issues
show
Unused Code introduced by
$stmt_type is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
90
91
        if ($var_id && $context->hasVariable($var_id, $statements_analyzer)) {
92
            $stmt_type = $context->vars_in_scope[$var_id];
93
94
            // we don't need to check anything
95
            $statements_analyzer->node_data->setType($stmt, $stmt_type);
96
97
            if (!$context->collect_initializations
98
                && !$context->collect_mutations
99
                && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
100
                && (!(($parent_source = $statements_analyzer->getSource())
101
                        instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
102
                    || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
103
            ) {
104
                $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
105
            }
106
107 View Code Duplication
            if ($codebase->store_node_types
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
108
                && !$context->collect_initializations
109
                && !$context->collect_mutations
110
            ) {
111
                $codebase->analyzer->addNodeType(
112
                    $statements_analyzer->getFilePath(),
113
                    $stmt->name,
114
                    $stmt_type->getId()
115
                );
116
            }
117
118
            if ($stmt_var_id === '$this'
119
                && !$stmt_type->initialized
120
                && $context->collect_initializations
121
                && ($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
122
                && $stmt_var_type->hasObjectType()
123
                && $stmt->name instanceof PhpParser\Node\Identifier
124
            ) {
125
                $source = $statements_analyzer->getSource();
126
127
                $property_id = null;
128
129
                foreach ($stmt_var_type->getAtomicTypes() as $lhs_type_part) {
130
                    if ($lhs_type_part instanceof TNamedObject) {
131
                        if (!$codebase->classExists($lhs_type_part->value)) {
132
                            continue;
133
                        }
134
135
                        $property_id = $lhs_type_part->value . '::$' . $stmt->name->name;
136
                    }
137
                }
138
139
                if ($property_id
140
                    && $source instanceof FunctionLikeAnalyzer
141
                    && $source->getMethodName() === '__construct'
142
                    && !$context->inside_unset
143
                ) {
144
                    if ($context->inside_isset
145
                        || ($context->inside_assignment
146
                            && isset($context->vars_in_scope[$var_id])
147
                            && $context->vars_in_scope[$var_id]->isNullable()
148
                        )
149
                    ) {
150
                        $stmt_type->initialized = true;
151
                    } else {
152
                        if (IssueBuffer::accepts(
153
                            new UninitializedProperty(
154
                                'Cannot use uninitialized property ' . $var_id,
155
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
156
                                $var_id
157
                            ),
158
                            $statements_analyzer->getSuppressedIssues()
159
                        )) {
160
                            // fall through
161
                        }
162
163
                        $stmt_type->addType(new Type\Atomic\TNull);
164
                    }
165
                }
166
            }
167
168
            if (($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
169
                && $stmt_var_type->hasObjectType()
170
                && $stmt->name instanceof PhpParser\Node\Identifier
171
            ) {
172
                // log the appearance
173
                foreach ($stmt_var_type->getAtomicTypes() as $lhs_type_part) {
174
                    if ($lhs_type_part instanceof TNamedObject) {
175
                        if (!$codebase->classExists($lhs_type_part->value)) {
176
                            continue;
177
                        }
178
179
                        $property_id = $lhs_type_part->value . '::$' . $stmt->name->name;
180
181
                        self::processTaints(
182
                            $statements_analyzer,
183
                            $stmt,
184
                            $stmt_type,
185
                            $property_id,
186
                            $codebase->classlike_storage_provider->get($lhs_type_part->value),
187
                            $in_assignment
188
                        );
189
190
                        $codebase->properties->propertyExists(
191
                            $property_id,
192
                            true,
193
                            $statements_analyzer,
194
                            $context,
195
                            $codebase->collect_locations
196
                                ? new CodeLocation($statements_analyzer->getSource(), $stmt)
197
                                : null
198
                        );
199
200 View Code Duplication
                        if ($codebase->store_node_types
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
                            && !$context->collect_initializations
202
                            && !$context->collect_mutations
203
                        ) {
204
                            $codebase->analyzer->addNodeReference(
205
                                $statements_analyzer->getFilePath(),
206
                                $stmt->name,
207
                                $property_id
208
                            );
209
                        }
210
                    }
211
                }
212
            }
213
214
            return true;
215
        }
216
217
        if ($stmt_var_id && $context->hasVariable($stmt_var_id, $statements_analyzer)) {
218
            $stmt_var_type = $context->vars_in_scope[$stmt_var_id];
219
        } else {
220
            $stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
221
        }
222
223
        if (!$stmt_var_type) {
224
            return true;
225
        }
226
227
        if ($stmt_var_type->isNull()) {
228
            if (IssueBuffer::accepts(
229
                new NullPropertyFetch(
230
                    'Cannot get property on null variable ' . $stmt_var_id,
231
                    new CodeLocation($statements_analyzer->getSource(), $stmt)
232
                ),
233
                $statements_analyzer->getSuppressedIssues()
234
            )) {
235
                return false;
236
            }
237
238
            return true;
239
        }
240
241
        if ($stmt_var_type->isEmpty()) {
242
            if (IssueBuffer::accepts(
243
                new MixedPropertyFetch(
244
                    'Cannot fetch property on empty var ' . $stmt_var_id,
245
                    new CodeLocation($statements_analyzer->getSource(), $stmt)
246
                ),
247
                $statements_analyzer->getSuppressedIssues()
248
            )) {
249
                return false;
250
            }
251
252
            return true;
253
        }
254
255
        if ($stmt_var_type->hasMixed()) {
256
            if (!$context->collect_initializations
257
                && !$context->collect_mutations
258
                && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
259
                && (!(($parent_source = $statements_analyzer->getSource())
260
                        instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
261
                    || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
262
            ) {
263
                $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
264
            }
265
266 View Code Duplication
            if ($stmt->name instanceof PhpParser\Node\Identifier) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
                $codebase->analyzer->addMixedMemberName(
268
                    '$' . $stmt->name->name,
269
                    $context->calling_method_id ?: $statements_analyzer->getFileName()
270
                );
271
            }
272
273
            if (IssueBuffer::accepts(
274
                new MixedPropertyFetch(
275
                    'Cannot fetch property on mixed var ' . $stmt_var_id,
276
                    new CodeLocation($statements_analyzer->getSource(), $stmt)
277
                ),
278
                $statements_analyzer->getSuppressedIssues()
279
            )) {
280
                // fall through
281
            }
282
283
            $statements_analyzer->node_data->setType($stmt, Type::getMixed());
284
285 View Code Duplication
            if ($codebase->store_node_types
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
                && !$context->collect_initializations
287
                && !$context->collect_mutations
288
            ) {
289
                $codebase->analyzer->addNodeType(
290
                    $statements_analyzer->getFilePath(),
291
                    $stmt->name,
292
                    $stmt_var_type->getId()
293
                );
294
            }
295
        }
296
297
        if (!$context->collect_initializations
298
            && !$context->collect_mutations
299
            && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
300
            && (!(($parent_source = $statements_analyzer->getSource())
301
                    instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
302
                || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
303
        ) {
304
            $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getRootFilePath());
305
        }
306
307
        if ($stmt_var_type->isNullable() && !$stmt_var_type->ignore_nullable_issues) {
308 View Code Duplication
            if (!$context->inside_isset) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309
                if (IssueBuffer::accepts(
310
                    new PossiblyNullPropertyFetch(
311
                        'Cannot get property on possibly null variable ' . $stmt_var_id . ' of type ' . $stmt_var_type,
312
                        new CodeLocation($statements_analyzer->getSource(), $stmt)
313
                    ),
314
                    $statements_analyzer->getSuppressedIssues()
315
                )) {
316
                    // fall through
317
                }
318
            } else {
319
                $statements_analyzer->node_data->setType($stmt, Type::getNull());
320
            }
321
        }
322
323
        if (!$prop_name) {
324
            if ($stmt_var_type->hasObjectType() && !$context->ignore_variable_property) {
325
                foreach ($stmt_var_type->getAtomicTypes() as $type) {
326 View Code Duplication
                    if ($type instanceof Type\Atomic\TNamedObject) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
327
                        $codebase->analyzer->addMixedMemberName(
328
                            strtolower($type->value) . '::$',
329
                            $context->calling_method_id ?: $statements_analyzer->getFileName()
330
                        );
331
                    }
332
                }
333
            }
334
335
            return true;
336
        }
337
338
        $invalid_fetch_types = [];
339
        $has_valid_fetch_type = false;
340
341
        foreach ($stmt_var_type->getAtomicTypes() as $lhs_type_part) {
342
            if ($lhs_type_part instanceof TNull) {
343
                continue;
344
            }
345
346
            if ($lhs_type_part instanceof Type\Atomic\TTemplateParam) {
347
                $extra_types = $lhs_type_part->extra_types;
348
349
                $lhs_type_part = array_values(
350
                    $lhs_type_part->as->getAtomicTypes()
351
                )[0];
352
353
                $lhs_type_part->from_docblock = true;
354
355
                if ($lhs_type_part instanceof TNamedObject) {
356
                    $lhs_type_part->extra_types = $extra_types;
357
                }
358
            }
359
360
            if ($lhs_type_part instanceof Type\Atomic\TMixed) {
361
                $statements_analyzer->node_data->setType($stmt, Type::getMixed());
362
                continue;
363
            }
364
365
            if ($lhs_type_part instanceof Type\Atomic\TFalse && $stmt_var_type->ignore_falsable_issues) {
366
                continue;
367
            }
368
369
            if (!$lhs_type_part instanceof TNamedObject && !$lhs_type_part instanceof TObject) {
370
                $invalid_fetch_types[] = (string)$lhs_type_part;
371
372
                continue;
373
            }
374
375
            $has_valid_fetch_type = true;
376
377
            if ($lhs_type_part instanceof TObjectWithProperties
378
                && isset($lhs_type_part->properties[$prop_name])
379
            ) {
380
                if ($stmt_type = $statements_analyzer->node_data->getType($stmt)) {
381
                    $statements_analyzer->node_data->setType(
382
                        $stmt,
383
                        Type::combineUnionTypes(
384
                            $lhs_type_part->properties[$prop_name],
385
                            $stmt_type
386
                        )
387
                    );
388
                } else {
389
                    $statements_analyzer->node_data->setType($stmt, $lhs_type_part->properties[$prop_name]);
390
                }
391
392
                continue;
393
            }
394
395
            // stdClass and SimpleXMLElement are special cases where we cannot infer the return types
396
            // but we don't want to throw an error
397
            // Hack has a similar issue: https://github.com/facebook/hhvm/issues/5164
398
            if ($lhs_type_part instanceof TObject
399
                || in_array(strtolower($lhs_type_part->value), ['stdclass', 'simplexmlelement'], true)
400
            ) {
401
                $statements_analyzer->node_data->setType($stmt, Type::getMixed());
402
403
                continue;
404
            }
405
406
            if (ExpressionAnalyzer::isMock($lhs_type_part->value)) {
407
                $statements_analyzer->node_data->setType($stmt, Type::getMixed());
408
                continue;
409
            }
410
411
            $intersection_types = $lhs_type_part->getIntersectionTypes() ?: [];
412
413
            $fq_class_name = $lhs_type_part->value;
414
415
            $override_property_visibility = false;
416
417
            $has_magic_getter = false;
418
419
            $class_exists = false;
420
            $interface_exists = false;
421
422
            if (!$codebase->classExists($lhs_type_part->value)) {
423 View Code Duplication
                if ($codebase->interfaceExists($lhs_type_part->value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
424
                    $interface_exists = true;
425
                    $interface_storage = $codebase->classlike_storage_provider->get($lhs_type_part->value);
426
427
                    $override_property_visibility = $interface_storage->override_property_visibility;
428
429
                    foreach ($intersection_types as $intersection_type) {
430
                        if ($intersection_type instanceof TNamedObject
431
                            && $codebase->classExists($intersection_type->value)
432
                        ) {
433
                            $fq_class_name = $intersection_type->value;
434
                            $class_exists = true;
435
                            break;
436
                        }
437
                    }
438
439
                    if (!$class_exists) {
440
                        if (IssueBuffer::accepts(
441
                            new NoInterfaceProperties(
442
                                'Interfaces cannot have properties',
443
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
444
                                $lhs_type_part->value
445
                            ),
446
                            $statements_analyzer->getSuppressedIssues()
447
                        )) {
448
                            return true;
449
                        }
450
451
                        if (!$codebase->methodExists($fq_class_name . '::__set')) {
452
                            return true;
453
                        }
454
                    }
455
                }
456
457
                if (!$class_exists && !$interface_exists) {
458 View Code Duplication
                    if ($lhs_type_part->from_docblock) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
459
                        if (IssueBuffer::accepts(
460
                            new UndefinedDocblockClass(
461
                                'Cannot set properties of undefined docblock class ' . $lhs_type_part->value,
462
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
463
                                $lhs_type_part->value
464
                            ),
465
                            $statements_analyzer->getSuppressedIssues()
466
                        )) {
467
                            // fall through
468
                        }
469
                    } else {
470
                        if (IssueBuffer::accepts(
471
                            new UndefinedClass(
472
                                'Cannot set properties of undefined class ' . $lhs_type_part->value,
473
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
474
                                $lhs_type_part->value
475
                            ),
476
                            $statements_analyzer->getSuppressedIssues()
477
                        )) {
478
                            // fall through
479
                        }
480
                    }
481
482
                    return true;
483
                }
484
            } else {
485
                $class_exists = true;
486
            }
487
488
            $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
489
            $property_id = $fq_class_name . '::$' . $prop_name;
490
491
            $naive_property_exists = $codebase->properties->propertyExists(
492
                $property_id,
493
                true,
494
                $statements_analyzer,
495
                $context,
496
                $codebase->collect_locations ? new CodeLocation($statements_analyzer->getSource(), $stmt) : null
497
            );
498
499
            // add method before changing fq_class_name
500
            $get_method_id = new \Psalm\Internal\MethodIdentifier($fq_class_name, '__get');
501
502
            if (!$naive_property_exists
503
                && $class_storage->mixin instanceof Type\Atomic\TNamedObject
504
            ) {
505
                $new_property_id = $class_storage->mixin->value . '::$' . $prop_name;
506
507
                try {
508
                    $new_class_storage = $codebase->classlike_storage_provider->get($class_storage->mixin->value);
509
                } catch (\InvalidArgumentException $e) {
510
                    $new_class_storage = null;
511
                }
512
513
                if ($new_class_storage
514
                    && ($codebase->properties->propertyExists(
515
                        $new_property_id,
516
                        true,
517
                        $statements_analyzer,
518
                        $context,
519
                        $codebase->collect_locations
520
                            ? new CodeLocation($statements_analyzer->getSource(), $stmt)
521
                            : null
522
                    )
523
                        || isset($new_class_storage->pseudo_property_get_types['$' . $prop_name]))
524
                ) {
525
                    $fq_class_name = $class_storage->mixin->value;
526
                    $lhs_type_part = clone $class_storage->mixin;
527
                    $class_storage = $new_class_storage;
528
529
                    if (!isset($new_class_storage->pseudo_property_get_types['$' . $prop_name])) {
530
                        $naive_property_exists = true;
531
                    }
532
533
                    $property_id = $new_property_id;
534
                }
535
            }
536
537
            if ((!$naive_property_exists
538
                    || ($stmt_var_id !== '$this'
539
                        && $fq_class_name !== $context->self
540
                        && ClassLikeAnalyzer::checkPropertyVisibility(
541
                            $property_id,
542
                            $context,
543
                            $statements_analyzer,
544
                            new CodeLocation($statements_analyzer->getSource(), $stmt),
545
                            $statements_analyzer->getSuppressedIssues(),
546
                            false
547
                        ) !== true)
548
                )
549
                && $codebase->methods->methodExists(
550
                    $get_method_id,
551
                    $context->calling_method_id,
552
                    $codebase->collect_locations
553
                        ? new CodeLocation($statements_analyzer->getSource(), $stmt)
554
                        : null,
555
                    !$context->collect_initializations
556
                        && !$context->collect_mutations
557
                        ? $statements_analyzer
558
                        : null,
559
                    $statements_analyzer->getFilePath()
560
                )
561
            ) {
562
                $has_magic_getter = true;
563
564 View Code Duplication
                if (isset($class_storage->pseudo_property_get_types['$' . $prop_name])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
565
                    $stmt_type = clone $class_storage->pseudo_property_get_types['$' . $prop_name];
566
567
                    $statements_analyzer->node_data->setType($stmt, $stmt_type);
568
569
                    self::processTaints(
570
                        $statements_analyzer,
571
                        $stmt,
572
                        $stmt_type,
573
                        $property_id,
574
                        $class_storage,
575
                        $in_assignment
576
                    );
577
                    continue;
578
                }
579
580
                $old_data_provider = $statements_analyzer->node_data;
581
582
                $statements_analyzer->node_data = clone $statements_analyzer->node_data;
583
584
                $fake_method_call = new PhpParser\Node\Expr\MethodCall(
585
                    $stmt->var,
586
                    new PhpParser\Node\Identifier('__get', $stmt->name->getAttributes()),
587
                    [
588
                        new PhpParser\Node\Arg(
589
                            new PhpParser\Node\Scalar\String_(
590
                                $prop_name,
591
                                $stmt->name->getAttributes()
592
                            )
593
                        )
594
                    ]
595
                );
596
597
                $suppressed_issues = $statements_analyzer->getSuppressedIssues();
598
599
                if (!in_array('PossiblyNullReference', $suppressed_issues, true)) {
600
                    $statements_analyzer->addSuppressedIssues(['PossiblyNullReference']);
601
                }
602
603
                if (!in_array('InternalMethod', $suppressed_issues, true)) {
604
                    $statements_analyzer->addSuppressedIssues(['InternalMethod']);
605
                }
606
607
                \Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(
608
                    $statements_analyzer,
609
                    $fake_method_call,
610
                    $context,
611
                    false
612
                );
613
614
                if (!in_array('PossiblyNullReference', $suppressed_issues, true)) {
615
                    $statements_analyzer->removeSuppressedIssues(['PossiblyNullReference']);
616
                }
617
618
                if (!in_array('InternalMethod', $suppressed_issues, true)) {
619
                    $statements_analyzer->removeSuppressedIssues(['InternalMethod']);
620
                }
621
622
                $fake_method_call_type = $statements_analyzer->node_data->getType($fake_method_call);
623
624
                $statements_analyzer->node_data = $old_data_provider;
625
626
                if ($fake_method_call_type) {
627
                    $statements_analyzer->node_data->setType($stmt, $fake_method_call_type);
628
                } else {
629
                    $statements_analyzer->node_data->setType($stmt, Type::getMixed());
630
                }
631
632
                $property_id = $lhs_type_part->value . '::$' . $prop_name;
633
634
                /*
635
                 * If we have an explicit list of all allowed magic properties on the class, and we're
636
                 * not in that list, fall through
637
                 */
638
                if (!$class_storage->sealed_properties && !$override_property_visibility) {
639
                    continue;
640
                }
641
642
                if (!$class_exists) {
643
                    $property_id = $lhs_type_part->value . '::$' . $prop_name;
644
645
                    if (IssueBuffer::accepts(
646
                        new UndefinedMagicPropertyFetch(
647
                            'Magic instance property ' . $property_id . ' is not defined',
648
                            new CodeLocation($statements_analyzer->getSource(), $stmt),
649
                            $property_id
650
                        ),
651
                        $statements_analyzer->getSuppressedIssues()
652
                    )) {
653
                        // fall through
654
                    }
655
656
                    continue;
657
                }
658
            }
659
660 View Code Duplication
            if ($codebase->store_node_types
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
661
                && !$context->collect_initializations
662
                && !$context->collect_mutations
663
            ) {
664
                $codebase->analyzer->addNodeReference(
665
                    $statements_analyzer->getFilePath(),
666
                    $stmt->name,
667
                    $property_id
668
                );
669
            }
670
671
            $config = $statements_analyzer->getProjectAnalyzer()->getConfig();
672
673
            if (!$naive_property_exists) {
674 View Code Duplication
                if ($config->use_phpdoc_property_without_magic_or_parent
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
675
                    && isset($class_storage->pseudo_property_get_types['$' . $prop_name])
676
                ) {
677
                    $stmt_type = clone $class_storage->pseudo_property_get_types['$' . $prop_name];
678
679
                    $statements_analyzer->node_data->setType($stmt, $stmt_type);
680
681
                    self::processTaints(
682
                        $statements_analyzer,
683
                        $stmt,
684
                        $stmt_type,
685
                        $property_id,
686
                        $class_storage,
687
                        $in_assignment
688
                    );
689
                    continue;
690
                }
691
692
                if ($fq_class_name !== $context->self
693
                    && $context->self
694
                    && $codebase->classlikes->classExtends($fq_class_name, $context->self)
695
                    && $codebase->properties->propertyExists(
696
                        $context->self . '::$' . $prop_name,
697
                        true,
698
                        $statements_analyzer,
699
                        $context,
700
                        $codebase->collect_locations
701
                            ? new CodeLocation($statements_analyzer->getSource(), $stmt)
702
                            : null
703
                    )
704
                ) {
705
                    $property_id = $context->self . '::$' . $prop_name;
706
                } else {
707
                    if ($context->inside_isset || $context->collect_initializations) {
708
                        return true;
709
                    }
710
711
                    if ($stmt_var_id === '$this') {
712
                        if (IssueBuffer::accepts(
713
                            new UndefinedThisPropertyFetch(
714
                                'Instance property ' . $property_id . ' is not defined',
715
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
716
                                $property_id
717
                            ),
718
                            $statements_analyzer->getSuppressedIssues()
719
                        )) {
720
                            // fall through
721
                        }
722 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
723
                        if ($has_magic_getter) {
724
                            if (IssueBuffer::accepts(
725
                                new UndefinedMagicPropertyFetch(
726
                                    'Magic instance property ' . $property_id . ' is not defined',
727
                                    new CodeLocation($statements_analyzer->getSource(), $stmt),
728
                                    $property_id
729
                                ),
730
                                $statements_analyzer->getSuppressedIssues()
731
                            )) {
732
                                // fall through
733
                            }
734
                        } else {
735
                            if (IssueBuffer::accepts(
736
                                new UndefinedPropertyFetch(
737
                                    'Instance property ' . $property_id . ' is not defined',
738
                                    new CodeLocation($statements_analyzer->getSource(), $stmt),
739
                                    $property_id
740
                                ),
741
                                $statements_analyzer->getSuppressedIssues()
742
                            )) {
743
                                // fall through
744
                            }
745
                        }
746
                    }
747
748
                    $stmt_type = Type::getMixed();
749
750
                    $statements_analyzer->node_data->setType($stmt, $stmt_type);
751
752
                    if ($var_id) {
753
                        $context->vars_in_scope[$var_id] = $stmt_type;
754
                    }
755
756
                    return true;
757
                }
758
            }
759
760 View Code Duplication
            if (!$override_property_visibility) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
761
                if (ClassLikeAnalyzer::checkPropertyVisibility(
762
                    $property_id,
763
                    $context,
764
                    $statements_analyzer,
765
                    new CodeLocation($statements_analyzer->getSource(), $stmt),
766
                    $statements_analyzer->getSuppressedIssues()
767
                ) === false) {
768
                    return false;
769
                }
770
            }
771
772
            $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
773
                $property_id,
774
                true,
775
                $statements_analyzer
776
            );
777
778
            if ($declaring_property_class === null) {
779
                continue;
780
            }
781
782 View Code Duplication
            if ($codebase->properties_to_rename) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
783
                $declaring_property_id = strtolower($declaring_property_class) . '::$' . $prop_name;
784
785
                foreach ($codebase->properties_to_rename as $original_property_id => $new_property_name) {
786
                    if ($declaring_property_id === $original_property_id) {
787
                        $file_manipulations = [
788
                            new \Psalm\FileManipulation(
789
                                (int) $stmt->name->getAttribute('startFilePos'),
790
                                (int) $stmt->name->getAttribute('endFilePos') + 1,
791
                                $new_property_name
792
                            )
793
                        ];
794
795
                        \Psalm\Internal\FileManipulation\FileManipulationBuffer::add(
796
                            $statements_analyzer->getFilePath(),
797
                            $file_manipulations
798
                        );
799
                    }
800
                }
801
            }
802
803
            $declaring_class_storage = $codebase->classlike_storage_provider->get(
804
                $declaring_property_class
805
            );
806
807
            if (isset($declaring_class_storage->properties[$prop_name])) {
808
                $property_storage = $declaring_class_storage->properties[$prop_name];
809
810
                if ($property_storage->deprecated) {
811
                    if (IssueBuffer::accepts(
812
                        new DeprecatedProperty(
813
                            $property_id . ' is marked deprecated',
814
                            new CodeLocation($statements_analyzer->getSource(), $stmt),
815
                            $property_id
816
                        ),
817
                        $statements_analyzer->getSuppressedIssues()
818
                    )) {
819
                        // fall through
820
                    }
821
                }
822
823 View Code Duplication
                if ($property_storage->psalm_internal && $context->self) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
824
                    if (! NamespaceAnalyzer::isWithin($context->self, $property_storage->psalm_internal)) {
825
                        if (IssueBuffer::accepts(
826
                            new InternalProperty(
827
                                $property_id . ' is marked internal to ' . $property_storage->psalm_internal,
828
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
829
                                $property_id
830
                            ),
831
                            $statements_analyzer->getSuppressedIssues()
832
                        )) {
833
                            // fall through
834
                        }
835
                    }
836
                }
837
838
                if ($property_storage->internal && $context->self) {
839
                    if (! NamespaceAnalyzer::nameSpaceRootsMatch($context->self, $declaring_property_class)) {
840
                        if (IssueBuffer::accepts(
841
                            new InternalProperty(
842
                                $property_id . ' is marked internal',
843
                                new CodeLocation($statements_analyzer->getSource(), $stmt),
844
                                $property_id
845
                            ),
846
                            $statements_analyzer->getSuppressedIssues()
847
                        )) {
848
                            // fall through
849
                        }
850
                    }
851
                }
852
            }
853
854
            $class_property_type = $codebase->properties->getPropertyType(
855
                $property_id,
856
                false,
857
                $statements_analyzer,
858
                $context
859
            );
860
861
            if (!$class_property_type) {
862 View Code Duplication
                if ($declaring_class_storage->location
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
863
                    && $config->isInProjectDirs(
864
                        $declaring_class_storage->location->file_path
865
                    )
866
                ) {
867
                    if (IssueBuffer::accepts(
868
                        new MissingPropertyType(
869
                            'Property ' . $fq_class_name . '::$' . $prop_name
870
                                . ' does not have a declared type',
871
                            new CodeLocation($statements_analyzer->getSource(), $stmt)
872
                        ),
873
                        $statements_analyzer->getSuppressedIssues()
874
                    )) {
875
                        // fall through
876
                    }
877
                }
878
879
                $class_property_type = Type::getMixed();
880
            } else {
881
                $class_property_type = \Psalm\Internal\Type\TypeExpander::expandUnion(
882
                    $codebase,
883
                    clone $class_property_type,
884
                    $declaring_class_storage->name,
885
                    $declaring_class_storage->name,
886
                    $declaring_class_storage->parent_class
887
                );
888
889
                if ($declaring_class_storage->template_types) {
890
                    if (!$lhs_type_part instanceof TGenericObject) {
891
                        $type_params = [];
892
893
                        foreach ($declaring_class_storage->template_types as $type_map) {
894
                            $type_params[] = clone array_values($type_map)[0][0];
895
                        }
896
897
                        $lhs_type_part = new TGenericObject($lhs_type_part->value, $type_params);
898
                    }
899
900
                    $class_property_type = self::localizePropertyType(
901
                        $codebase,
902
                        $class_property_type,
903
                        $lhs_type_part,
904
                        $class_storage,
905
                        $declaring_class_storage
906
                    );
907
                }
908
909
                if ($lhs_type_part instanceof TGenericObject) {
910
                    $class_property_type = self::localizePropertyType(
911
                        $codebase,
912
                        $class_property_type,
913
                        $lhs_type_part,
914
                        $class_storage,
915
                        $declaring_class_storage
916
                    );
917
                }
918
            }
919
920
            self::processTaints(
921
                $statements_analyzer,
922
                $stmt,
923
                $class_property_type,
924
                $property_id,
925
                $class_storage,
926
                $in_assignment
927
            );
928
929 View Code Duplication
            if ($stmt_type = $statements_analyzer->node_data->getType($stmt)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
930
                $statements_analyzer->node_data->setType(
931
                    $stmt,
932
                    Type::combineUnionTypes($class_property_type, $stmt_type)
933
                );
934
            } else {
935
                $statements_analyzer->node_data->setType($stmt, $class_property_type);
936
            }
937
        }
938
939
        $stmt_type = $statements_analyzer->node_data->getType($stmt);
940
941
        if ($stmt_var_type->isNullable() && !$context->inside_isset && $stmt_type) {
942
            $stmt_type->addType(new TNull);
943
944
            if ($stmt_var_type->ignore_nullable_issues) {
945
                $stmt_type->ignore_nullable_issues = true;
946
            }
947
        }
948
949 View Code Duplication
        if ($codebase->store_node_types
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
950
            && !$context->collect_initializations
951
            && !$context->collect_mutations
952
            && ($stmt_type = $statements_analyzer->node_data->getType($stmt))
953
        ) {
954
            $codebase->analyzer->addNodeType(
955
                $statements_analyzer->getFilePath(),
956
                $stmt->name,
957
                $stmt_type->getId()
958
            );
959
        }
960
961
        if ($invalid_fetch_types) {
962
            $lhs_type_part = $invalid_fetch_types[0];
963
964 View Code Duplication
            if ($has_valid_fetch_type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
965
                if (IssueBuffer::accepts(
966
                    new PossiblyInvalidPropertyFetch(
967
                        'Cannot fetch property on possible non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
968
                        new CodeLocation($statements_analyzer->getSource(), $stmt)
969
                    ),
970
                    $statements_analyzer->getSuppressedIssues()
971
                )) {
972
                    // fall through
973
                }
974
            } else {
975
                if (IssueBuffer::accepts(
976
                    new InvalidPropertyFetch(
977
                        'Cannot fetch property on non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
978
                        new CodeLocation($statements_analyzer->getSource(), $stmt)
979
                    ),
980
                    $statements_analyzer->getSuppressedIssues()
981
                )) {
982
                    // fall through
983
                }
984
            }
985
        }
986
987
        if ($var_id) {
988
            $context->vars_in_scope[$var_id] = $statements_analyzer->node_data->getType($stmt) ?: Type::getMixed();
989
        }
990
991
        return true;
992
    }
993
994
    public static function localizePropertyType(
995
        \Psalm\Codebase $codebase,
996
        Type\Union $class_property_type,
997
        TGenericObject $lhs_type_part,
998
        ClassLikeStorage $calling_class_storage,
999
        ClassLikeStorage $declaring_class_storage
1000
    ) : Type\Union {
1001
        $template_types = CallAnalyzer::getTemplateTypesForCall(
1002
            $codebase,
1003
            $declaring_class_storage,
1004
            $declaring_class_storage->name,
1005
            $calling_class_storage,
1006
            $calling_class_storage->template_types ?: []
1007
        );
1008
1009
        $extended_types = $calling_class_storage->template_type_extends;
1010
1011
        if ($template_types) {
1012
            if ($calling_class_storage->template_types) {
1013
                foreach ($lhs_type_part->type_params as $param_offset => $lhs_param_type) {
1014
                    $i = -1;
1015
1016
                    foreach ($calling_class_storage->template_types as $calling_param_name => $_) {
1017
                        $i++;
1018
1019
                        if ($i === $param_offset) {
1020
                            $template_types[$calling_param_name][$calling_class_storage->name] = [
1021
                                $lhs_param_type,
1022
                                0
1023
                            ];
1024
                            break;
1025
                        }
1026
                    }
1027
                }
1028
            }
1029
1030
            foreach ($template_types as $type_name => $_) {
1031
                if (isset($extended_types[$declaring_class_storage->name][$type_name])) {
1032
                    $mapped_type = $extended_types[$declaring_class_storage->name][$type_name];
1033
1034
                    foreach ($mapped_type->getAtomicTypes() as $mapped_type_atomic) {
1035
                        if (!$mapped_type_atomic instanceof Type\Atomic\TTemplateParam) {
1036
                            continue;
1037
                        }
1038
1039
                        $param_name = $mapped_type_atomic->param_name;
1040
1041
                        $position = false;
1042
1043
                        if (isset($calling_class_storage->template_types[$param_name])) {
1044
                            $position = \array_search(
1045
                                $param_name,
1046
                                array_keys($calling_class_storage->template_types)
1047
                            );
1048
                        }
1049
1050
                        if ($position !== false && isset($lhs_type_part->type_params[$position])) {
1051
                            $template_types[$type_name][$declaring_class_storage->name] = [
1052
                                $lhs_type_part->type_params[$position],
1053
                                0
1054
                            ];
1055
                        }
1056
                    }
1057
                }
1058
            }
1059
1060
            $class_property_type->replaceTemplateTypesWithArgTypes(
1061
                new TemplateResult([], $template_types),
1062
                $codebase
1063
            );
1064
        }
1065
1066
        return $class_property_type;
1067
    }
1068
1069
    private static function processTaints(
1070
        StatementsAnalyzer $statements_analyzer,
1071
        PhpParser\Node\Expr\PropertyFetch $stmt,
1072
        Type\Union $type,
1073
        string $property_id,
1074
        \Psalm\Storage\ClassLikeStorage $class_storage,
1075
        bool $in_assignment
1076
    ) : void {
1077
        $codebase = $statements_analyzer->getCodebase();
1078
1079
        if (!$codebase->taint
1080
            || !$codebase->config->trackTaintsInPath($statements_analyzer->getFilePath())
1081
        ) {
1082
            return;
1083
        }
1084
1085
        $var_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var);
1086
        $property_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
1087
1088
        if ($class_storage->specialize_instance) {
1089
            $var_id = ExpressionIdentifier::getArrayVarId(
1090
                $stmt->var,
1091
                null,
1092
                $statements_analyzer
1093
            );
1094
1095
            $var_property_id = ExpressionIdentifier::getArrayVarId(
1096
                $stmt,
1097
                null,
1098
                $statements_analyzer
1099
            );
1100
1101
            if ($var_id) {
1102
                $var_type = $statements_analyzer->node_data->getType($stmt->var);
1103
1104
                if ($var_type && \in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())) {
1105
                    $var_type->parent_nodes = [];
1106
                    return;
1107
                }
1108
1109
                $var_node = TaintNode::getForAssignment(
1110
                    $var_id,
1111
                    $var_location
1112
                );
1113
1114
                $codebase->taint->addTaintNode($var_node);
1115
1116
                $property_node = TaintNode::getForAssignment(
1117
                    $var_property_id ?: $var_id . '->$property',
1118
                    $property_location
1119
                );
1120
1121
                $codebase->taint->addTaintNode($property_node);
1122
1123
                $codebase->taint->addPath(
1124
                    $var_node,
1125
                    $property_node,
1126
                    'property-fetch'
1127
                        . ($stmt->name instanceof PhpParser\Node\Identifier ? '-' . $stmt->name : '')
1128
                );
1129
1130
                if ($var_type && $var_type->parent_nodes) {
1131
                    foreach ($var_type->parent_nodes as $parent_node) {
1132
                        $codebase->taint->addPath(
1133
                            $parent_node,
1134
                            $var_node,
1135
                            '='
1136
                        );
1137
                    }
1138
                }
1139
1140
                $type->parent_nodes = [$property_node];
1141
            }
1142
        } else {
1143
            $code_location = new CodeLocation($statements_analyzer, $stmt->name);
1144
1145
            $localized_property_node = new TaintNode(
1146
                $property_id . '-' . $code_location->file_name . ':' . $code_location->raw_file_start,
1147
                $property_id,
1148
                $code_location,
1149
                null
1150
            );
1151
1152
            $codebase->taint->addTaintNode($localized_property_node);
1153
1154
            $property_node = new TaintNode(
1155
                $property_id,
1156
                $property_id,
1157
                null,
1158
                null
1159
            );
1160
1161
            $codebase->taint->addTaintNode($property_node);
1162
1163
            if ($in_assignment) {
1164
                $codebase->taint->addPath($localized_property_node, $property_node, 'property-assignment');
1165
            } else {
1166
                $codebase->taint->addPath($property_node, $localized_property_node, 'property-fetch');
1167
            }
1168
1169
            $type->parent_nodes[] = $localized_property_node;
1170
        }
1171
    }
1172
}
1173