33
44use PhpParser \ParserFactory ;
55
6- /**
7- * @suppress PhanTypeMismatchProperty https://github.com/etsy/phan/issues/609
8- * @suppress PhanUndeclaredProperty - docComment really exists.
9- * NOTE: this may be removed in the future.
10- *
11- * Phan was used while developing this. The asserts can be cleaned up in the future.
12- */
13- function astnode (int $ kind , int $ flags , ?array $ children , int $ lineno , ?string $ docComment = null ) : \ast \Node {
14- $ node = new \ast \Node ();
15- $ node ->kind = $ kind ;
16- $ node ->flags = $ flags ;
17- $ node ->lineno = $ lineno ;
18- $ node ->children = $ children ;
19- if (is_string ($ docComment )) {
20- $ node ->docComment = $ docComment ;
21- }
22- return $ node ;
23- }
24-
25- function sl ($ node ) : ?int {
26- if ($ node instanceof \PhpParser \Node) {
27- return $ node ->getAttribute ('startLine ' );
28- }
29- return null ;
30- }
31-
32- function el ($ node ) : ?int {
33- if ($ node instanceof \PhpParser \Node) {
34- return $ node ->getAttribute ('endLine ' );
35- }
36- return null ;
37- }
38-
396class ASTConverter {
407 // The latest stable version of php-ast.
418 // For something > 40, update the library's release.
429 // For something < 40, there are no releases.
4310 const AST_VERSION = 40 ;
4411
45- public static function ast_parse_code_fallback (string $ source , int $ version ) {
12+ private static $ should_add_placeholders = false ;
13+
14+ public static function set_should_add_placeholders (bool $ value ) : void {
15+ self ::$ should_add_placeholders = $ value ;
16+ }
17+
18+ public static function ast_parse_code_fallback (string $ source , int $ version , bool $ suppressErrors = false , array &$ errors = null ) {
4619 if ($ version !== self ::AST_VERSION ) {
4720 throw new \InvalidArgumentException (sprintf ("Unexpected version: want %d, got %d " , self ::AST_VERSION , $ version ));
4821 }
4922 // Aside: this can be implemented as a stub.
50- $ parserNode = self ::phpparser_parse ($ source );
23+ $ parserNode = self ::phpparser_parse ($ source, $ suppressErrors , $ errors );
5124 return self ::phpparser_to_phpast ($ parserNode , $ version );
5225 }
5326
54- public static function phpparser_parse (string $ source ) {
27+ public static function phpparser_parse (string $ source, bool $ suppressErrors = false , array & $ errors = null ) {
5528 $ parser = (new ParserFactory ())->create (ParserFactory::ONLY_PHP7 );
29+ $ errorHandler = $ suppressErrors ? new \PhpParser \ErrorHandler \Collecting () : null ;
5630 // $nodeDumper = new PhpParser\NodeDumper();
57- return $ parser ->parse ($ source );
31+ $ result = $ parser ->parse ($ source , $ errorHandler );
32+ if ($ suppressErrors ) {
33+ $ errors = $ errorHandler ->getErrors ();
34+ }
35+ return $ result ;
5836 }
5937
6038
@@ -84,7 +62,7 @@ private static function _phpparser_stmtlist_to_ast_node(array $parserNodes, ?int
8462 foreach ($ childNode as $ childNodePart ) {
8563 $ children [] = $ childNodePart ;
8664 }
87- } else {
65+ } else if (! is_null ( $ childNode )) {
8866 $ children [] = $ childNode ;
8967 }
9068 }
@@ -126,8 +104,6 @@ private static function _phpparser_node_to_ast_node($n) {
126104 self ::_phpparser_node_to_ast_node ($ n ->expr ),
127105 $ startLine
128106 );
129- case 'PhpParser\Node\Expr\AssignOp\ShiftLeft ' :
130- return self ::_ast_node_assignop (\ast \flags \BINARY_SHIFT_LEFT , $ n , $ startLine );
131107 case 'PhpParser\Node\Expr\AssignOp\BitwiseAnd ' :
132108 return self ::_ast_node_assignop (\ast \flags \BINARY_BITWISE_AND , $ n , $ startLine );
133109 case 'PhpParser\Node\Expr\AssignOp\BitwiseOr ' :
@@ -152,41 +128,41 @@ private static function _phpparser_node_to_ast_node($n) {
152128 return self ::_ast_node_assignop (\ast \flags \BINARY_SHIFT_LEFT , $ n , $ startLine );
153129 case 'PhpParser\Node\Expr\AssignOp\ShiftRight ' :
154130 return self ::_ast_node_assignop (\ast \flags \BINARY_SHIFT_RIGHT , $ n , $ startLine );
131+ case 'PhpParser\Node\Expr\BinaryOp\BitwiseAnd ' :
132+ return self ::_ast_node_binaryop (\ast \flags \BINARY_BITWISE_AND , $ n , $ startLine );
133+ case 'PhpParser\Node\Expr\BinaryOp\BitwiseOr ' :
134+ return self ::_ast_node_binaryop (\ast \flags \BINARY_BITWISE_OR , $ n , $ startLine );
135+ case 'PhpParser\Node\Expr\BinaryOp\BitwiseXor ' :
136+ return self ::_ast_node_binaryop (\ast \flags \BINARY_BITWISE_XOR , $ n , $ startLine );
137+ case 'PhpParser\Node\Expr\BinaryOp\Concat ' :
138+ return self ::_ast_node_binaryop (\ast \flags \BINARY_CONCAT , $ n , $ startLine );
155139 case 'PhpParser\Node\Expr\BinaryOp\Coalesce ' :
156- return astnode (
157- \ast \AST_BINARY_OP ,
158- \ast \flags \BINARY_COALESCE ,
159- self ::_phpparser_nodes_to_left_right_children ($ n ->left , $ n ->right ),
160- $ startLine
161- );
140+ return self ::_ast_node_binaryop (\ast \flags \BINARY_COALESCE , $ n , $ startLine );
141+ case 'PhpParser\Node\Expr\BinaryOp\Div ' :
142+ return self ::_ast_node_binaryop (\ast \flags \BINARY_DIV , $ n , $ startLine );
162143 case 'PhpParser\Node\Expr\BinaryOp\Greater ' :
163- return astnode (
164- \ast \AST_BINARY_OP ,
165- \ast \flags \BINARY_IS_GREATER ,
166- self ::_phpparser_nodes_to_left_right_children ($ n ->left , $ n ->right ),
167- $ startLine
168- );
144+ return self ::_ast_node_binaryop (\ast \flags \BINARY_IS_GREATER , $ n , $ startLine );
169145 case 'PhpParser\Node\Expr\BinaryOp\GreaterOrEqual ' :
170- return astnode (
171- \ast \AST_BINARY_OP ,
172- \ast \flags \BINARY_IS_GREATER_OR_EQUAL ,
173- self ::_phpparser_nodes_to_left_right_children ($ n ->left , $ n ->right ),
174- $ startLine
175- );
146+ return self ::_ast_node_binaryop (\ast \flags \BINARY_IS_GREATER_OR_EQUAL , $ n , $ startLine );
176147 case 'PhpParser\Node\Expr\BinaryOp\LogicalAnd ' :
177- return astnode (
178- \ast \AST_BINARY_OP ,
179- \ast \flags \BINARY_BOOL_AND ,
180- self ::_phpparser_nodes_to_left_right_children ($ n ->left , $ n ->right ),
181- $ startLine
182- );
148+ return self ::_ast_node_binaryop (\ast \flags \BINARY_BOOL_AND , $ n , $ startLine );
183149 case 'PhpParser\Node\Expr\BinaryOp\LogicalOr ' :
184- return astnode (
185- \ast \AST_BINARY_OP ,
186- \ast \flags \BINARY_BOOL_OR ,
187- self ::_phpparser_nodes_to_left_right_children ($ n ->left , $ n ->right ),
188- $ startLine
189- );
150+ return self ::_ast_node_binaryop (\ast \flags \BINARY_BOOL_OR , $ n , $ startLine );
151+ // FIXME: rest of binary operations.
152+ case 'PhpParser\Node\Expr\BinaryOp\Mod ' :
153+ return self ::_ast_node_binaryop (\ast \flags \BINARY_MOD , $ n , $ startLine );
154+ case 'PhpParser\Node\Expr\BinaryOp\Mul ' :
155+ return self ::_ast_node_binaryop (\ast \flags \BINARY_MUL , $ n , $ startLine );
156+ case 'PhpParser\Node\Expr\BinaryOp\Minus ' :
157+ return self ::_ast_node_binaryop (\ast \flags \BINARY_SUB , $ n , $ startLine );
158+ case 'PhpParser\Node\Expr\BinaryOp\Plus ' :
159+ return self ::_ast_node_binaryop (\ast \flags \BINARY_ADD , $ n , $ startLine );
160+ case 'PhpParser\Node\Expr\BinaryOp\Pow ' :
161+ return self ::_ast_node_binaryop (\ast \flags \BINARY_POW , $ n , $ startLine );
162+ case 'PhpParser\Node\Expr\BinaryOp\ShiftLeft ' :
163+ return self ::_ast_node_binaryop (\ast \flags \BINARY_SHIFT_LEFT , $ n , $ startLine );
164+ case 'PhpParser\Node\Expr\BinaryOp\ShiftRight ' :
165+ return self ::_ast_node_binaryop (\ast \flags \BINARY_SHIFT_RIGHT , $ n , $ startLine );
190166 case 'PhpParser\Node\Expr\Closure ' :
191167 // TODO: is there a corresponding flag for $n->static? $n->byRef?
192168 return self ::_ast_decl_closure (
@@ -200,6 +176,9 @@ private static function _phpparser_node_to_ast_node($n) {
200176 $ n ->getAttribute ('endLine ' ),
201177 self ::_extract_phpdoc_comment ($ n ->getAttribute ('comments ' ))
202178 );
179+ // FIXME: add a test of ClassConstFetch to php-ast
180+ case 'PhpParser\Node\Expr\ClassConstFetch ' :
181+ return self ::_phpparser_classconstfetch_to_ast_classconstfetch ($ n , $ startLine );
203182 case 'PhpParser\Node\Expr\ConstFetch ' :
204183 return astnode (\ast \AST_CONST , 0 , ['name ' => self ::_phpparser_node_to_ast_node ($ n ->name )], $ startLine );
205184 case 'PhpParser\Node\Expr\ErrorSuppress ' :
@@ -209,6 +188,9 @@ private static function _phpparser_node_to_ast_node($n) {
209188 self ::_phpparser_node_to_ast_node ($ n ->expr ),
210189 $ startLine
211190 );
191+ case 'PhpParser\Node\Expr\Error ' :
192+ // TODO: handle this.
193+ return null ;
212194 case 'PhpParser\Node\Expr\FuncCall ' :
213195 return self ::_ast_node_call (
214196 self ::_phpparser_node_to_ast_node ($ n ->name ),
@@ -234,11 +216,7 @@ private static function _phpparser_node_to_ast_node($n) {
234216 'args ' => self ::_phpparser_arg_list_to_ast_arg_list ($ n ->args , $ startLine ),
235217 ], $ startLine );
236218 case 'PhpParser\Node\Expr\PropertyFetch ' :
237- $ name = $ n ->name ;
238- return astnode (\ast \AST_PROP , 0 , [
239- 'expr ' => self ::_phpparser_node_to_ast_node ($ n ->var ),
240- 'prop ' => is_object ($ name ) ? self ::_phpparser_node_to_ast_node ($ name ) : $ name ,
241- ], $ startLine );
219+ return self ::_phpparser_propertyfetch_to_ast_prop ($ n , $ startLine );
242220 case 'PhpParser\Node\Expr\UnaryMinus ' :
243221 return self ::_ast_node_unary_op (\ast \flags \UNARY_MINUS , self ::_phpparser_node_to_ast_node ($ n ->expr ), $ startLine );
244222 case 'PhpParser\Node\Expr\UnaryPlus ' :
@@ -476,7 +454,14 @@ private static function _ast_node_while($cond, $stmts, int $startLine) : \ast\No
476454 return $ node ;
477455 }
478456
479- private static function _ast_node_assign ($ var , $ expr , int $ line ) : \ast \Node {
457+ private static function _ast_node_assign ($ var , $ expr , int $ line ) : ?\ast \Node {
458+ if ($ expr === null ) {
459+ if (self ::$ should_add_placeholders ) {
460+ $ expr = '__INCOMPLETE_EXPR__ ' ;
461+ } else {
462+ return null ;
463+ }
464+ }
480465 $ node = new \ast \Node ();
481466 $ node ->kind = \ast \AST_ASSIGN ;
482467 $ node ->flags = 0 ;
@@ -600,9 +585,17 @@ private static function _ast_node_name_fullyqualified(string $name, int $line) :
600585 return astnode (\ast \AST_NAME , \ast \flags \NAME_FQ , ['name ' => $ name ], $ line );
601586 }
602587
603- private static function _ast_node_variable ($ expr , int $ line ) : \ast \Node {
588+ private static function _ast_node_variable ($ expr , int $ line ) : ?\ast \Node {
589+ // TODO: 2 different ways to handle an Error. 1. Add a placeholder. 2. remove all of the statements in that tree.
604590 if ($ expr instanceof \PhpParser \Node) {
605591 $ expr = self ::_phpparser_node_to_ast_node ($ expr );
592+ if ($ expr === null ) {
593+ if (self ::$ should_add_placeholders ) {
594+ $ expr = '__INCOMPLETE_VARIABLE__ ' ;
595+ } else {
596+ return null ;
597+ }
598+ }
606599 }
607600 $ node = new \ast \Node ;
608601 $ node ->kind = \ast \AST_VAR ;
@@ -897,6 +890,18 @@ private static function _ast_node_assignop(int $flags, \PhpParser\Node $node, in
897890 );
898891 }
899892
893+ /**
894+ * @suppress PhanUndeclaredProperty
895+ */
896+ private static function _ast_node_binaryop (int $ flags , \PhpParser \Node $ n , int $ startLine ) {
897+ return astnode (
898+ \ast \AST_BINARY_OP ,
899+ $ flags ,
900+ self ::_phpparser_nodes_to_left_right_children ($ n ->left , $ n ->right ),
901+ $ startLine
902+ );
903+ }
904+
900905 private static function _phpparser_nodes_to_left_right_children ($ left , $ right ) : array {
901906 return [
902907 'left ' => self ::_phpparser_node_to_ast_node ($ left ),
@@ -1046,4 +1051,75 @@ private static function _phpparser_array_to_ast_array(\PhpParser\Node $n, int $s
10461051 }
10471052 return astnode (\ast \AST_ARRAY , \ast \flags \ARRAY_SYNTAX_SHORT , $ astItems , $ startLine );
10481053 }
1054+
1055+ private static function _phpparser_propertyfetch_to_ast_prop (\PhpParser \Node $ n , int $ startLine ) : ?\ast \Node {
1056+ assert ($ n instanceof \PhpParser \Node \Expr \PropertyFetch);
1057+ $ name = $ n ->name ;
1058+ if (is_object ($ name )) {
1059+ $ name = self ::_phpparser_node_to_ast_node ($ name );
1060+ }
1061+ if ($ name === null ) {
1062+ if (self ::$ should_add_placeholders ) {
1063+ $ name = '__INCOMPLETE_PROPERTY__ ' ;
1064+ } else {
1065+ return null ;
1066+ }
1067+ }
1068+ return astnode (\ast \AST_PROP , 0 , [
1069+ 'expr ' => self ::_phpparser_node_to_ast_node ($ n ->var ),
1070+ 'prop ' => is_object ($ name ) ? : $ name ,
1071+ ], $ startLine );
1072+ }
1073+
1074+ private static function _phpparser_classconstfetch_to_ast_classconstfetch (\PhpParser \Node $ n , int $ startLine ) : ?\ast \Node {
1075+ assert ($ n instanceof \PhpParser \Node \Expr \ClassConstFetch);
1076+ $ name = $ n ->name ;
1077+ if (is_object ($ name )) {
1078+ $ name = self ::_phpparser_node_to_ast_node ($ name );
1079+ }
1080+ if ($ name === null ) {
1081+ if (self ::$ should_add_placeholders ) {
1082+ $ name = '__INCOMPLETE_CLASS_CONST__ ' ;
1083+ } else {
1084+ return null ;
1085+ }
1086+ }
1087+ return astnode (\ast \AST_CLASS_CONST , 0 , [
1088+ 'class ' => self ::_phpparser_node_to_ast_node ($ n ->class ),
1089+ 'const ' => $ name ,
1090+ ], $ startLine );
1091+ }
1092+ }
1093+
1094+ /**
1095+ * @suppress PhanTypeMismatchProperty https://github.com/etsy/phan/issues/609
1096+ * @suppress PhanUndeclaredProperty - docComment really exists.
1097+ * NOTE: this may be removed in the future.
1098+ *
1099+ * Phan was used while developing this. The asserts can be cleaned up in the future.
1100+ */
1101+ function astnode (int $ kind , int $ flags , ?array $ children , int $ lineno , ?string $ docComment = null ) : \ast \Node {
1102+ $ node = new \ast \Node ();
1103+ $ node ->kind = $ kind ;
1104+ $ node ->flags = $ flags ;
1105+ $ node ->lineno = $ lineno ;
1106+ $ node ->children = $ children ;
1107+ if (is_string ($ docComment )) {
1108+ $ node ->docComment = $ docComment ;
1109+ }
1110+ return $ node ;
1111+ }
1112+
1113+ function sl ($ node ) : ?int {
1114+ if ($ node instanceof \PhpParser \Node) {
1115+ return $ node ->getAttribute ('startLine ' );
1116+ }
1117+ return null ;
1118+ }
1119+
1120+ function el ($ node ) : ?int {
1121+ if ($ node instanceof \PhpParser \Node) {
1122+ return $ node ->getAttribute ('endLine ' );
1123+ }
1124+ return null ;
10491125}
0 commit comments