UNPKG

5.79 kBJavaScriptView Raw
1"use strict";
2
3const assert = require("assert");
4
5const { getPrecedence, shouldFlatten, isBitwiseOperator } = require("./util");
6
7function needsParens(path) {
8 const parent = path.getParentNode();
9
10 if (!parent) {
11 return false;
12 }
13
14 const name = path.getName();
15 const node = path.getNode();
16
17 if (
18 [
19 // No need parens for top level children of this nodes
20 "program",
21 "expressionstatement",
22 "namespace",
23 "declare",
24 "block",
25
26 // No need parens
27 "include",
28 "print",
29 "return",
30 "echo",
31 ].includes(parent.kind)
32 ) {
33 return false;
34 }
35
36 switch (node.kind) {
37 case "pre":
38 case "post":
39 if (parent.kind === "unary") {
40 return (
41 node.kind === "pre" &&
42 ((node.type === "+" && parent.type === "+") ||
43 (node.type === "-" && parent.type === "-"))
44 );
45 }
46 // else fallthrough
47 case "unary":
48 switch (parent.kind) {
49 case "unary":
50 return (
51 node.type === parent.type &&
52 (node.type === "+" || node.type === "-")
53 );
54 case "propertylookup":
55 case "staticlookup":
56 case "offsetlookup":
57 case "call":
58 return name === "what" && parent.what === node;
59 case "bin":
60 return parent.type === "**" && name === "left";
61 default:
62 return false;
63 }
64 case "bin": {
65 switch (parent.kind) {
66 case "assign":
67 case "retif":
68 return ["and", "xor", "or"].includes(node.type);
69 case "silent":
70 case "cast":
71 // TODO: bug https://github.com/glayzzle/php-parser/issues/172
72 return node.parenthesizedExpression;
73 case "pre":
74 case "post":
75 case "unary":
76 return true;
77 case "call":
78 case "propertylookup":
79 case "staticlookup":
80 case "offsetlookup":
81 return name === "what" && parent.what === node;
82 case "bin": {
83 const po = parent.type;
84 const pp = getPrecedence(po);
85 const no = node.type;
86 const np = getPrecedence(no);
87
88 if (pp > np) {
89 return true;
90 }
91
92 if (po === "||" && no === "&&") {
93 return true;
94 }
95
96 if (pp === np && name === "right") {
97 assert.strictEqual(parent.right, node);
98
99 return true;
100 }
101
102 if (pp === np && !shouldFlatten(po, no)) {
103 return true;
104 }
105
106 if (pp < np && no === "%") {
107 return po === "+" || po === "-";
108 }
109
110 // Add parenthesis when working with bitwise operators
111 // It's not stricly needed but helps with code understanding
112 if (isBitwiseOperator(po)) {
113 return true;
114 }
115
116 return false;
117 }
118
119 default:
120 return false;
121 }
122 }
123 case "propertylookup":
124 case "staticlookup": {
125 switch (parent.kind) {
126 case "call":
127 return (
128 name === "what" &&
129 parent.what === node &&
130 node.parenthesizedExpression
131 );
132
133 default:
134 return false;
135 }
136 }
137 case "clone":
138 case "new": {
139 switch (parent.kind) {
140 case "propertylookup":
141 case "staticlookup":
142 case "offsetlookup":
143 case "call":
144 return name === "what" && parent.what === node;
145 default:
146 return false;
147 }
148 }
149 case "yield": {
150 switch (parent.kind) {
151 case "propertylookup":
152 case "staticlookup":
153 case "offsetlookup":
154 case "call":
155 return name === "what" && parent.what === node;
156
157 case "retif":
158 return parent.test === node;
159
160 default:
161 return !!(node.key || node.value);
162 }
163 }
164 case "assign": {
165 if (
166 parent.kind === "for" &&
167 (parent.init.includes(node) || parent.increment.includes(node))
168 ) {
169 return false;
170 } else if (parent.kind === "assign") {
171 return false;
172 } else if (parent.kind === "static") {
173 return false;
174 } else if (
175 ["if", "do", "while", "foreach", "switch"].includes(parent.kind)
176 ) {
177 return false;
178 } else if (parent.kind === "silent") {
179 return false;
180 } else if (parent.kind === "call") {
181 return false;
182 }
183
184 return true;
185 }
186 case "retif":
187 switch (parent.kind) {
188 case "cast":
189 return true;
190 case "unary":
191 case "bin":
192 case "retif":
193 if (name === "test" && !parent.trueExpr) {
194 return false;
195 }
196
197 return true;
198 case "propertylookup":
199 case "staticlookup":
200 case "offsetlookup":
201 case "call":
202 return name === "what" && parent.what === node;
203
204 default:
205 return false;
206 }
207 case "closure":
208 return parent.kind === "call" && name === "what" && parent.what === node;
209 case "silence":
210 case "cast":
211 // TODO: bug https://github.com/glayzzle/php-parser/issues/172
212 return node.parenthesizedExpression;
213 // else fallthrough
214 case "string":
215 case "array":
216 switch (parent.kind) {
217 case "propertylookup":
218 case "staticlookup":
219 case "offsetlookup":
220 case "call":
221 if (
222 ["string", "array"].includes(node.kind) &&
223 parent.kind === "offsetlookup"
224 ) {
225 return false;
226 }
227
228 return name === "what" && parent.what === node;
229 default:
230 return false;
231 }
232 case "print":
233 case "include":
234 return parent.kind === "bin";
235 }
236
237 return false;
238}
239
240module.exports = needsParens;