Yaffs site version 1.1
[yaffs-website] / vendor / psy / psysh / src / Psy / CodeCleaner / ImplicitReturnPass.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2017 Justin Hileman
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Psy\CodeCleaner;
13
14 use PhpParser\Node\Expr;
15 use PhpParser\Node\Expr\Exit_;
16 use PhpParser\Node\Expr\New_;
17 use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
18 use PhpParser\Node\Stmt;
19 use PhpParser\Node\Stmt\Break_;
20 use PhpParser\Node\Stmt\If_;
21 use PhpParser\Node\Stmt\Namespace_;
22 use PhpParser\Node\Stmt\Return_;
23 use PhpParser\Node\Stmt\Switch_;
24
25 /**
26  * Add an implicit "return" to the last statement, provided it can be returned.
27  */
28 class ImplicitReturnPass extends CodeCleanerPass
29 {
30     /**
31      * @param array $nodes
32      *
33      * @return array
34      */
35     public function beforeTraverse(array $nodes)
36     {
37         return $this->addImplicitReturn($nodes);
38     }
39
40     /**
41      * @param array $nodes
42      *
43      * @return array
44      */
45     private function addImplicitReturn(array $nodes)
46     {
47         // If nodes is empty, it can't have a return value.
48         if (empty($nodes)) {
49             return array(new Return_(new New_(new FullyQualifiedName('Psy\CodeCleaner\NoReturnValue'))));
50         }
51
52         $last = end($nodes);
53
54         // Special case a few types of statements to add an implicit return
55         // value (even though they technically don't have any return value)
56         // because showing a return value in these instances is useful and not
57         // very surprising.
58         if ($last instanceof If_) {
59             $last->stmts = $this->addImplicitReturn($last->stmts);
60
61             foreach ($last->elseifs as $elseif) {
62                 $elseif->stmts = $this->addImplicitReturn($elseif->stmts);
63             }
64
65             if ($last->else) {
66                 $last->else->stmts = $this->addImplicitReturn($last->else->stmts);
67             }
68         } elseif ($last instanceof Switch_) {
69             foreach ($last->cases as $case) {
70                 // only add an implicit return to cases which end in break
71                 $caseLast = end($case->stmts);
72                 if ($caseLast instanceof Break_) {
73                     $case->stmts = $this->addImplicitReturn(array_slice($case->stmts, 0, -1));
74                     $case->stmts[] = $caseLast;
75                 }
76             }
77         } elseif ($last instanceof Expr && !($last instanceof Exit_)) {
78             $nodes[count($nodes) - 1] = new Return_($last, array(
79                 'startLine' => $last->getLine(),
80                 'endLine'   => $last->getLine(),
81             ));
82         } elseif ($last instanceof Namespace_) {
83             $last->stmts = $this->addImplicitReturn($last->stmts);
84         }
85
86         // Return a "no return value" for all non-expression statements, so that
87         // PsySH can suppress the `null` that `eval()` returns otherwise.
88         //
89         // Note that statements special cased above (if/elseif/else, switch)
90         // _might_ implicitly return a value before this catch-all return is
91         // reached.
92         //
93         // We're not adding a fallback return after namespace statements,
94         // because code outside namespace statements doesn't really work, and
95         // there's already an implicit return in the namespace statement anyway.
96         if ($last instanceof Stmt && !$last instanceof Return_ && !$last instanceof Namespace_) {
97             $nodes[] = new Return_(new New_(new FullyQualifiedName('Psy\CodeCleaner\NoReturnValue')));
98         }
99
100         return $nodes;
101     }
102 }