@param  StatementsSource $source
@param  CodeLocation     $code_location
@param  array<string>    $suppressed_issues
@param  array<string, bool> $phantom_classes
@param  bool             $inferred

@return null|false
334785
public function check(
        StatementsSource $source,
        CodeLocation $code_location,
        array $suppressed_issues,
        array $phantom_classes = [],
        $inferred = true
    ) {
        if ($this->checked) {
            return;
        }

        $all_good = true;

        foreach ($this->types as $atomic_type) {
            if ($atomic_type->check(
                $source,
                $code_location,
                $suppressed_issues,
                $phantom_classes,
                $inferred
            ) === false) {
                $all_good = false;
            }
        }

        if (!$all_good) {
            return false;
        } else {
            $this->checked = true;
        }
    }
287354
public RepositoryResult<Statement> getStatements(Resource subj, URI pred, Value obj, boolean includeInferred) throws RepositoryException {
        try {
            if (isQuadMode()) {
                TupleQuery tupleQuery = prepareTupleQuery(GET_STATEMENTS);
                setBindings(tupleQuery, subj, pred, obj);
                tupleQuery.setIncludeInferred(includeInferred);
                TupleQueryResult qRes = tupleQuery.evaluate();
                return new RepositoryResult<Statement>(
                        new ExceptionConvertingIteration<Statement, RepositoryException>(
                                toStatementIteration(qRes, subj, pred, obj)) {
                            @Override
                            protected RepositoryException convert(Exception e) {
                                return new RepositoryException(e);
                            }
                        });
            } else if (subj != null && pred != null && obj != null) {
                if (hasStatement(subj, pred, obj, includeInferred)) {
                    Statement st = new StatementImpl(subj, pred, obj);
                    CloseableIteration<Statement, RepositoryException> cursor;
                    cursor = new SingletonIteration<Statement, RepositoryException>(st);
                    return new RepositoryResult<Statement>(cursor);
                } else {
                    return new RepositoryResult<Statement>(new EmptyIteration<Statement, RepositoryException>());
                }
            }
            GraphQuery query = prepareGraphQuery(EVERYTHING);
            setBindings(query, subj, pred, obj);
            GraphQueryResult result = query.evaluate();
            return new RepositoryResult<Statement>(
                    new ExceptionConvertingIteration<Statement, RepositoryException>(result) {
                        @Override
                        protected RepositoryException convert(Exception e) {
                            return new RepositoryException(e);
                        }
                    });
        } catch (MalformedQueryException e) {
            throw new RepositoryException(e);
        } catch (QueryEvaluationException e) {
            throw new RepositoryException(e);
        }
    }
