1 | "use strict";
|
2 |
|
3 | const assert = require("assert");
|
4 |
|
5 | const { getPrecedence, shouldFlatten, isBitwiseOperator } = require("./util");
|
6 |
|
7 | function 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 |
|
20 | "program",
|
21 | "expressionstatement",
|
22 | "namespace",
|
23 | "declare",
|
24 | "block",
|
25 |
|
26 |
|
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 |
|
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 |
|
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 |
|
111 |
|
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 |
|
212 | return node.parenthesizedExpression;
|
213 |
|
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 |
|
240 | module.exports = needsParens;
|