744699
public static function analyze(
        StatementsAnalyzer $statements_analyzer,
        PhpParser\Node\Expr $stmt,
        Context $context,
        $array_assignment = false,
        Context $global_context = null
    ) {
        $codebase = $statements_analyzer->getCodebase();

        if ($stmt instanceof PhpParser\Node\Expr\Variable) {
            if (VariableFetchAnalyzer::analyze(
                $statements_analyzer,
                $stmt,
                $context,
                false,
                null,
                $array_assignment
            ) === false
            ) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Assign) {
            $assignment_type = AssignmentAnalyzer::analyze(
                $statements_analyzer,
                $stmt->var,
                $stmt->expr,
                null,
                $context,
                (string)$stmt->getDocComment(),
                $stmt->getLine()
            );

            if ($assignment_type === false) {
                return false;
            }

            $stmt->inferredType = $assignment_type;
        } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp) {
            if (AssignmentAnalyzer::analyzeAssignmentOperation($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\MethodCall) {
            if (MethodCallAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\StaticCall) {
            if (StaticCallAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
            ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context);
        } elseif ($stmt instanceof PhpParser\Node\Scalar\String_) {
            $stmt->inferredType = Type::getString($stmt->value);
        } elseif ($stmt instanceof PhpParser\Node\Scalar\EncapsedStringPart) {
            // do nothing
        } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst) {
            switch (strtolower($stmt->getName())) {
                case '__line__':
                    $stmt->inferredType = Type::getInt();
                    break;

                case '__class__':
                    if (!$context->self) {
                        if (IssueBuffer::accepts(
                            new UndefinedConstant(
                                'Cannot get __class__ outside a class',
                                new CodeLocation($statements_analyzer->getSource(), $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            // fall through
                        }

                        $stmt->inferredType = Type::getClassString();
                        break;
                    }

                    $stmt->inferredType = Type::getLiteralClassString($context->self);
                    break;

                case '__namespace__':
                    $namespace = $statements_analyzer->getNamespace();
                    if ($namespace === null &&
                    IssueBuffer::accepts(
                        new UndefinedConstant(
                            'Cannot get __namespace__ outside a namespace',
                            new CodeLocation($statements_analyzer->getSource(), $stmt)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }

                    $stmt->inferredType = Type::getString($namespace);
                    break;

                case '__file__':
                case '__dir__':
                case '__function__':
                case '__trait__':
                case '__method__':
                    $stmt->inferredType = Type::getString();
                    break;
            }
        } elseif ($stmt instanceof PhpParser\Node\Scalar\LNumber) {
            $stmt->inferredType = Type::getInt(false, $stmt->value);
        } elseif ($stmt instanceof PhpParser\Node\Scalar\DNumber) {
            $stmt->inferredType = Type::getFloat($stmt->value);
        } elseif ($stmt instanceof PhpParser\Node\Expr\UnaryMinus ||
            $stmt instanceof PhpParser\Node\Expr\UnaryPlus
        ) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            if (!isset($stmt->expr->inferredType)) {
                $stmt->inferredType = new Type\Union([new TInt, new TFloat]);
            } elseif ($stmt->expr->inferredType->isMixed()) {
                $stmt->inferredType = Type::getMixed();
            } else {
                $acceptable_types = [];

                foreach ($stmt->expr->inferredType->getTypes() as $type_part) {
                    if ($type_part instanceof TInt || $type_part instanceof TFloat) {
                        if ($type_part instanceof Type\Atomic\TLiteralInt
                            && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
                        ) {
                            $type_part->value = -$type_part->value;
                        } elseif ($type_part instanceof Type\Atomic\TLiteralFloat
                            && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
                        ) {
                            $type_part->value = -$type_part->value;
                        }

                        $acceptable_types[] = $type_part;
                    } elseif ($type_part instanceof TString) {
                        $acceptable_types[] = new TInt;
                        $acceptable_types[] = new TFloat;
                    } else {
                        $acceptable_types[] = new TInt;
                    }
                }

                $stmt->inferredType = new Type\Union($acceptable_types);
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Isset_) {
            self::analyzeIsset($statements_analyzer, $stmt, $context);
            $stmt->inferredType = Type::getBool();
        } elseif ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
            if (ConstFetchAnalyzer::analyzeClassConst($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\PropertyFetch) {
            if (PropertyFetchAnalyzer::analyzeInstance($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\StaticPropertyFetch) {
            if (PropertyFetchAnalyzer::analyzeStatic($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\BitwiseNot) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            if (!isset($stmt->expr->inferredType)) {
                $stmt->inferredType = new Type\Union([new TInt(), new TString()]);
            } elseif ($stmt->expr->inferredType->isMixed()) {
                $stmt->inferredType = Type::getMixed();
            } else {
                $acceptable_types = [];
                $unacceptable_type = null;
                $has_valid_operand = false;

                foreach ($stmt->expr->inferredType->getTypes() as $type_string => $type_part) {
                    if ($type_part instanceof TInt || $type_part instanceof TString) {
                        if ($type_part instanceof Type\Atomic\TLiteralInt) {
                            $type_part->value = ~$type_part->value;
                        } elseif ($type_part instanceof Type\Atomic\TLiteralString) {
                            $type_part->value = ~$type_part->value;
                        }

                        $acceptable_types[] = $type_part;
                        $has_valid_operand = true;
                    } elseif ($type_part instanceof TFloat) {
                        $type_part = ($type_part instanceof Type\Atomic\TLiteralFloat) ?
                            new Type\Atomic\TLiteralInt(~$type_part->value) :
                            new TInt;

                        $stmt->expr->inferredType->removeType($type_string);
                        $stmt->expr->inferredType->addType($type_part);

                        $acceptable_types[] = $type_part;
                        $has_valid_operand = true;
                    } elseif (!$unacceptable_type) {
                        $unacceptable_type = $type_part;
                    }
                }

                if ($unacceptable_type) {
                    $message = 'Cannot negate a non-numeric non-string type ' . $unacceptable_type;
                    if ($has_valid_operand) {
                        if (IssueBuffer::accepts(
                            new PossiblyInvalidOperand(
                                $message,
                                new CodeLocation($statements_analyzer, $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            // fall through
                        }
                    } else {
                        if (IssueBuffer::accepts(
                            new InvalidOperand(
                                $message,
                                new CodeLocation($statements_analyzer, $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            // fall through
                        }
                    }

                    $stmt->inferredType = Type::getMixed();
                } else {
                    $stmt->inferredType = new Type\Union($acceptable_types);
                }
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
            if (BinaryOpAnalyzer::analyze(
                $statements_analyzer,
                $stmt,
                $context
            ) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\PostInc ||
            $stmt instanceof PhpParser\Node\Expr\PostDec ||
            $stmt instanceof PhpParser\Node\Expr\PreInc ||
            $stmt instanceof PhpParser\Node\Expr\PreDec
        ) {
            if (self::analyze($statements_analyzer, $stmt->var, $context) === false) {
                return false;
            }

            if (isset($stmt->var->inferredType)) {
                $return_type = null;

                $fake_right_expr = new PhpParser\Node\Scalar\LNumber(1, $stmt->getAttributes());
                $fake_right_expr->inferredType = Type::getInt();

                BinaryOpAnalyzer::analyzeNonDivArithmeticOp(
                    $statements_analyzer,
                    $stmt->var,
                    $fake_right_expr,
                    $stmt,
                    $return_type,
                    $context
                );

                $stmt->inferredType = clone $stmt->var->inferredType;
                $stmt->inferredType->from_calculation = true;

                foreach ($stmt->inferredType->getTypes() as $atomic_type) {
                    if ($atomic_type instanceof Type\Atomic\TLiteralInt) {
                        $stmt->inferredType->addType(new Type\Atomic\TInt);
                    } elseif ($atomic_type instanceof Type\Atomic\TLiteralFloat) {
                        $stmt->inferredType->addType(new Type\Atomic\TFloat);
                    }
                }

                $var_id = self::getArrayVarId($stmt->var, null);

                if ($var_id && isset($context->vars_in_scope[$var_id])) {
                    $context->vars_in_scope[$var_id] = $stmt->inferredType;

                    if ($context->collect_references && $stmt->var instanceof PhpParser\Node\Expr\Variable) {
                        $location = new CodeLocation($statements_analyzer, $stmt->var);
                        $context->assigned_var_ids[$var_id] = true;
                        $context->possibly_assigned_var_ids[$var_id] = true;
                        $statements_analyzer->registerVariableAssignment(
                            $var_id,
                            $location
                        );
                        $context->unreferenced_vars[$var_id] = [$location->getHash() => $location];
                    }
                }
            } else {
                $stmt->inferredType = Type::getMixed();
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\New_) {
            if (NewAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Array_) {
            if (ArrayAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Scalar\Encapsed) {
            if (self::analyzeEncapsulatedString($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\FuncCall) {
            if (FunctionCallAnalyzer::analyze(
                $statements_analyzer,
                $stmt,
                $context
            ) === false
            ) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Ternary) {
            if (TernaryAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\BooleanNot) {
            if (self::analyzeBooleanNot($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Empty_) {
            self::analyzeEmpty($statements_analyzer, $stmt, $context);
        } elseif ($stmt instanceof PhpParser\Node\Expr\Closure) {
            $closure_analyzer = new ClosureAnalyzer($stmt, $statements_analyzer);

            if (self::analyzeClosureUses($statements_analyzer, $stmt, $context) === false) {
                return false;
            }

            $use_context = new Context($context->self);
            $use_context->collect_references = $codebase->collect_references;

            if (!$statements_analyzer->isStatic()) {
                if ($context->collect_mutations &&
                    $context->self &&
                    $codebase->classExtends(
                        $context->self,
                        (string)$statements_analyzer->getFQCLN()
                    )
                ) {
                    $use_context->vars_in_scope['$this'] = clone $context->vars_in_scope['$this'];
                } elseif ($context->self) {
                    $use_context->vars_in_scope['$this'] = new Type\Union([new TNamedObject($context->self)]);
                }
            }

            foreach ($context->vars_in_scope as $var => $type) {
                if (strpos($var, '$this->') === 0) {
                    $use_context->vars_in_scope[$var] = clone $type;
                }
            }

            foreach ($context->vars_possibly_in_scope as $var => $_) {
                if (strpos($var, '$this->') === 0) {
                    $use_context->vars_possibly_in_scope[$var] = true;
                }
            }

            $byref_uses = [];

            foreach ($stmt->uses as $use) {
                if (!is_string($use->var->name)) {
                    continue;
                }

                $use_var_id = '$' . $use->var->name;

                if ($use->byRef) {
                    $byref_uses[$use_var_id] = true;
                }

                // insert the ref into the current context if passed by ref, as whatever we're passing
                // the closure to could execute it straight away.
                if (!$context->hasVariable($use_var_id, $statements_analyzer) && $use->byRef) {
                    $context->vars_in_scope[$use_var_id] = Type::getMixed();
                }

                $use_context->vars_in_scope[$use_var_id] =
                    $context->hasVariable($use_var_id, $statements_analyzer) && !$use->byRef
                    ? clone $context->vars_in_scope[$use_var_id]
                    : Type::getMixed();

                $use_context->vars_possibly_in_scope[$use_var_id] = true;
            }

            $use_context->calling_method_id = $context->calling_method_id;

            $closure_analyzer->analyze($use_context, $context, false, $byref_uses);

            if (!isset($stmt->inferredType)) {
                $stmt->inferredType = Type::getClosure();
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) {
            if (ArrayFetchAnalyzer::analyze(
                $statements_analyzer,
                $stmt,
                $context
            ) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Int_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            $stmt->inferredType = Type::getInt();
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Double) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            $stmt->inferredType = Type::getFloat();
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Bool_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            $stmt->inferredType = Type::getBool();
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\String_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            if (isset($stmt->expr->inferredType)) {
                self::castStringAttempt($statements_analyzer, $stmt->expr);
            }

            $stmt->inferredType = Type::getString();
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Object_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            $stmt->inferredType = new Type\Union([new TNamedObject('stdClass')]);
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Array_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            $permissible_atomic_types = [];
            $all_permissible = false;

            if (isset($stmt->expr->inferredType)) {
                $all_permissible = true;

                foreach ($stmt->expr->inferredType->getTypes() as $type) {
                    if ($type instanceof Scalar) {
                        $permissible_atomic_types[] = new ObjectLike([new Type\Union([$type])]);
                    } elseif ($type instanceof TNull) {
                        $permissible_atomic_types[] = new TArray([Type::getEmpty(), Type::getEmpty()]);
                    } elseif ($type instanceof TArray) {
                        $permissible_atomic_types[] = clone $type;
                    } elseif ($type instanceof ObjectLike) {
                        $permissible_atomic_types[] = clone $type;
                    } else {
                        $all_permissible = false;
                        break;
                    }
                }
            }

            if ($all_permissible) {
                $stmt->inferredType = TypeCombination::combineTypes($permissible_atomic_types);
            } else {
                $stmt->inferredType = Type::getArray();
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Unset_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            $stmt->inferredType = Type::getNull();
        } elseif ($stmt instanceof PhpParser\Node\Expr\Clone_) {
            self::analyzeClone($statements_analyzer, $stmt, $context);
        } elseif ($stmt instanceof PhpParser\Node\Expr\Instanceof_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }

            if ($stmt->class instanceof PhpParser\Node\Expr) {
                if (self::analyze($statements_analyzer, $stmt->class, $context) === false) {
                    return false;
                }
            } elseif (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
            ) {
                if ($context->check_classes) {
                    $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
                        $stmt->class,
                        $statements_analyzer->getAliases()
                    );

                    if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
                        $statements_analyzer,
                        $fq_class_name,
                        new CodeLocation($statements_analyzer->getSource(), $stmt->class),
                        $statements_analyzer->getSuppressedIssues(),
                        false
                    ) === false) {
                        return false;
                    }

                    if ($codebase->store_node_types
                        && $fq_class_name
                        && $codebase->classOrInterfaceExists($fq_class_name)
                    ) {
                        $codebase->analyzer->addNodeReference(
                            $statements_analyzer->getFilePath(),
                            $stmt->class,
                            $fq_class_name
                        );
                    }
                }
            }

            $stmt->inferredType = Type::getBool();
        } elseif ($stmt instanceof PhpParser\Node\Expr\Exit_) {
            if ($stmt->expr) {
                if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                    return false;
                }
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Include_) {
            IncludeAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context);
        } elseif ($stmt instanceof PhpParser\Node\Expr\Eval_) {
            $context->check_classes = false;
            $context->check_variables = false;

            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\AssignRef) {
            if (AssignmentAnalyzer::analyzeAssignmentRef($statements_analyzer, $stmt, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\ErrorSuppress) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }
            $stmt->inferredType = isset($stmt->expr->inferredType) ? $stmt->expr->inferredType : null;
        } elseif ($stmt instanceof PhpParser\Node\Expr\ShellExec) {
            if (IssueBuffer::accepts(
                new ForbiddenCode(
                    'Use of shell_exec',
                    new CodeLocation($statements_analyzer->getSource(), $stmt)
                ),
                $statements_analyzer->getSuppressedIssues()
            )) {
                // continue
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Print_) {
            if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
                return false;
            }
        } elseif ($stmt instanceof PhpParser\Node\Expr\Yield_) {
            self::analyzeYield($statements_analyzer, $stmt, $context);
        } elseif ($stmt instanceof PhpParser\Node\Expr\YieldFrom) {
            self::analyzeYieldFrom($statements_analyzer, $stmt, $context);
        } elseif ($stmt instanceof PhpParser\Node\Expr\Error) {
            // do nothing
        } else {
            if (IssueBuffer::accepts(
                new UnrecognizedExpression(
                    'Psalm does not understand ' . get_class($stmt),
                    new CodeLocation($statements_analyzer->getSource(), $stmt)
                ),
                $statements_analyzer->getSuppressedIssues()
            )) {
                return false;
            }
        }

        if (!$context->inside_conditional
            && ($stmt instanceof PhpParser\Node\Expr\BinaryOp
                || $stmt instanceof PhpParser\Node\Expr\Instanceof_
                || $stmt instanceof PhpParser\Node\Expr\Assign
                || $stmt instanceof PhpParser\Node\Expr\BooleanNot
                || $stmt instanceof PhpParser\Node\Expr\Empty_
                || $stmt instanceof PhpParser\Node\Expr\Isset_
                || $stmt instanceof PhpParser\Node\Expr\FuncCall)
        ) {
            AssertionFinder::scrapeAssertions(
                $stmt,
                $context->self,
                $statements_analyzer,
                $codebase
            );
        }

        $plugin_classes = $codebase->config->after_expression_checks;

        if ($plugin_classes) {
            $file_manipulations = [];

            foreach ($plugin_classes as $plugin_fq_class_name) {
                if ($plugin_fq_class_name::afterExpressionAnalysis(
                    $stmt,
                    $context,
                    $statements_analyzer->getSource(),
                    $codebase,
                    $file_manipulations
                ) === false) {
                    return false;
                }
            }

            if ($file_manipulations) {
                /** @psalm-suppress MixedTypeCoercion */
                FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
            }
        }

        return null;
    }
1150004
public static function afterStatementAnalysis(
        PhpParser\Node\Stmt $stmt,
        Context $context,
        StatementsSource $statements_source,
        Codebase $codebase,
        array &$file_replacements = []
    ) {
        if ($stmt instanceof PhpParser\Node\Stmt\Echo_) {
            foreach ($stmt->exprs as $expr) {
                if (!isset($expr->inferredType) || $expr->inferredType->hasMixed()) {
                    if (IssueBuffer::accepts(
                        new ArgumentTypeCoercion(
                            'Echo requires an unescaped string, ' . $expr->inferredType . ' provided',
                            new CodeLocation($statements_source, $expr),
                            'echo'
                        ),
                        $statements_source->getSuppressedIssues()
                    )) {
                        // keep soldiering on
                    }

                    continue;
                }

                $types = $expr->inferredType->getTypes();

                foreach ($types as $type) {
                    if ($type instanceof \Psalm\Type\Atomic\TString
                        && !$type instanceof \Psalm\Type\Atomic\TLiteralString
                        && !$type instanceof \Psalm\Type\Atomic\THtmlEscapedString
                    ) {
                        if (IssueBuffer::accepts(
                            new ArgumentTypeCoercion(
                                'Echo requires an unescaped string, ' . $expr->inferredType . ' provided',
                                new CodeLocation($statements_source, $expr),
                                'echo'
                            ),
                            $statements_source->getSuppressedIssues()
                        )) {
                            // keep soldiering on
                        }
                    }
                }
            }
        }
    }
838175
@Override
    public SqlInfo buildSqlInfo(BuildSource source) {
        Object paramObj = source.getParamObj();
        SqlInfo sqlInfo = source.getSqlInfo();
        Node node = source.getNode();

        // 循环判断所有`when`的值，直到能找到第n个when的时候，如果都为false，则跳出循环走else的逻辑
        // 循环过程中，如果判断到第x个when解析后的值为true时，则解析拼接这第x个then的值到Sql中，并直接返回.
        int i = 0;
        while (true) {
            // 每次循环加1，如果是第一个，则，when/then等添加的后缀为空字符串.
            i++;
            String x = i == 1 ? "" : String.valueOf(i);

            // 获取第i个when属性的文本值，如果其文本内容为空则不再拼接，进入else的分支条件来拼接Sql片段信息.
            String whenText = XmlNodeHelper.getNodeAttrText(node, ZealotConst.ATTR_WHEN + x);
            if (StringHelper.isBlank(whenText)) {
                log.info("case标签中第" + i + "个when属性不存在或者内容为空，进入else的分支条件.");
                break;
            }

            // 如果when属性的解析值为true,则拼接其对应的`then`块的SQL片段.then块的值为解析其字符串模板的值.
            if (ParseHelper.isTrue(whenText, paramObj)) {
                String thenText = XmlNodeHelper.getNodeAttrText(node, ZealotConst.ATTR_THEN + x);
                if (StringHelper.isNotBlank(thenText)) {
                    sqlInfo.getJoin().append(ParseHelper.parseTemplate(thenText, paramObj));
                }
                return sqlInfo;
            }
        }

        // 如果没进入前面任何一个when-then的分支块，则进入else的分支，如果else为空，则直接返回原SqlInfo.
        String elseText = XmlNodeHelper.getNodeAttrText(node, ZealotConst.ATTR_ELSE);
        if (StringHelper.isBlank(elseText)) {
            log.info("case标签中else属性不存在或者内容为空，不拼接任何SQL片段.");
            return sqlInfo;
        }

        sqlInfo.getJoin().append(ParseHelper.parseTemplate(elseText, paramObj));
        return sqlInfo;
    }
57317
public static function afterExpressionAnalysis(
        PhpParser\Node\Expr $expr,
        Context $context,
        StatementsSource $statements_source,
        Codebase $codebase,
        array &$file_replacements = []
    ) {
        if ($expr instanceof PhpParser\Node\Expr\Assign
            && isset($expr->inferredType)
            && $expr->inferredType->hasFloat()
        ) {
            if (\Psalm\IssueBuffer::accepts(
                new NoFloatAssignment(
                    'Don’t assign to floats',
                    new CodeLocation($statements_source, $expr)
                ),
                $statements_source->getSuppressedIssues()
            )) {
                // fall through
            }
        }
    }
41098
private void locateCodebasesRequiredForAnalysis(IClassPath classPath, IClassPathBuilderProgress progress)
            throws InterruptedException, IOException, ResourceNotFoundException {
        boolean foundJavaLangObject = false;
        boolean foundFindBugsAnnotations = false;
        boolean foundJSR305Annotations = false;

        for (DiscoveredCodeBase discoveredCodeBase : discoveredCodeBaseList) {
            if (!foundJavaLangObject) {
                foundJavaLangObject = probeCodeBaseForResource(discoveredCodeBase, "java/lang/Object.class");
            }
            if (!foundFindBugsAnnotations) {
                foundFindBugsAnnotations = probeCodeBaseForResource(discoveredCodeBase,
                        "edu/umd/cs/findbugs/annotations/Nonnull.class");
            }
            if (!foundJSR305Annotations) {
                foundJSR305Annotations = probeCodeBaseForResource(discoveredCodeBase, "javax/annotation/meta/TypeQualifier.class");
                if (DEBUG) {
                    System.out.println("foundJSR305Annotations: " + foundJSR305Annotations);
                }
            }
        }

        if (!foundJavaLangObject) {
            processWorkList(classPath, buildSystemCodebaseList(), progress);
        }

        // If we're running findbugs-full.jar, IT contains the contents
        // of jsr305.jar and annotations.jar. So, add it to the classpath.
        if (runningFindBugsFullJar()) {
            processWorkList(classPath, buildFindBugsFullJarCodebaseList(), progress);
            return;
        }

        // Not running findbugs-full.jar: try to find jsr305.jar and
        // annotations.jar.

        if (!foundFindBugsAnnotations) {
            processWorkList(classPath, buildFindBugsAnnotationCodebaseList(), progress);
        }
        if (!foundJSR305Annotations) {
            processWorkList(classPath, buildJSR305AnnotationsCodebaseList(), progress);
        }
    }
622246
public static function isMethodVisible(
        $method_id,
        Context $context,
        StatementsSource $source
    ) {
        $codebase = $source->getCodebase();

        list($fq_classlike_name, $method_name) = explode('::', $method_id);

        if ($codebase->methods->visibility_provider->has($fq_classlike_name)) {
            $method_visible = $codebase->methods->visibility_provider->isMethodVisible(
                $source,
                $fq_classlike_name,
                $method_name,
                $context,
                null
            );

            if ($method_visible !== null) {
                return $method_visible;
            }
        }

        $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);

        if (!$declaring_method_id) {
            // this can happen for methods in the callmap that were not reflected
            return true;
        }

        $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);

        $appearing_method_class = null;

        if ($appearing_method_id) {
            list($appearing_method_class) = explode('::', $appearing_method_id);

            // if the calling class is the same, we know the method exists, so it must be visible
            if ($appearing_method_class === $context->self) {
                return true;
            }
        }

        list($declaring_method_class) = explode('::', $declaring_method_id);

        if ($source->getSource() instanceof TraitAnalyzer && $declaring_method_class === $source->getFQCLN()) {
            return true;
        }

        $storage = $codebase->methods->getStorage($declaring_method_id);

        switch ($storage->visibility) {
            case ClassLikeAnalyzer::VISIBILITY_PUBLIC:
                return true;

            case ClassLikeAnalyzer::VISIBILITY_PRIVATE:
                return $context->self && $appearing_method_class === $context->self;

            case ClassLikeAnalyzer::VISIBILITY_PROTECTED:
                if (!$context->self) {
                    return false;
                }

                if ($appearing_method_class
                    && $codebase->classExtends($appearing_method_class, $context->self)
                ) {
                    return true;
                }

                if ($appearing_method_class
                    && !$codebase->classExtends($context->self, $appearing_method_class)
                ) {
                    return false;
                }
        }

        return true;
    }
187103
protected static function analyzeYield(
        StatementsAnalyzer $statements_analyzer,
        PhpParser\Node\Expr\Yield_ $stmt,
        Context $context
    ) {
        $doc_comment_text = (string)$stmt->getDocComment();

        $var_comments = [];
        $var_comment_type = null;

        $codebase = $statements_analyzer->getCodebase();

        if ($doc_comment_text) {
            try {
                $var_comments = CommentAnalyzer::getTypeFromComment(
                    $doc_comment_text,
                    $statements_analyzer,
                    $statements_analyzer->getAliases()
                );
            } catch (DocblockParseException $e) {
                if (IssueBuffer::accepts(
                    new InvalidDocblock(
                        (string)$e->getMessage(),
                        new CodeLocation($statements_analyzer->getSource(), $stmt)
                    )
                )) {
                    // fall through
                }
            }

            foreach ($var_comments as $var_comment) {
                $comment_type = ExpressionAnalyzer::fleshOutType(
                    $codebase,
                    $var_comment->type,
                    $context->self,
                    $context->self ? new Type\Atomic\TNamedObject($context->self) : null
                );

                if (!$var_comment->var_id) {
                    $var_comment_type = $comment_type;
                    continue;
                }

                $context->vars_in_scope[$var_comment->var_id] = $comment_type;
            }
        }

        if ($stmt->key) {
            if (self::analyze($statements_analyzer, $stmt->key, $context) === false) {
                return false;
            }
        }

        if ($stmt->value) {
            if (self::analyze($statements_analyzer, $stmt->value, $context) === false) {
                return false;
            }

            if ($var_comment_type) {
                $stmt->inferredType = $var_comment_type;
            } elseif (isset($stmt->value->inferredType)) {
                $stmt->inferredType = $stmt->value->inferredType;
            } else {
                $stmt->inferredType = Type::getMixed();
            }
        } else {
            $stmt->inferredType = Type::getNull();
        }

        $source = $statements_analyzer->getSource();

        if ($source instanceof FunctionLikeAnalyzer
            && !($source->getSource() instanceof TraitAnalyzer)
        ) {
            $source->examineParamTypes($statements_analyzer, $context, $codebase, $stmt);

            $storage = $source->getFunctionLikeStorage($statements_analyzer);

            if ($storage->return_type) {
                foreach ($storage->return_type->getTypes() as $atomic_return_type) {
                    if ($atomic_return_type instanceof Type\Atomic\TGenericObject
                        && $atomic_return_type->value === 'Generator'
                    ) {
                        if (!$atomic_return_type->type_params[2]->isMixed()
                            && !$atomic_return_type->type_params[2]->isVoid()
                        ) {
                            $stmt->inferredType = clone $atomic_return_type->type_params[2];
                        }
                    }
                }
            }
        }

        return null;
    }
394244
public function findAllByNotifiable($identifier, $class, $seen = null, $order = 'DESC', $limit = null,
                                        $offset = null)
    {
        $qb = $this->findAllByNotifiableQb($identifier, $class, $order);

        if ($seen !== null) {
            $whereSeen = $seen ? 1 : 0;
            $qb
                ->andWhere('nn.seen = :seen')
                ->setParameter('seen', $whereSeen)
            ;
        }

        if (null !== $limit) {
            $qb->setMaxResults($limit);
        }
        if (null !== $offset) {
            $qb->setFirstResult($offset);
        }

        return $qb->getQuery()->getResult();
    }
179696
public static function afterFunctionCallAnalysis(
        FuncCall $expr,
        string $function_id,
        Context $context,
        StatementsSource $statements_source,
        Codebase $codebase,
        array &$file_replacements = [],
        Union &$return_type_candidate = null
    ) {
        if ($expr->name instanceof PhpParser\Node\Expr) {
            return;
        }

        try {
            $function_storage = $codebase->functions->getStorage(
                $statements_source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer
                    ? $statements_source
                    : null,
                $function_id
            );

            $function_name_parts = explode('\\', $function_storage->cased_name);

            if (end($function_name_parts) !== end($expr->name->parts)) {
                if (\Psalm\IssueBuffer::accepts(
                    new IncorrectFunctionCasing(
                        'Function is incorrectly cased, expecting ' . $function_storage->cased_name,
                        new CodeLocation($statements_source, $expr->name)
                    ),
                    $statements_source->getSuppressedIssues()
                )) {
                    // fall through
                }
            }
        } catch (\Exception $e) {
            // can throw if storage is missing
        }
    }
1359556
public static function analyzeStatic(
        StatementsAnalyzer $statements_analyzer,
        PhpParser\Node\Expr\StaticPropertyFetch $stmt,
        Context $context
    ) {
        if ($stmt->class instanceof PhpParser\Node\Expr\Variable ||
            $stmt->class instanceof PhpParser\Node\Expr\ArrayDimFetch
        ) {
            // @todo check this
            return null;
        }

        $fq_class_name = null;

        $codebase = $statements_analyzer->getCodebase();

        if ($stmt->class instanceof PhpParser\Node\Name) {
            if (count($stmt->class->parts) === 1
                && in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
            ) {
                if ($stmt->class->parts[0] === 'parent') {
                    $fq_class_name = $statements_analyzer->getParentFQCLN();

                    if ($fq_class_name === null) {
                        if (IssueBuffer::accepts(
                            new ParentNotFound(
                                'Cannot check property fetch on parent as this class does not extend another',
                                new CodeLocation($statements_analyzer->getSource(), $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            return false;
                        }

                        return;
                    }
                } else {
                    $fq_class_name = (string)$context->self;
                }

                if ($context->isPhantomClass($fq_class_name)) {
                    return null;
                }
            } else {
                $aliases = $statements_analyzer->getAliases();

                if ($context->calling_method_id
                    && !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
                ) {
                    $codebase->file_reference_provider->addMethodReferenceToClassMember(
                        $context->calling_method_id,
                        'use:' . $stmt->class->parts[0] . ':' . \md5($statements_analyzer->getFilePath())
                    );
                }

                $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
                    $stmt->class,
                    $aliases
                );

                if ($context->isPhantomClass($fq_class_name)) {
                    return null;
                }

                if ($context->check_classes) {
                    if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
                        $statements_analyzer,
                        $fq_class_name,
                        new CodeLocation($statements_analyzer->getSource(), $stmt->class),
                        $statements_analyzer->getSuppressedIssues(),
                        false
                    ) !== true) {
                        return false;
                    }
                }
            }

            $stmt->class->inferredType = $fq_class_name ? new Type\Union([new TNamedObject($fq_class_name)]) : null;
        }

        if ($stmt->name instanceof PhpParser\Node\VarLikeIdentifier) {
            $prop_name = $stmt->name->name;
        } elseif (isset($stmt->name->inferredType)
            && $stmt->name->inferredType->isSingleStringLiteral()
        ) {
            $prop_name = $stmt->name->inferredType->getSingleStringLiteral()->value;
        } else {
            $prop_name = null;
        }

        if (!$prop_name) {
            if ($fq_class_name) {
                $codebase->analyzer->addMixedMemberName(
                    strtolower($fq_class_name) . '::$',
                    $context->calling_method_id ?: $statements_analyzer->getFileName()
                );
            }

            return null;
        }

        if (!$fq_class_name
            || !$context->check_classes
            || !$context->check_variables
            || ExpressionAnalyzer::isMock($fq_class_name)
        ) {
            return null;
        }

        $var_id = ExpressionAnalyzer::getVarId(
            $stmt,
            $context->self ?: $statements_analyzer->getFQCLN(),
            $statements_analyzer
        );

        $property_id = $fq_class_name . '::$' . $prop_name;

        if ($codebase->store_node_types) {
            $codebase->analyzer->addNodeReference(
                $statements_analyzer->getFilePath(),
                $stmt->name,
                $property_id
            );
        }

        if ($var_id && $context->hasVariable($var_id, $statements_analyzer)) {
            // we don't need to check anything
            $stmt->inferredType = $context->vars_in_scope[$var_id];

            if ($context->collect_references) {
                // log the appearance
                $codebase->properties->propertyExists(
                    $property_id,
                    false,
                    $statements_analyzer,
                    $context,
                    new CodeLocation($statements_analyzer->getSource(), $stmt)
                );
            }

            if ($codebase->store_node_types
                && (!$context->collect_initializations
                    && !$context->collect_mutations)
                && isset($stmt->inferredType)
            ) {
                $codebase->analyzer->addNodeType(
                    $statements_analyzer->getFilePath(),
                    $stmt->name,
                    (string) $stmt->inferredType
                );
            }

            return null;
        }

        if (!$codebase->properties->propertyExists(
            $property_id,
            false,
            $statements_analyzer,
            $context,
            $context->collect_references ? new CodeLocation($statements_analyzer->getSource(), $stmt) : null
        )
        ) {
            if (IssueBuffer::accepts(
                new UndefinedPropertyFetch(
                    'Static property ' . $property_id . ' is not defined',
                    new CodeLocation($statements_analyzer->getSource(), $stmt),
                    $property_id
                ),
                $statements_analyzer->getSuppressedIssues()
            )) {
                // fall through
            }

            return;
        }

        if (ClassLikeAnalyzer::checkPropertyVisibility(
            $property_id,
            $context,
            $statements_analyzer,
            new CodeLocation($statements_analyzer->getSource(), $stmt),
            $statements_analyzer->getSuppressedIssues()
        ) === false) {
            return false;
        }

        $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
            $fq_class_name . '::$' . $prop_name,
            true
        );

        $class_storage = $codebase->classlike_storage_provider->get((string)$declaring_property_class);
        $property = $class_storage->properties[$prop_name];

        if ($var_id) {
            if ($property->type) {
                $context->vars_in_scope[$var_id] = ExpressionAnalyzer::fleshOutType(
                    $codebase,
                    clone $property->type,
                    $declaring_property_class,
                    $declaring_property_class
                );
            } else {
                $context->vars_in_scope[$var_id] = Type::getMixed();
            }

            $stmt->inferredType = clone $context->vars_in_scope[$var_id];

            if ($codebase->store_node_types
                && (!$context->collect_initializations
                    && !$context->collect_mutations)
            ) {
                $codebase->analyzer->addNodeType(
                    $statements_analyzer->getFilePath(),
                    $stmt->name,
                    (string) $stmt->inferredType
                );
            }
        } else {
            $stmt->inferredType = Type::getMixed();
        }

        return null;
    }
723393
private function checkForMissingPropertyType(
        StatementsSource $source,
        PhpParser\Node\Stmt\Property $stmt,
        Context $context
    ) {
        $comment = $stmt->getDocComment();

        if (!$comment || !$comment->getText()) {
            $fq_class_name = $source->getFQCLN();
            $property_name = $stmt->props[0]->name->name;

            $codebase = $this->getCodebase();

            $property_id = $fq_class_name . '::$' . $property_name;

            $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
                $property_id,
                true
            );

            if (!$declaring_property_class) {
                throw new \UnexpectedValueException(
                    'Cannot get declaring class for ' . $property_id
                );
            }

            $fq_class_name = $declaring_property_class;

            // gets inherited property type
            $class_property_type = $codebase->properties->getPropertyType($property_id, false, $source, $context);

            if ($class_property_type) {
                return;
            }

            $message = 'Property ' . $property_id . ' does not have a declared type';

            $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);

            $property_storage = $class_storage->properties[$property_name];

            if ($property_storage->suggested_type && !$property_storage->suggested_type->isNull()) {
                $message .= ' - consider ' . str_replace(
                    ['<mixed, mixed>', '<empty, empty>'],
                    '',
                    (string)$property_storage->suggested_type
                );
            }

            if (IssueBuffer::accepts(
                new MissingPropertyType(
                    $message,
                    new CodeLocation($source, $stmt->props[0]->name)
                ),
                $this->source->getSuppressedIssues()
            )) {
                // fall through
            }
        }
    }
804614
private static function triggerIssueForImpossible(
        Union $existing_var_type,
        $old_var_type_string,
        $key,
        $new_var_type,
        $redundant,
        CodeLocation $code_location,
        array $suppressed_issues
    ) {
        $reconciliation = ' and trying to reconcile type \'' . $old_var_type_string . '\' to ' . $new_var_type;

        $existing_var_atomic_types = $existing_var_type->getTypes();

        $from_docblock = $existing_var_type->from_docblock
            || (isset($existing_var_atomic_types[$new_var_type])
                && $existing_var_atomic_types[$new_var_type]->from_docblock);

        if ($redundant) {
            if ($from_docblock) {
                if (IssueBuffer::accepts(
                    new RedundantConditionGivenDocblockType(
                        'Found a redundant condition when evaluating docblock-defined type '
                            . $key . $reconciliation,
                        $code_location
                    ),
                    $suppressed_issues
                )) {
                    // fall through
                }
            } else {
                if (IssueBuffer::accepts(
                    new RedundantCondition(
                        'Found a redundant condition when evaluating ' . $key . $reconciliation,
                        $code_location
                    ),
                    $suppressed_issues
                )) {
                    // fall through
                }
            }
        } else {
            if ($from_docblock) {
                if (IssueBuffer::accepts(
                    new DocblockTypeContradiction(
                        'Found a contradiction with a docblock-defined type '
                            . 'when evaluating ' . $key . $reconciliation,
                        $code_location
                    ),
                    $suppressed_issues
                )) {
                    // fall through
                }
            } else {
                if (IssueBuffer::accepts(
                    new TypeDoesNotContainType(
                        'Found a contradiction when evaluating ' . $key . $reconciliation,
                        $code_location
                    ),
                    $suppressed_issues
                )) {
                    // fall through
                }
            }
        }
    }
84412
public function findAllBySource($source, array $arrOptions = [])
    {
        /** @var CfgTagModel $adapter */
        if (null === ($adapter = $this->framework->getAdapter(self::class))) {
            return null;
        }

        return $adapter->findBy('source', $source, $arrOptions);
    }
1173361
public static function analyze(
        StatementsAnalyzer $statements_analyzer,
        PhpParser\Node\Expr\New_ $stmt,
        Context $context
    ) {
        $fq_class_name = null;

        $codebase = $statements_analyzer->getCodebase();
        $config = $codebase->config;

        $can_extend = false;

        if ($stmt->class instanceof PhpParser\Node\Name) {
            if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
                $aliases = $statements_analyzer->getAliases();

                if ($context->calling_method_id
                    && !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
                ) {
                    $codebase->file_reference_provider->addMethodReferenceToClassMember(
                        $context->calling_method_id,
                        'use:' . $stmt->class->parts[0] . ':' . \md5($statements_analyzer->getFilePath())
                    );
                }

                $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
                    $stmt->class,
                    $aliases
                );

                $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
            } else {
                switch ($stmt->class->parts[0]) {
                    case 'self':
                        $fq_class_name = $context->self;
                        break;

                    case 'parent':
                        $fq_class_name = $context->parent;
                        break;

                    case 'static':
                        // @todo maybe we can do better here
                        $fq_class_name = $context->self;
                        $can_extend = true;
                        break;
                }
            }

            if ($codebase->store_node_types && $fq_class_name) {
                $codebase->analyzer->addNodeReference(
                    $statements_analyzer->getFilePath(),
                    $stmt->class,
                    $fq_class_name
                );
            }
        } elseif ($stmt->class instanceof PhpParser\Node\Stmt\Class_) {
            $statements_analyzer->analyze([$stmt->class], $context);
            $fq_class_name = ClassAnalyzer::getAnonymousClassName($stmt->class, $statements_analyzer->getFilePath());
        } else {
            ExpressionAnalyzer::analyze($statements_analyzer, $stmt->class, $context);

            if (isset($stmt->class->inferredType)) {
                $has_single_class = $stmt->class->inferredType->isSingleStringLiteral();

                if ($has_single_class) {
                    $fq_class_name = $stmt->class->inferredType->getSingleStringLiteral()->value;
                } else {
                    $generic_params = null;

                    if (self::checkMethodArgs(
                        null,
                        $stmt->args,
                        $generic_params,
                        $context,
                        new CodeLocation($statements_analyzer->getSource(), $stmt),
                        $statements_analyzer
                    ) === false) {
                        return false;
                    }
                }

                $new_type = null;

                foreach ($stmt->class->inferredType->getTypes() as $lhs_type_part) {
                    if ($lhs_type_part instanceof Type\Atomic\TTemplateParamClass) {
                        if (!isset($stmt->inferredType)) {
                            $new_type_part = new Type\Atomic\TTemplateParam(
                                $lhs_type_part->param_name,
                                $lhs_type_part->as_type
                                    ? new Type\Union([$lhs_type_part->as_type])
                                    : Type::parseString($lhs_type_part->as)
                            );

                            if ($new_type) {
                                $new_type = Type::combineUnionTypes(
                                    $new_type,
                                    new Type\Union([$new_type_part])
                                );
                            } else {
                                $new_type = new Type\Union([$new_type_part]);
                            }
                        }

                        continue;
                    }

                    if ($lhs_type_part instanceof Type\Atomic\TLiteralClassString
                        || $lhs_type_part instanceof Type\Atomic\TClassString
                    ) {
                        if (!isset($stmt->inferredType)) {
                            $class_name = $lhs_type_part instanceof Type\Atomic\TClassString
                                ? $lhs_type_part->as
                                : $lhs_type_part->value;

                            if ($lhs_type_part instanceof Type\Atomic\TClassString) {
                                $can_extend = true;
                            }

                            if ($class_name === 'object') {
                                if (IssueBuffer::accepts(
                                    new MixedMethodCall(
                                        'Cannot call constructor on an unknown class',
                                        new CodeLocation($statements_analyzer->getSource(), $stmt)
                                    ),
                                    $statements_analyzer->getSuppressedIssues()
                                )) {
                                    // fall through
                                }
                            }

                            if ($new_type) {
                                $new_type = Type::combineUnionTypes(
                                    $new_type,
                                    Type::parseString($class_name)
                                );
                            } else {
                                $new_type = Type::parseString($class_name);
                            }
                        }

                        continue;
                    }

                    if ($lhs_type_part instanceof Type\Atomic\TString) {
                        if ($config->allow_string_standin_for_class
                            && !$lhs_type_part instanceof Type\Atomic\TNumericString
                        ) {
                            // do nothing
                        } elseif (IssueBuffer::accepts(
                            new InvalidStringClass(
                                'String cannot be used as a class',
                                new CodeLocation($statements_analyzer->getSource(), $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            // fall through
                        }
                    } elseif ($lhs_type_part instanceof Type\Atomic\TMixed
                        || $lhs_type_part instanceof Type\Atomic\TTemplateParam
                    ) {
                        if (IssueBuffer::accepts(
                            new MixedMethodCall(
                                'Cannot call constructor on an unknown class',
                                new CodeLocation($statements_analyzer->getSource(), $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            // fall through
                        }
                    } elseif ($lhs_type_part instanceof Type\Atomic\TFalse
                        && $stmt->class->inferredType->ignore_falsable_issues
                    ) {
                        // do nothing
                    } elseif ($lhs_type_part instanceof Type\Atomic\TNull
                        && $stmt->class->inferredType->ignore_nullable_issues
                    ) {
                        // do nothing
                    } elseif (IssueBuffer::accepts(
                        new UndefinedClass(
                            'Type ' . $lhs_type_part . ' cannot be called as a class',
                            new CodeLocation($statements_analyzer->getSource(), $stmt),
                            (string)$lhs_type_part
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }

                    if ($new_type) {
                        $new_type = Type::combineUnionTypes(
                            $new_type,
                            Type::getObject()
                        );
                    } else {
                        $new_type = Type::getObject();
                    }
                }

                if (!$has_single_class) {
                    if ($new_type) {
                        $stmt->inferredType = $new_type;
                    }

                    return null;
                }
            } else {
                return null;
            }
        }

        if ($fq_class_name) {
            if ($context->check_classes) {
                if ($context->isPhantomClass($fq_class_name)) {
                    return null;
                }

                if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
                    $statements_analyzer,
                    $fq_class_name,
                    new CodeLocation($statements_analyzer->getSource(), $stmt->class),
                    $statements_analyzer->getSuppressedIssues(),
                    false
                ) === false) {
                    return false;
                }

                if ($codebase->interfaceExists($fq_class_name)) {
                    if (IssueBuffer::accepts(
                        new InterfaceInstantiation(
                            'Interface ' . $fq_class_name . ' cannot be instantiated',
                            new CodeLocation($statements_analyzer->getSource(), $stmt->class)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        return false;
                    }

                    return null;
                }
            }

            $stmt->inferredType = new Type\Union([new TNamedObject($fq_class_name)]);

            if (strtolower($fq_class_name) !== 'stdclass' &&
                $context->check_classes &&
                $codebase->classlikes->classExists($fq_class_name)
            ) {
                $storage = $codebase->classlike_storage_provider->get($fq_class_name);

                // if we're not calling this constructor via new static()
                if ($storage->abstract && !$can_extend) {
                    if (IssueBuffer::accepts(
                        new AbstractInstantiation(
                            'Unable to instantiate a abstract class ' . $fq_class_name,
                            new CodeLocation($statements_analyzer->getSource(), $stmt)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        return false;
                    }
                }

                if ($storage->deprecated) {
                    if (IssueBuffer::accepts(
                        new DeprecatedClass(
                            $fq_class_name . ' is marked deprecated',
                            new CodeLocation($statements_analyzer->getSource(), $stmt)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }

                if ($storage->internal && $context->self) {
                    $self_root = preg_replace('/^([^\\\]+).*/', '$1', $context->self);
                    $declaring_root = preg_replace('/^([^\\\]+).*/', '$1', $fq_class_name);

                    if (strtolower($self_root) !== strtolower($declaring_root)) {
                        if (IssueBuffer::accepts(
                            new InternalClass(
                                $fq_class_name . ' is marked internal',
                                new CodeLocation($statements_analyzer->getSource(), $stmt)
                            ),
                            $statements_analyzer->getSuppressedIssues()
                        )) {
                            // fall through
                        }
                    }
                }

                if ($codebase->methods->methodExists(
                    $fq_class_name . '::__construct',
                    $context->calling_method_id,
                    $context->collect_references ? new CodeLocation($statements_analyzer->getSource(), $stmt) : null,
                    null,
                    $statements_analyzer->getFilePath()
                )) {
                    $method_id = $fq_class_name . '::__construct';

                    if (self::checkMethodArgs(
                        $method_id,
                        $stmt->args,
                        $found_generic_params,
                        $context,
                        new CodeLocation($statements_analyzer->getSource(), $stmt),
                        $statements_analyzer
                    ) === false) {
                        return false;
                    }

                    if (MethodAnalyzer::checkMethodVisibility(
                        $method_id,
                        $context,
                        $statements_analyzer->getSource(),
                        new CodeLocation($statements_analyzer->getSource(), $stmt),
                        $statements_analyzer->getSuppressedIssues()
                    ) === false) {
                        return false;
                    }

                    $generic_param_types = null;

                    if ($storage->template_types) {
                        $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);

                        $declaring_fq_class_name = $declaring_method_id
                            ? explode('::', $declaring_method_id)[0]
                            : $fq_class_name;

                        foreach ($storage->template_types as $template_name => $base_type) {
                            if (isset($found_generic_params[$template_name][$fq_class_name])) {
                                $generic_param_types[] = $found_generic_params[$template_name][$fq_class_name][0];
                            } elseif ($storage->template_type_extends && $found_generic_params) {
                                $generic_param_types[] = self::getGenericParamForOffset(
                                    $declaring_fq_class_name,
                                    $template_name,
                                    $storage->template_type_extends,
                                    $found_generic_params
                                );
                            } else {
                                $generic_param_types[] = array_values($base_type)[0][0];
                            }
                        }
                    }

                    if ($generic_param_types) {
                        $stmt->inferredType = new Type\Union([
                            new Type\Atomic\TGenericObject(
                                $fq_class_name,
                                $generic_param_types
                            ),
                        ]);
                    }
                } elseif ($stmt->args) {
                    if (IssueBuffer::accepts(
                        new TooManyArguments(
                            'Class ' . $fq_class_name . ' has no __construct, but arguments were passed',
                            new CodeLocation($statements_analyzer->getSource(), $stmt),
                            $fq_class_name . '::__construct'
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }
            }
        }

        if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
            $context->removeAllObjectVars();
        }

        return null;
    }
234753
public static function printMainSources(Fact $fact, int $level): void
    {
        $factrec = $fact->gedcom();
        $parent  = $fact->record();
        $tree    = $fact->record()->tree();

        $nlevel = $level + 1;
        if ($fact->isPendingAddition()) {
            $styleadd = 'wt-new';
            $can_edit = $level === 1 && $fact->canEdit();
        } elseif ($fact->isPendingDeletion()) {
            $styleadd = 'wt-old';
            $can_edit = false;
        } else {
            $styleadd = '';
            $can_edit = $level === 1 && $fact->canEdit();
        }

        // -- find source for each fact
        $ct    = preg_match_all("/($level SOUR (.+))/", $factrec, $match, PREG_SET_ORDER);
        $spos2 = 0;
        for ($j = 0; $j < $ct; $j++) {
            $sid   = trim($match[$j][2], '@');
            $spos1 = strpos($factrec, $match[$j][1], $spos2);
            $spos2 = strpos($factrec, "\n$level", $spos1);
            if (!$spos2) {
                $spos2 = strlen($factrec);
            }
            $srec   = substr($factrec, $spos1, $spos2 - $spos1);
            $source = Source::getInstance($sid, $tree);
            // Allow access to "1 SOUR @non_existent_source@", so it can be corrected/deleted
            if (!$source || $source->canShow()) {
                if ($level > 1) {
                    echo '<tr class="wt-level-two-source collapse">';
                } else {
                    echo '<tr>';
                }
                echo '<th class="';
                if ($level > 1) {
                    echo ' rela';
                }
                echo ' ', $styleadd, '">';
                $factlines = explode("\n", $factrec); // 1 BIRT Y\n2 SOUR ...
                $factwords = explode(' ', $factlines[0]); // 1 BIRT Y
                $factname  = $factwords[1]; // BIRT
                if ($factname === 'EVEN' || $factname === 'FACT') {
                    // Add ' EVEN' to provide sensible output for an event with an empty TYPE record
                    $ct = preg_match('/2 TYPE (.*)/', $factrec, $ematch);
                    if ($ct > 0) {
                        $factname = trim($ematch[1]);
                        echo $factname;
                    } else {
                        echo GedcomTag::getLabel($factname, $parent);
                    }
                } elseif ($can_edit) {
                    echo '<a href="' . e(route('edit-fact', [
                            'xref'    => $parent->xref(),
                            'fact_id' => $fact->id(),
                            'ged'     => $tree->name(),
                        ])) . '" title="', I18N::translate('Edit'), '">';
                    if ($tree->getPreference('SHOW_FACT_ICONS') && $level === 1) {
                        echo '<i class="icon-source"></i> ';
                    }
                    echo GedcomTag::getLabel($factname, $parent), '</a>';
                    echo '<div class="editfacts">';
                    if (preg_match('/^@.+@$/', $match[$j][2])) {
                        // Inline sources can't be edited. Attempting to save one will convert it
                        // into a link, and delete it.
                        // e.g. "1 SOUR my source" becomes "1 SOUR @my source@" which does not exist.
                        echo view('edit/icon-fact-edit', ['fact' => $fact]);
                        echo view('edit/icon-fact-copy', ['fact' => $fact]);
                    }
                    echo view('edit/icon-fact-delete', ['fact' => $fact]);
                } else {
                    echo GedcomTag::getLabel($factname, $parent);
                }
                echo '</th>';
                echo '<td class="', $styleadd, '">';
                if ($source) {
                    echo '<a href="', e($source->url()), '">', $source->fullName(), '</a>';
                    // PUBL
                    $publ = $source->facts(['PUBL'])->first();
                    if ($publ) {
                        echo GedcomTag::getLabelValue('PUBL', $publ->value());
                    }
                    // 2 RESN tags. Note, there can be more than one, such as "privacy" and "locked"
                    if (preg_match_all("/\n2 RESN (.+)/", $factrec, $rmatches)) {
                        foreach ($rmatches[1] as $rmatch) {
                            echo '<br><span class="label">', GedcomTag::getLabel('RESN'), ':</span> <span class="field">';
                            switch ($rmatch) {
                                case 'none':
                                    // Note: "2 RESN none" is not valid gedcom, and the GUI will not let you add it.
                                    // However, webtrees privacy rules will interpret it as "show an otherwise private fact to public".
                                    echo '<i class="icon-resn-none"></i> ', I18N::translate('Show to visitors');
                                    break;
                                case 'privacy':
                                    echo '<i class="icon-resn-privacy"></i> ', I18N::translate('Show to members');
                                    break;
                                case 'confidential':
                                    echo '<i class="icon-resn-confidential"></i> ', I18N::translate('Show to managers');
                                    break;
                                case 'locked':
                                    echo '<i class="icon-resn-locked"></i> ', I18N::translate('Only managers can edit');
                                    break;
                                default:
                                    echo $rmatch;
                                    break;
                            }
                            echo '</span>';
                        }
                    }
                    $cs = preg_match("/$nlevel EVEN (.*)/", $srec, $cmatch);
                    if ($cs > 0) {
                        echo '<br><span class="label">', GedcomTag::getLabel('EVEN'), ' </span><span class="field">', $cmatch[1], '</span>';
                        $cs = preg_match('/' . ($nlevel + 1) . ' ROLE (.*)/', $srec, $cmatch);
                        if ($cs > 0) {
                            echo '<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="label">', GedcomTag::getLabel('ROLE'), ' </span><span class="field">', $cmatch[1], '</span>';
                        }
                    }
                    echo self::printSourceStructure($tree, self::getSourceStructure($srec));
                    echo '<div class="indent">';
                    self::printMediaLinks($tree, $srec, $nlevel);
                    if ($nlevel === 2) {
                        self::printMediaLinks($tree, $source->gedcom(), 1);
                    }
                    echo FunctionsPrint::printFactNotes($tree, $srec, $nlevel);
                    if ($nlevel === 2) {
                        echo FunctionsPrint::printFactNotes($tree, $source->gedcom(), 1);
                    }
                    echo '</div>';
                } else {
                    echo $sid;
                }
                echo '</td></tr>';
            }
        }
    }