1. 23// #!/usr/bin/env node
2. 23// JSLint
3. 23
4. 23// The Unlicense
5. 23//
6. 23// This is free and unencumbered software released into the public domain.
7. 23//
8. 23// Anyone is free to copy, modify, publish, use, compile, sell, or
9. 23// distribute this software, either in source code form or as a compiled
10. 23// binary, for any purpose, commercial or non-commercial, and by any
11. 23// means.
12. 23//
13. 23// In jurisdictions that recognize copyright laws, the author or authors
14. 23// of this software dedicate any and all copyright interest in the
15. 23// software to the public domain. We make this dedication for the benefit
16. 23// of the public at large and to the detriment of our heirs and
17. 23// successors. We intend this dedication to be an overt act of
18. 23// relinquishment in perpetuity of all present and future rights to this
19. 23// software under copyright law.
20. 23//
21. 23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22. 23// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23. 23// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24. 23// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25. 23// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26. 23// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27. 23// OTHER DEALINGS IN THE SOFTWARE.
28. 23
29. 23
30. 23// jslint(source, option_dict, global_list) is a function that takes 3
31. 23// arguments. The second two arguments are optional.
32. 23
33. 23// source A text to analyze.
34. 23// option_dict An object whose keys correspond to option names.
35. 23// global_list An array of strings containing global variables that
36. 23// the file is allowed readonly access.
37. 23
38. 23// jslint returns an object containing its results. The object contains a lot
39. 23// of valuable information. It can be used to generate reports. The object
40. 23// contains:
41. 23
42. 23// directives: an array of directive comment tokens.
43. 23// edition: the version of JSLint that did the analysis.
44. 23// exports: the names exported from the module.
45. 23// froms: an array of strings representing each of the imports.
46. 23// functions: an array of objects that represent all functions
47. 23// declared in the file.
48. 23// global: an object representing the global object. Its .context property
49. 23// is an object containing a property for each global variable.
50. 23// id: "(JSLint)"
51. 23// json: true if the file is a JSON text.
52. 23// lines: an array of strings, the source.
53. 23// module: true if an import or export statement was used.
54. 23// ok: true if no warnings were generated. This is what you want.
55. 23// option: the option argument.
56. 23// property: a property object.
57. 23// stop: true if JSLint was unable to finish. You don't want this.
58. 23// tokens: an array of objects representing the tokens in the file.
59. 23// tree: the token objects arranged in a tree.
60. 23// warnings: an array of warning objects. A warning object can contain:
61. 23// name: "JSLintError"
62. 23// column: A column number in the file.
63. 23// line: A line number in the file.
64. 23// code: A warning code string.
65. 23// message: The warning message string.
66. 23// a: Exhibit A.
67. 23// b: Exhibit B.
68. 23// c: Exhibit C.
69. 23// d: Exhibit D.
70. 23
71. 23// jslint works in several phases. In any of these phases, errors might be
72. 23// found. Sometimes JSLint is able to recover from an error and continue
73. 23// parsing. In some cases, it cannot and will stop early. If that should happen,
74. 23// repair your code and try again.
75. 23
76. 23// Phases:
77. 23
78. 23// PHASE 1. Split <source> by newlines into <line_list>.
79. 23// PHASE 2. Lex <line_list> into <token_list>.
80. 23// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
81. 23// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
82. 23// recursive traversal. Each node may be processed on the way down
83. 23// (preaction) and on the way up (postaction).
84. 23// PHASE 5. Check whitespace between tokens in <token_list>.
85. 23
86. 23// jslint can also examine JSON text. It decides that a file is JSON text if
87. 23// the first token is "[" or "{". Processing of JSON text is much simpler than
88. 23// the processing of JavaScript programs. Only the first three phases are
89. 23// required.
90. 23
91. 23// WARNING: JSLint will hurt your feelings.
92. 23
93. 23/*jslint beta, node, trace*/
94. 23/*property
95. 23 JSLINT_BETA,
96. 23 NODE_V8_COVERAGE,
97. 23 alive,
98. 23 all,
99. 23 argv,
100. 23 arity,
101. 23 artifact,
102. 23 assertErrorThrownAsync,
103. 23 assertJsonEqual,
104. 23 assertOrThrow,
105. 23 assign,
106. 23 assigned,
107. 23 assignment,
108. 23 async,
109. 23 at,
110. 23 autofix,
111. 23 autofixed,
112. 23 beta,
113. 23 bitwise,
114. 23 block,
115. 23 block_list,
116. 23 block_stack,
117. 23 browser,
118. 23 catch,
119. 23 char,
120. 23 children,
121. 23 clear,
122. 23 closer,
123. 23 closure,
124. 23 code,
125. 23 column,
126. 23 concat,
127. 23 consoleError,
128. 23 console_error,
129. 23 console_log,
130. 23 constant,
131. 23 context,
132. 23 context_report,
133. 23 convert,
134. 23 count,
135. 23 coverageDir,
136. 23 create,
137. 23 cwd,
138. 23 debugInline,
139. 23 default,
140. 23 delta,
141. 23 describe,
142. 23 description,
143. 23 devel,
144. 23 directive,
145. 23 directive_ignore_line,
146. 23 directive_list,
147. 23 directives,
148. 23 dirname,
149. 23 disrupt,
150. 23 dot,
151. 23 dot_depth,
152. 23 edition,
153. 23 elem_list,
154. 23 ellipsis,
155. 23 else,
156. 23 end,
157. 23 endOffset,
158. 23 endsWith,
159. 23 entries,
160. 23 env,
161. 23 err,
162. 23 error,
163. 23 eval,
164. 23 every,
165. 23 example_list,
166. 23 excludeList,
167. 23 exec,
168. 23 execArgv,
169. 23 exit,
170. 23 exitCode,
171. 23 export_dict,
172. 23 exports,
173. 23 expression,
174. 23 extra,
175. 23 fart,
176. 23 file,
177. 23 fileList,
178. 23 fileURLToPath,
179. 23 filter,
180. 23 finally,
181. 23 flag,
182. 23 floor,
183. 23 forEach,
184. 23 for_init,
185. 23 for_loop,
186. 23 for_of,
187. 23 for_semicolon,
188. 23 formatted_message,
189. 23 freeze,
190. 23 from,
191. 23 froms,
192. 23 fsWriteFileWithParents,
193. 23 fud_stmt,
194. 23 functionName,
195. 23 function_body,
196. 23 function_list,
197. 23 function_stack,
198. 23 functions,
199. 23 get,
200. 23 getset,
201. 23 github_repo,
202. 23 globExclude,
203. 23 global,
204. 23 global_dict,
205. 23 global_list,
206. 23 holeList,
207. 23 htmlEscape,
208. 23 id,
209. 23 identifier,
210. 23 ignoreLine,
211. 23 import,
212. 23 import_list,
213. 23 import_meta_url,
214. 23 includeList,
215. 23 includes,
216. 23 indent2,
217. 23 index,
218. 23 indexOf,
219. 23 isArray,
220. 23 isBlockCoverage,
221. 23 isHole,
222. 23 isNaN,
223. 23 is_equal,
224. 23 is_weird,
225. 23 itList,
226. 23 join,
227. 23 jslint,
228. 23 jslint_apidoc,
229. 23 jslint_charset_ascii,
230. 23 jslint_cli,
231. 23 jslint_edition,
232. 23 jslint_phase1_split,
233. 23 jslint_phase2_lex,
234. 23 jslint_phase3_parse,
235. 23 jslint_phase4_walk,
236. 23 jslint_phase5_whitage,
237. 23 jslint_phase6_autofix,
238. 23 jslint_report,
239. 23 json,
240. 23 jstestDescribe,
241. 23 jstestIt,
242. 23 keys,
243. 23 label_break,
244. 23 label_name,
245. 23 lbp,
246. 23 led_infix,
247. 23 length,
248. 23 level,
249. 23 line,
250. 23 lineList,
251. 23 line_list,
252. 23 line_offset,
253. 23 line_source,
254. 23 lines,
255. 23 linesCovered,
256. 23 linesTotal,
257. 23 log,
258. 23 long,
259. 23 loop,
260. 23 map,
261. 23 margin,
262. 23 match,
263. 23 max,
264. 23 message,
265. 23 meta,
266. 23 min,
267. 23 mkdir,
268. 23 modeCoverageIgnoreFile,
269. 23 modeIndex,
270. 23 mode_cli,
271. 23 mode_conditional,
272. 23 mode_json,
273. 23 mode_module,
274. 23 mode_noop,
275. 23 mode_property,
276. 23 mode_shebang,
277. 23 mode_stop,
278. 23 module,
279. 23 moduleFsInit,
280. 23 moduleName,
281. 23 module_list,
282. 23 name,
283. 23 name_alias,
284. 23 name_list,
285. 23 name_only,
286. 23 node,
287. 23 nomen,
288. 23 noop,
289. 23 now,
290. 23 npm_config_mode_test_filter,
291. 23 nr,
292. 23 nud_prefix,
293. 23 objectDeepCopyWithKeysSorted,
294. 23 ok,
295. 23 on,
296. 23 open,
297. 23 opening,
298. 23 operator,
299. 23 option,
300. 23 option_dict,
301. 23 optional,
302. 23 order,
303. 23 package_name,
304. 23 padEnd,
305. 23 padStart,
306. 23 parameter_count,
307. 23 parameter_rest,
308. 23 parentIi,
309. 23 parse,
310. 23 parseInt,
311. 23 pass,
312. 23 pathname,
313. 23 pathnameList,
314. 23 platform,
315. 23 pop,
316. 23 printed,
317. 23 processArgv,
318. 23 process_argv,
319. 23 process_env,
320. 23 process_exit,
321. 23 promises,
322. 23 property,
323. 23 property_dict,
324. 23 push,
325. 23 quote,
326. 23 ranges,
327. 23 readFile,
328. 23 readdir,
329. 23 readonly,
330. 23 recursive,
331. 23 reduce,
332. 23 repeat,
333. 23 replace,
334. 23 resolve,
335. 23 result,
336. 23 reverse,
337. 23 rgx,
338. 23 role,
339. 23 round,
340. 23 scope_block,
341. 23 scope_block_pop,
342. 23 scope_block_push,
343. 23 scope_declared,
344. 23 scope_function,
345. 23 scope_function_pop,
346. 23 scope_function_push,
347. 23 scope_name,
348. 23 scope_report_push,
349. 23 scriptId,
350. 23 search,
351. 23 set,
352. 23 shebang,
353. 23 shell,
354. 23 shift,
355. 23 signature,
356. 23 single,
357. 23 slice,
358. 23 some,
359. 23 sort,
360. 23 source,
361. 23 spawn,
362. 23 splice,
363. 23 split,
364. 23 stack,
365. 23 stack_trace,
366. 23 start,
367. 23 startOffset,
368. 23 startsWith,
369. 23 statement,
370. 23 status,
371. 23 stdio,
372. 23 stop,
373. 23 stop_at,
374. 23 stringify,
375. 23 subscript,
376. 23 suffix,
377. 23 suffix_file,
378. 23 switch,
379. 23 syntax_dict,
380. 23 tenure,
381. 23 test,
382. 23 testFunction,
383. 23 test_internal_error,
384. 23 test_unknown_warning_code,
385. 23 text,
386. 23 this,
387. 23 thru,
388. 23 toFixed,
389. 23 toLocaleString,
390. 23 toLowerCase,
391. 23 toString,
392. 23 token,
393. 23 token_global,
394. 23 token_list,
395. 23 token_nxt,
396. 23 token_tree,
397. 23 tokens,
398. 23 trace,
399. 23 tree,
400. 23 trim,
401. 23 trimEnd,
402. 23 trimStart,
403. 23 try,
404. 23 type,
405. 23 unlink,
406. 23 unordered,
407. 23 unshift,
408. 23 url,
409. 23 used,
410. 23 v8CoverageListMerge,
411. 23 v8CoverageReportCreate,
412. 23 value,
413. 23 values,
414. 23 variable,
415. 23 variable_prv,
416. 23 version,
417. 23 versions,
418. 23 warn,
419. 23 warn_at,
420. 23 warning,
421. 23 warning_list,
422. 23 warning_list_untagged,
423. 23 warnings,
424. 23 white,
425. 23 wrapped,
426. 23 writeFile
427. 23*/
428. 23
429. 23// init debugInline
430. 23const debugInline = (function () {
431. 23 let consoleError = Object;
432. 23 function debug(...argList) {
433. 23
434. 23// This function will print <argList> to stderr and then return <argList>[0].
435. 23
436. 23 consoleError("\n\ndebugInline");
437. 23 consoleError(...argList);
438. 23 consoleError("\n");
439. 23 if (consoleError === Object) {
440. 23 consoleError = console.error;
441. 23 }
442. 23 return argList[0];
443. 23 }
444. 23 return debug;
445. 23}());
446. 23debugInline(); // coverage-hack
447. 23
448. 23const jslint_autofix_warning_list = [ //jslint-ignore-line
449. 23 "expected_a_at_b_c",
450. 23 "expected_a_at_end",
451. 23 "expected_line_break_a_b",
452. 23 "expected_space_a_b",
453. 23 "unexpected_space_a_b",
454. 23
455. 23// PR-511 - jslint-autofix - Warning 'too_long' no longer blocks autofix.
456. 23
457. 23 "too_long"
458. 23];
459. 23const jslint_charset_ascii = (
460. 23 "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" +
461. 23 "\b\t\n\u000b\f\r\u000e\u000f" +
462. 23 "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" +
463. 23 "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" +
464. 23 " !\"#$%&'()*+,-./0123456789:;<=>?" +
465. 23 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
466. 23 "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
467. 23);
468. 23const jslint_edition = "v2026.10.30";
469. 23const jslint_fudge = 1; // Fudge starting line and starting
470. 23 // ... column to 1.
471. 23const jslint_global_dict_all = {
472. 23/*jslint-disable*/
473. 23
474. 23// Assign global browser variables to global_dict.
475. 23
476. 23 "browser": {
477. 23 "CharacterData": true,
478. 23 "DocumentType": true,
479. 23 "Element": true,
480. 23 "FileReader": true,
481. 23 "FontFace": true,
482. 23 "IntersectionObserver": true,
483. 23 "MutationObserver": true,
484. 23 "WebAssembly": true,
485. 23 "Worker": true,
486. 23 "XMLHttpRequest": true,
487. 23 "caches": false,
488. 23 "document": true,
489. 23 "history": false,
490. 23 "indexedDB": true,
491. 23 "location": true,
492. 23 "name": false,
493. 23 "postMessage": true,
494. 23 "screen": false,
495. 23 "window": true
496. 23 },
497. 23
498. 23// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
499. 23
500. 23 "couch": {
501. 23 "emit": true,
502. 23 "getRow": true,
503. 23 "isArray": true,
504. 23 "log": true,
505. 23 "provides": true,
506. 23 "registerType": true,
507. 23 "require": true,
508. 23 "send": true,
509. 23 "start": true,
510. 23 "sum": true,
511. 23 "toJSON": true
512. 23 },
513. 23 "devel": {
514. 23 "alert": true,
515. 23 "confirm": true,
516. 23 "console": true,
517. 23 "prompt": true
518. 23 },
519. 23
520. 23// These are the globals that are provided by the language standard.
521. 23// Assign global ECMAScript variables to global_dict.
522. 23
523. 23 "ecma": {
524. 23 "ShadowRealm": true,
525. 23 "eval": true,
526. 23 "import": true
527. 23 },
528. 23
529. 23// Assign global Node.js variables to global_dict.
530. 23
531. 23 "node": {
532. 23 "__dirname": true,
533. 23 "__filename": true
534. 23 },
535. 23
536. 23// sh jslint_ci.sh shCiJslintGlobalDictAllFetch
537. 23
538. 23// jslint_global_dict_all - auto-generated - start.
539. 23
540. 23 "ecma_auto": {
541. 23 "AggregateError": true,
542. 23 "Array": true,
543. 23 "ArrayBuffer": true,
544. 23 "AsyncDisposableStack": true,
545. 23 "AsyncFunction": true,
546. 23 "AsyncGenerator": true,
547. 23 "AsyncGeneratorFunction": true,
548. 23 "AsyncIterator": true,
549. 23 "Atomics": true,
550. 23 "BigInt": true,
551. 23 "BigInt64Array": true,
552. 23 "BigUint64Array": true,
553. 23 "Boolean": true,
554. 23 "DataView": true,
555. 23 "Date": true,
556. 23 "DisposableStack": true,
557. 23 "Error": true,
558. 23 "EvalError": true,
559. 23 "FinalizationRegistry": true,
560. 23 "Float16Array": true,
561. 23 "Float32Array": true,
562. 23 "Float64Array": true,
563. 23 "Function": true,
564. 23 "Generator": true,
565. 23 "GeneratorFunction": true,
566. 23 "Infinity": true,
567. 23 "Int16Array": true,
568. 23 "Int32Array": true,
569. 23 "Int8Array": true,
570. 23 "InternalError": false,
571. 23 "Intl": true,
572. 23 "Iterator": true,
573. 23 "JSON": true,
574. 23 "Map": true,
575. 23 "Math": true,
576. 23 "NaN": true,
577. 23 "Number": true,
578. 23 "Object": true,
579. 23 "Promise": true,
580. 23 "Proxy": true,
581. 23 "RangeError": true,
582. 23 "ReferenceError": true,
583. 23 "Reflect": true,
584. 23 "RegExp": true,
585. 23 "Set": true,
586. 23 "SharedArrayBuffer": true,
587. 23 "String": true,
588. 23 "SuppressedError": true,
589. 23 "Symbol": true,
590. 23 "SyntaxError": true,
591. 23 "Temporal": true,
592. 23 "TypeError": true,
593. 23 "TypedArray": true,
594. 23 "URIError": true,
595. 23 "Uint16Array": true,
596. 23 "Uint32Array": true,
597. 23 "Uint8Array": true,
598. 23 "Uint8ClampedArray": true,
599. 23 "WeakMap": true,
600. 23 "WeakRef": true,
601. 23 "WeakSet": true,
602. 23 "decodeURI": true,
603. 23 "decodeURIComponent": true,
604. 23 "encodeURI": true,
605. 23 "encodeURIComponent": true,
606. 23 "escape": false,
607. 23 "globalThis": true,
608. 23 "isFinite": true,
609. 23 "isNaN": true,
610. 23 "parseFloat": true,
611. 23 "parseInt": true,
612. 23 "undefined": true,
613. 23 "unescape": false
614. 23 },
615. 23 "node_auto": {
616. 23 "AbortController": "node brow",
617. 23 "AbortSignal": "node brow",
618. 23 "Blob": "node brow",
619. 23 "BroadcastChannel": "node brow",
620. 23 "Buffer": "node ----",
621. 23 "ByteLengthQueuingStrategy": "node brow",
622. 23 "CloseEvent": "node brow",
623. 23 "CompressionStream": "node brow",
624. 23 "CountQueuingStrategy": "node brow",
625. 23 "Crypto": "node brow",
626. 23 "CryptoKey": "node brow",
627. 23 "CustomEvent": "node brow",
628. 23 "DOMException": "node brow",
629. 23 "DecompressionStream": "node brow",
630. 23 "Event": "node brow",
631. 23 "EventSource": "node brow",
632. 23 "EventTarget": "node brow",
633. 23 "File": "node brow",
634. 23 "FormData": "node brow",
635. 23 "Headers": "node brow",
636. 23 "MessageChannel": "node brow",
637. 23 "MessageEvent": "node brow",
638. 23 "MessagePort": "node brow",
639. 23 "Navigator": "node brow",
640. 23 "PerformanceEntry": "node brow",
641. 23 "PerformanceMark": "node brow",
642. 23 "PerformanceMeasure": "node brow",
643. 23 "PerformanceObserver": "node brow",
644. 23 "PerformanceObserverEntryList": "node brow",
645. 23 "PerformanceResourceTiming": "node brow",
646. 23 "ReadableByteStreamController": "node brow",
647. 23 "ReadableStream": "node brow",
648. 23 "ReadableStreamBYOBReader": "node brow",
649. 23 "ReadableStreamBYOBRequest": "node brow",
650. 23 "ReadableStreamDefaultController": "node brow",
651. 23 "ReadableStreamDefaultReader": "node brow",
652. 23 "Request": "node brow",
653. 23 "Response": "node brow",
654. 23 "Storage": "node brow",
655. 23 "SubtleCrypto": "node brow",
656. 23 "TextDecoder": "node brow",
657. 23 "TextDecoderStream": "node brow",
658. 23 "TextEncoder": "node brow",
659. 23 "TextEncoderStream": "node brow",
660. 23 "TransformStream": "node brow",
661. 23 "TransformStreamDefaultController": "node brow",
662. 23 "URL": "node brow",
663. 23 "URLPattern": "node brow",
664. 23 "URLSearchParams": "node brow",
665. 23 "WebAssembly": "node ----",
666. 23 "WebSocket": "node brow",
667. 23 "WritableStream": "node brow",
668. 23 "WritableStreamDefaultController": "node brow",
669. 23 "WritableStreamDefaultWriter": "node brow",
670. 23 "__dirname": "node ----",
671. 23 "__filename": "node ----",
672. 23 "atob": "---- ----",
673. 23 "btoa": "---- ----",
674. 23 "clearImmediate": "node ----",
675. 23 "clearInterval": "node brow",
676. 23 "clearTimeout": "node brow",
677. 23 "console": "node brow",
678. 23 "crypto": "node brow",
679. 23 "exports": "node ----",
680. 23 "fetch": "node brow",
681. 23 "global": "node ----",
682. 23 "localStorage": "node brow",
683. 23 "module": "node ----",
684. 23 "navigator": "node brow",
685. 23 "performance": "node brow",
686. 23 "process": "node ----",
687. 23 "queueMicrotask": "node brow",
688. 23 "require": "node ----",
689. 23 "sessionStorage": "node brow",
690. 23 "setImmediate": "node ----",
691. 23 "setInterval": "node brow",
692. 23 "setTimeout": "node brow",
693. 23 "structuredClone": "node brow"
694. 23 }
695. 23
696. 23// jslint_global_dict_all - auto-generated - end.
697. 23
698. 23/*jslint-enable*/
699. 23};
700. 23const jslint_rgx_cap = (
701. 23 /^[A-Z]/
702. 23);
703. 23const jslint_rgx_crlf = (
704. 23 /\n|\r\n?/
705. 23);
706. 23const jslint_rgx_digits_bits = (
707. 23 /^[01_]*/
708. 23);
709. 23const jslint_rgx_digits_decimals = (
710. 23 /^[0-9_]*/
711. 23);
712. 23const jslint_rgx_digits_hexs = (
713. 23 /^[0-9A-F_]*/i
714. 23);
715. 23const jslint_rgx_digits_octals = (
716. 23 /^[0-7_]*/
717. 23);
718. 23const jslint_rgx_directive = (
719. 23 /^(jslint|property|global)\s+?(\S.*?)$/
720. 23);
721. 23const jslint_rgx_directive_part = (
722. 23 /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
723. 23);
724. 23const jslint_rgx_identifier = (
725. 23 /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
726. 23);
727. 23const jslint_rgx_json_number = (
728. 23
729. 23// https://datatracker.ietf.org/doc/html/rfc7159#section-6
730. 23// number = [ minus ] int [ frac ] [ exp ]
731. 23
732. 23 /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/
733. 23);
734. 23const jslint_rgx_mega = (
735. 23
736. 23// Vim-hack - vim-editor has trouble parsing naked '`' in regexp
737. 23
738. 23 /[\u0060\\]|\$\{/
739. 23);
740. 23const jslint_rgx_module = (
741. 23 /^[a-zA-Z0-9_$:.@\-\/]+$/
742. 23);
743. 23const jslint_rgx_numeric_separator_illegal = (
744. 23 /__|_$|_n$/m
745. 23);
746. 23const jslint_rgx_slash_star_or_slash = (
747. 23 /\/\*|\/$/
748. 23);
749. 23const jslint_rgx_tab = (
750. 23 /\t/g
751. 23);
752. 23const jslint_rgx_todo = (
753. 23 /\b(?:todo|TO\s?DO|HACK)\b/
754. 23);
755. 23const jslint_rgx_token = new RegExp(
756. 23 "^(" +
757. 23 "(\\s+)" +
758. 23 "|([a-zA-Z_$][a-zA-Z0-9_$]*)" +
759. 23 "|[(){}\\[\\],:;'\"~\\`]" +
760. 23 "|\\?\\?=" +
761. 23 "|\\?[?.]?" +
762. 23 "|=(?:==?|>)?" +
763. 23 "|\\.+" +
764. 23
765. 23// PR-514 - Add Exponentiation-assignment-operator '**=' support.
766. 23
767. 23 "|\\*(?:\\*=?|[\\/=])?" +
768. 23 "|\\/[*\\/]?" +
769. 23 "|\\+[=+]?" +
770. 23 "|-[=\\-]?" +
771. 23 "|[\\^%]=?" +
772. 23 "|&&=" +
773. 23 "|&[&=]?" +
774. 23 "|\\|\\|=" +
775. 23 "|\\|[|=]?" +
776. 23 "|>{1,3}=?" +
777. 23 "|<<?=?" +
778. 23 "|!(?:!|==?)?" +
779. 23
780. 23// PR-351 - Add BigInt support.
781. 23// PR-390 - Add numeric-separator support.
782. 23
783. 23 "|((?:0_?|[1-9][0-9_]*)n?)" +
784. 23 ")" +
785. 23 "(.*)$"
786. 23);
787. 23const jslint_rgx_url_search_window_jslint = (
788. 23 /[&?]window_jslint=1(?:$|&)/m
789. 23);
790. 23const jslint_rgx_weird_property = (
791. 23 /^_|\$|Sync$|_$/m
792. 23);
793. 23const jstestItRecordList = [];
794. 23let jslint_export; // The jslint object to be exported.
795. 23let jslint_import_meta_url = ""; // import.meta.url used by cli.
796. 23let jstestDescribeNow; // The describe whose body is running.
797. 23let jstestDescribePrv = "";
798. 23let jstestItCount = 0;
799. 23let jstestTimeStart;
800. 23let moduleChildProcess;
801. 23let moduleFs;
802. 23let moduleFsInitResolveList;
803. 23let modulePath;
804. 23let moduleUrl;
805. 23
806. 11async function assertErrorThrownAsync(asyncFunc, regexp) {
807. 11
808. 11// This function will assert calling <asyncFunc> throws an error.
809. 11
810. 11 let err;
811. 11 try {
812. 1 await asyncFunc();
813. 10 } catch (errCaught) {
814. 10 err = errCaught;
815. 10 }
816. 11 assertOrThrow(err, "No error thrown.");
817. 11 assertOrThrow(
818. 4 !regexp || new RegExp(regexp).test(err.message),
819. 11 err
820. 11 );
821. 11}
822. 23
823. 320function assertJsonEqual(aa, bb, message) {
824. 320
825. 320// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
826. 320
827. 320 aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
828. 320 bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
829. 3 if (aa !== bb) {
830. 3 throw new Error(
831. 3 "\n" + aa + "\n!==\n" + bb +
832. 3 (
833. 3 typeof message === "string"
834. 3 ? " - " + message
835. 3 : message
836. 3 ? " - " + JSON.stringify(message)
837. 3 : ""
838. 3 )
839. 3 );
840. 3 }
841. 320}
842. 23
843. 1842function assertOrThrow(condition, message) {
844. 1842
845. 1842// This function will throw <message> if <condition> is falsy.
846. 1842
847. 4 if (!condition) {
848. 4 throw (
849. 4 (!message || typeof message === "string")
850. 4 ? new Error(String(message).slice(0, 2048))
851. 4 : message
852. 4 );
853. 4 }
854. 1842}
855. 23
856. 155760function empty() {
857. 155760
858. 155760// The empty function produces a new empty object that inherits nothing. This is
859. 155760// much better than '{}' because confusions around accidental method names like
860. 155760// 'constructor' are completely avoided.
861. 155760
862. 155760 return Object.create(null);
863. 155760}
864. 23
865. 95async function fsWriteFileWithParents(pathname, data) {
866. 95
867. 95// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
868. 95
869. 95 await moduleFsInit();
870. 95
871. 95// Try writing to pathname.
872. 95
873. 95 try {
874. 72 await moduleFs.promises.writeFile(pathname, data);
875. 72 } catch (ignore) {
876. 23
877. 23// Lazy mkdirp.
878. 23
879. 23 await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
880. 23 recursive: true
881. 23 });
882. 23
883. 23// Retry writing to pathname.
884. 23
885. 23 await moduleFs.promises.writeFile(pathname, data);
886. 23 }
887. 95 console.error("wrote file " + pathname);
888. 95}
889. 23
890. 185function globExclude({
891. 185 excludeList = [],
892. 185 includeList = [],
893. 185 pathnameList = []
894. 185}) {
895. 185
896. 185// This function will
897. 185// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
898. 185// <includeList>.
899. 185// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
900. 185// <excludeList>.
901. 185
902. 555 function globAssertNotWeird(list, name) {
903. 555
904. 555// This function will check if <list> of strings contain weird characters.
905. 555
906. 555 [
907. 555 [
908. 555 "\n", (
909. 555 /^.*?([\u0000-\u0007\r]).*/gm
910. 555 )
911. 555 ],
912. 555 [
913. 555 "\r", (
914. 555 /^.*?([\n]).*/gm
915. 555 )
916. 555 ]
917. 1108 ].forEach(function ([
918. 1108 separator, rgx
919. 1108 ]) {
920. 3 list.join(separator).replace(rgx, function (match0, char) {
921. 3 throw new Error(
922. 3 "Weird character " +
923. 3 JSON.stringify(char) +
924. 3 " found in " + name + " " +
925. 3 JSON.stringify(match0)
926. 3 );
927. 3 });
928. 1108 });
929. 555 }
930. 185
931. 1371 function globToRegexp(pattern) {
932. 1371
933. 1371// This function will translate glob <pattern> to javascript-regexp,
934. 1371// which javascript can then use to "glob" pathnames.
935. 1371
936. 1371 let ii = 0;
937. 1371 let isClass = false;
938. 1371 let strClass = "";
939. 1371 let strRegex = "";
940. 1371 pattern = pattern.replace((
941. 1371 /\/\/+/g
942. 1371 ), "/");
943. 1371 pattern = pattern.replace((
944. 1371 /\*\*\*+/g
945. 1371 ), "**");
946. 1371 pattern.replace((
947. 1371 /\\\\|\\\[|\\\]|\[|\]|./g
948. 18704 ), function (match0) {
949. 18704 switch (match0) {
950. 310 case "[":
951. 310 if (isClass) {
952. 310 strClass += "[";
953. 310 return;
954. 310 }
955. 310 strClass += "\u0000";
956. 310 strRegex += "\u0000";
957. 310 isClass = true;
958. 310 return;
959. 310 case "]":
960. 310 if (isClass) {
961. 310 isClass = false;
962. 310 return;
963. 310 }
964. 310 strRegex += "]";
965. 310 return;
966. 18084 default:
967. 18084 if (isClass) {
968. 18084 strClass += match0;
969. 18084 return;
970. 18084 }
971. 18084 strRegex += match0;
972. 15034 }
973. 15034 return "";
974. 15034 });
975. 1371 strClass += "\u0000";
976. 1371
977. 1371// An expression "[!...]" matches a single character, namely any character that
978. 1371// is not matched by the expression obtained by removing the first '!' from it.
979. 1371// (Thus, "[!a-]" matches any single character except 'a', and '-'.)
980. 1371
981. 1371 strClass = strClass.replace((
982. 1371 /\u0000!/g
983. 1371 ), "\u0000^");
984. 1371
985. 1371// One may include '-' in its literal meaning by making it the first or last
986. 1371// character between the brackets.
987. 1371
988. 1371 strClass = strClass.replace((
989. 1371 /\u0000-/g
990. 1371 ), "\u0000\\-");
991. 1371 strClass = strClass.replace((
992. 1371 /-\u0000/g
993. 1371 ), "\\-\u0000");
994. 1371
995. 1371// Escape brackets '[', ']' in character class.
996. 1371
997. 1371 strClass = strClass.replace((
998. 1371 /[\[\]]/g
999. 1371 ), "\\$&");
1000. 1371
1001. 1371// https://stackoverflow.com/questions/3561493
1002. 1371// /is-there-a-regexp-escape-function-in-javascript
1003. 1371// $()*+-./?[\]^{|}
1004. 1371
1005. 1371 strRegex = strRegex.replace((
1006. 1371
1007. 1371// Ignore [-/].
1008. 1371
1009. 1371 /[$()*+.?\[\\\]\^{|}]/g
1010. 1371 ), "\\$&");
1011. 1371
1012. 1371// Expand wildcard '**/*'.
1013. 1371
1014. 1371 strRegex = strRegex.replace((
1015. 1371 /\\\*\\\*\/(?:\\\*)+/g
1016. 1371 ), ".*?");
1017. 1371
1018. 1371// Expand wildcard '**'.
1019. 1371
1020. 1371 strRegex = strRegex.replace((
1021. 1371 /(^|\/)\\\*\\\*(\/|$)/gm
1022. 1371 ), "$1.*?$2");
1023. 1371
1024. 1371// Expand wildcard '*'.
1025. 1371
1026. 1371 strRegex = strRegex.replace((
1027. 1371 /(?:\\\*)+/g
1028. 1371 ), "[^\\/]*?");
1029. 1371
1030. 1371// Expand wildcard '?'.
1031. 1371
1032. 1371 strRegex = strRegex.replace((
1033. 1371 /\\\?/g
1034. 1371 ), "[^\\/]");
1035. 1371
1036. 1371// Expand directory-with-trailing-slash '.../'.
1037. 1371
1038. 1371 strRegex = strRegex.replace((
1039. 1371 /\/$/gm
1040. 1371 ), "\\/.*?");
1041. 1371
1042. 1371// Merge strClass into strRegex.
1043. 1371
1044. 1371 ii = 0;
1045. 1371 strClass = strClass.split("\u0000");
1046. 1371 strRegex = strRegex.replace((
1047. 1371 /\u0000/g
1048. 306 ), function () {
1049. 306 ii += 1;
1050. 2 if (strClass[ii] === "") {
1051. 2 return "";
1052. 304 }
1053. 304 return "[" + strClass[ii] + "]";
1054. 304 });
1055. 1371
1056. 1371// Change strRegex from string to regexp.
1057. 1371
1058. 1371 strRegex = new RegExp("^" + strRegex + "$", "gm");
1059. 1371 return strRegex;
1060. 1371 }
1061. 185
1062. 185// Validate excludeList, includeList, pathnameList.
1063. 185
1064. 185 globAssertNotWeird(excludeList, "pattern");
1065. 185 globAssertNotWeird(includeList, "pattern");
1066. 185 globAssertNotWeird(pathnameList, "pathname");
1067. 185
1068. 185// Optimization
1069. 185// Concat pathnames into a single, newline-separated string,
1070. 185// whose pathnames can all be filtered with a single, regexp-pass.
1071. 185
1072. 185 pathnameList = pathnameList.join("\n");
1073. 185
1074. 185// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
1075. 185// <includeList>.
1076. 185
1077. 142 if (includeList.length > 0) {
1078. 142 includeList = includeList.map(globToRegexp);
1079. 574 includeList.forEach(function (pattern) {
1080. 574 pathnameList = pathnameList.replace(pattern, "\u0000$&");
1081. 574 });
1082. 142 pathnameList = pathnameList.replace((
1083. 142 /^[^\u0000].*/gm
1084. 142 ), "");
1085. 142 pathnameList = pathnameList.replace((
1086. 142 /^\u0000+/gm
1087. 142 ), "");
1088. 182 }
1089. 182
1090. 182// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
1091. 182// <excludeList>.
1092. 182
1093. 182 excludeList = excludeList.map(globToRegexp);
1094. 797 excludeList.forEach(function (pattern) {
1095. 797 pathnameList = pathnameList.replace(pattern, "");
1096. 797 });
1097. 182
1098. 182// Split newline-separated pathnames back to list.
1099. 182
1100. 10118 pathnameList = pathnameList.split("\n").filter(function (elem) {
1101. 10118 return elem;
1102. 10118 });
1103. 182 return {
1104. 182 excludeList,
1105. 182 includeList,
1106. 182 pathnameList
1107. 182 };
1108. 182}
1109. 23
1110. 20054function htmlEscape(str) {
1111. 20054
1112. 20054// This function will make <str> html-safe by escaping & < >.
1113. 20054
1114. 20054 return String(str)
1115. 20054 .replace((/&/g), "&")
1116. 20054 .replace((/</g), "<")
1117. 20054 .replace((/>/g), ">");
1118. 20054}
1119. 23
1120. 1050function jslint(
1121. 1050 source = "", // A text to analyze.
1122. 1050 option_dict = empty(), // An object whose keys correspond to option
1123. 1050 // ... names.
1124. 1050 global_list = [] // An array of strings containing global
1125. 1050 // ... variables that the file is allowed
1126. 1050 // ... readonly access.
1127. 1050) {
1128. 1050
1129. 1050// The jslint function itself.
1130. 1050
1131. 1050 const block_list = []; // The array containing all lexical-blocks.
1132. 1050 const block_stack = []; // The stack of lexical-blocks.
1133. 1050 const directive_list = []; // The directive comments.
1134. 1050 const export_dict = empty(); // The exported names and values.
1135. 1050 const function_list = []; // The array containing all functions.
1136. 1050 const function_stack = []; // The stack of functions.
1137. 1050 const global_dict = empty(); // The object containing the global
1138. 1050 // ... declarations.
1139. 1050 const import_list = []; // The array collecting all import-from strings.
1140. 1050 const line_list = String( // The array containing source lines.
1141. 1050 "\n" + source
1142. 121465 ).split(jslint_rgx_crlf).map(function (line_source) {
1143. 121465 return {
1144. 121465 line_source
1145. 121465 };
1146. 121465 });
1147. 1050 const mode_autofix = (
1148. 1050 option_dict.autofix === true
1149. 36 ? 256
1150. 1014 : Math.max(0, Number(option_dict.autofix) || 0)
1151. 1050 );
1152. 1050 const property_dict = empty(); // The object containing the tallied
1153. 1050 // ... property names.
1154. 1050 // jslint functions.
1155. 1050 const state = empty(); // jslint state-object to be passed between
1156. 1050 const syntax_dict = empty(); // The object containing the parser.
1157. 1050 const tenure = empty(); // The predefined property registry.
1158. 1050 const token_global = { // The global object; the outermost context.
1159. 1050 async: 0,
1160. 1050 context: empty(),
1161. 1050 finally: 0,
1162. 1050 from: 0,
1163. 1050 function_body: true,
1164. 1050 id: "(global)",
1165. 1050 level: 0,
1166. 1050 line: jslint_fudge,
1167. 1050 loop: 0,
1168. 1050 switch: 0,
1169. 1050 thru: 0,
1170. 1050 try: 0
1171. 1050 };
1172. 1050 const token_list = []; // The array of tokens.
1173. 1050 const warning_list = []; // The array collecting all generated warnings.
1174. 1050 let autofixed; // The <source> after one autofix pass.
1175. 1050 let mode_stop = false; // true if JSLint cannot finish.
1176. 1050 let result_autofix; // The re-lint of <autofixed>.
1177. 1050
1178. 1050// Error reportage functions:
1179. 1050
1180. 9832 function artifact(the_token) {
1181. 9832
1182. 9832// Return a string representing an artifact.
1183. 9832
1184. 41 the_token = the_token || state.token_nxt;
1185. 9832 return (
1186. 6598 (the_token.id === "(string)" || the_token.id === "(number)")
1187. 3551 ? String(the_token.value)
1188. 6281 : the_token.id
1189. 9832 );
1190. 9832 }
1191. 1050
1192. 33879 function is_equal(aa, bb) {
1193. 33879
1194. 33879// test_cause:
1195. 33879// ["0&&0", "is_equal", "(live)", "", 0]
1196. 33879
1197. 33879// PR-510 - deadcode-confirmed - <aa> and <bb> are never the same object. Each
1198. 33879// call below pairs a slot of <aa> with the same slot of <bb>, and the only
1199. 33879// slot that can be undefined on both sides is <expression>[1] of a binary
1200. 33879// token. Case "`" returns before that branch, and <aa>.id !== "(" skips it.
1201. 33879//
1202. 33879// deadcode-revive - Delete case '`' below and a tagged-megastring with no
1203. 33879// substitution falls through to the binary branch again. Its <expression>
1204. 33879// holds one element, so both sides read <expression>[1] as undefined. Any
1205. 33879// token with fewer slots than its <arity> implies does the same.
1206. 33879//
1207. 33879// if (aa === bb) {
1208. 33879// return true;
1209. 33879// }
1210. 33879
1211. 33879 jslint_assert(!(aa === bb), `Expected !(aa === bb).`);
1212. 38 if (Array.isArray(aa)) {
1213. 38 return (
1214. 38 Array.isArray(bb) &&
1215. 38 aa.length === bb.length &&
1216. 39 aa.every(function (value, index) {
1217. 39
1218. 39// test_cause:
1219. 39// ["`${0}`&&`${0}`", "is_equal", "(live)", "", 0]
1220. 39// ["`${0}`&&`${1}`", "is_equal", "(live)", "", 0]
1221. 39
1222. 39 return is_equal(value, bb[index]);
1223. 39 })
1224. 38 );
1225. 33841 }
1226. 33841
1227. 33841// PR-510 - deadcode-confirmed - If <aa> is not an array then <bb> is not one
1228. 33841// either. Arrays only reach <is_equal> from case "`" below, and only when
1229. 33841// <aa>.id === <bb>.id, so they arrive in pairs and the branch above takes both.
1230. 33841//
1231. 33841// deadcode-revive - Recurse on a slot that holds an array for some token ids
1232. 33841// and a single token for others, or let the backtick case run when only one
1233. 33841// side is a backtick.
1234. 33841//
1235. 33841// if (Array.isArray(bb)) {
1236. 33841// return false;
1237. 33841// }
1238. 33841
1239. 33841 jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
1240. 33841 switch (aa.id === bb.id && aa.id) {
1241. 82 case "(number)":
1242. 24192 case "(string)":
1243. 24192 return aa.value === bb.value;
1244. 23 case "`":
1245. 23
1246. 23// test_cause:
1247. 23// ["aa=bb`${cc}${dd}`&&bb`${cc}${ee}`", "is_equal", "(live)", "", 0]
1248. 23
1249. 23 return (
1250. 23
1251. 23// PR-394 - Bugfix - Fix jslint treating megastrings bb`0` and bb`1` as equal.
1252. 23// The untagged form always warns, because <post_b_and> fires on any constant.
1253. 23
1254. 23 is_equal(aa.value, bb.value) &&
1255. 23
1256. 23// PR-510 - Bugfix - Fix jslint treating tagged-megastrings as equal when they
1257. 23// differ past the first substitution, which is all the binary branch compared.
1258. 23
1259. 23 is_equal(aa.expression, bb.expression)
1260. 23 );
1261. 9626 }
1262. 9626 if (is_weird(aa) || is_weird(bb)) {
1263. 35
1264. 35// test_cause:
1265. 35// ["aa(/./)||{}", "is_equal", "(live)", "", 0]
1266. 35
1267. 35 return false;
1268. 9591 }
1269. 9591 if (aa.arity === bb.arity && aa.id === bb.id) {
1270. 2194 if (aa.id === "." || aa.id === "?.") {
1271. 2194
1272. 2194// test_cause:
1273. 2194// ["aa.bb&&aa.bb", "is_equal", "(live)", "", 0]
1274. 2194// ["aa?.bb&&aa?.bb", "is_equal", "(live)", "", 0]
1275. 2194
1276. 2194 return (
1277. 2194 is_equal(aa.expression, bb.expression) &&
1278. 2194 is_equal(aa.name, bb.name)
1279. 2194 );
1280. 2194 }
1281. 2194 if (aa.arity === "unary") {
1282. 2194
1283. 2194// test_cause:
1284. 2194// ["+0&&+0", "is_equal", "(live)", "", 0]
1285. 2194
1286. 2194 return is_equal(aa.expression, bb.expression);
1287. 2194 }
1288. 2194 if (aa.arity === "binary") {
1289. 2194
1290. 2194// test_cause:
1291. 2194// ["aa[0]&&aa[0]", "is_equal", "(live)", "", 0]
1292. 2194
1293. 2194 return (
1294. 2194 aa.id !== "(" &&
1295. 2194 is_equal(aa.expression[0], bb.expression[0]) &&
1296. 2194 is_equal(aa.expression[1], bb.expression[1])
1297. 2194 );
1298. 2194 }
1299. 2194 if (aa.arity === "ternary") {
1300. 2194
1301. 2194// test_cause:
1302. 2194// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "(live)", "", 0]
1303. 2194
1304. 2194 return (
1305. 2194 is_equal(aa.expression[0], bb.expression[0]) &&
1306. 2194 is_equal(aa.expression[1], bb.expression[1]) &&
1307. 2194 is_equal(aa.expression[2], bb.expression[2])
1308. 2194 );
1309. 2194 }
1310. 2194
1311. 2194// PR-510 - deadcode-confirmed - Nothing in this file sets <arity> to "regexp",
1312. 2194// and <arity> "function" is set in one place, on the "(" that opens a
1313. 2194// parameter list in <prefix_function>. That token is never stored in an
1314. 2194// <expression>, <name> or <value> slot, so <is_equal> never sees it.
1315. 2194//
1316. 2194// deadcode-revive - Compare two function expressions by recursing into their
1317. 2194// parameter lists, which puts that "(" on both sides. Giving any token
1318. 2194// <arity> "regexp" would also do it.
1319. 2194//
1320. 2194// if (aa.arity === "function" || aa.arity === "regexp") {
1321. 2194// return false;
1322. 2194// }
1323. 2194
1324. 2194 jslint_assert(
1325. 2194 !(aa.arity === "function" || aa.arity === "regexp"),
1326. 2194 `Expected !(aa.arity === "function" || aa.arity === "regexp").`
1327. 2194 );
1328. 2194
1329. 2194// test_cause:
1330. 2194// ["undefined&&undefined", "is_equal", "(live)", "", 0]
1331. 2194
1332. 2194 return true;
1333. 7397 }
1334. 7397
1335. 7397// test_cause:
1336. 7397// ["null&&undefined", "is_equal", "(live)", "", 0]
1337. 7397
1338. 7397 return false;
1339. 7397 }
1340. 1050
1341. 32332 function is_weird(thing) {
1342. 32332 switch (thing.id) {
1343. 1 case "(regexp)":
1344. 1 return true;
1345. 1 case "=>":
1346. 1 return true;
1347. 601 case "[":
1348. 601 return thing.arity === "unary";
1349. 14 case "function":
1350. 14 return true;
1351. 11 case "{":
1352. 11 return true;
1353. 31704 default:
1354. 31704 return false;
1355. 32332 }
1356. 32332 }
1357. 1050
1358. 22676 function scope_block_pop() {
1359. 22676 jslint_assert(
1360. 22676 block_stack.length > 1,
1361. 22676 `block_stack.length=${block_stack.length}`
1362. 22676 );
1363. 22676 block_stack.shift();
1364. 22676 return block_stack[0];
1365. 22676 }
1366. 1050
1367. 24576 function scope_block_push(value, list_push) {
1368. 9028 if (!value.context) {
1369. 9028 value.context = empty();
1370. 9028 }
1371. 24576 block_stack.unshift(value);
1372. 10022 if (list_push) {
1373. 10022 block_list.push(value);
1374. 10022 }
1375. 24576 return value;
1376. 24576 }
1377. 1050
1378. 4705 function scope_function_pop() {
1379. 4705 jslint_assert(
1380. 4705 function_stack.length > 1,
1381. 4705 `function_stack.length=${function_stack.length}`
1382. 4705 );
1383. 4705 function_stack.shift();
1384. 4705 return function_stack[0];
1385. 4705 }
1386. 1050
1387. 6573 function scope_function_push(value, list_push) {
1388. 6573 function_stack.unshift(value);
1389. 2367 if (list_push) {
1390. 2367 function_list.push(value);
1391. 2367 }
1392. 6573 return value;
1393. 6573 }
1394. 1050
1395. 22967 function scope_report_push(the_function, name) {
1396. 22967
1397. 22967// PR-516 - Bugfix - This function will push <name> to what <jslint_report>
1398. 22967// lists for <the_function>, which are the names it declares and the outer
1399. 22967// names it uses. An own 'bb' and an outer 'bb' are two entries.
1400. 22967
1401. 5 if (the_function.scope_name === name.scope_declared) {
1402. 5
1403. 5// PR-516 - A function using its own name from <scope_name> lists it nowhere,
1404. 5// though a nested function using it lists it as outer.
1405. 5
1406. 5// test_cause:
1407. 5// ["aa=function bb(){return bb;}", "scope_report_push", "(live)", "", 0]
1408. 5
1409. 5 return;
1410. 22962 }
1411. 22962 the_function.context_report ??= [];
1412. 22962 the_function.context_report.push(name);
1413. 22962 }
1414. 1050
1415. 134 function stop(code, the_token, a, b, c, d) {
1416. 134
1417. 134// Similar to warn and stop_at. If the token already had a warning, that
1418. 134// warning will be replaced with this new one. It is likely that the stopping
1419. 134// warning will be the more meaningful.
1420. 134
1421. 7 the_token = the_token || state.token_nxt;
1422. 134 delete the_token.warning;
1423. 134 throw warn(code, the_token, a, b, c, d);
1424. 134 }
1425. 1050
1426. 40 function stop_at(code, line, column, a, b, c, d) {
1427. 40
1428. 40// Same as warn_at, except that it stops the analysis.
1429. 40
1430. 40 throw warn_at(code, line, column, a, b, c, d);
1431. 40 }
1432. 1050
1433. 1830 function warn(code, the_token, a, b, c, d) {
1434. 1830
1435. 1830// Same as warn_at, except the warning will be associated with a specific token.
1436. 1830// If there is already a warning on this token, suppress the new one. It is
1437. 1830// likely that the first warning will be the most meaningful.
1438. 1830
1439. 1830 let the_warning;
1440. 21 the_token = the_token || state.token_nxt;
1441. 1830 the_warning = warn_at(
1442. 1830 code,
1443. 1830 the_token.line,
1444. 1830 the_token.from,
1445. 1342 a || artifact(the_token),
1446. 1830 b,
1447. 1830 c,
1448. 1830 d
1449. 1830 );
1450. 1830
1451. 1830// Issue #408
1452. 1830// Warnings that should be ignored sometimes suppress legitimate warnings.
1453. 1830
1454. 88 if (the_warning.directive_ignore_line) {
1455. 88 return the_warning;
1456. 1742 }
1457. 1742
1458. 1742// If there is already a warning on this token, suppress the new one. It is
1459. 1742// likely that the first warning will be the most meaningful.
1460. 1742
1461. 1742 if (the_token.warning) {
1462. 234 warning_list.pop();
1463. 234 return the_warning;
1464. 1508 }
1465. 1508 the_token.warning = the_warning;
1466. 1508 return the_warning;
1467. 1508 }
1468. 1050
1469. 2389 function warn_at(code, line, column, a, b, c, d) {
1470. 2389
1471. 2389// Report an error at some line and column of the program. The warning object
1472. 2389// resembles an exception.
1473. 2389
1474. 2389 const warning = {
1475. 2389 a, //jslint-ignore-line
1476. 2389 b, //jslint-ignore-line
1477. 2389 c, //jslint-ignore-line
1478. 2389 code,
1479. 2389 d, //jslint-ignore-line
1480. 2389 line,
1481. 2389 line_source: "",
1482. 2389 name: "JSLintError",
1483. 2389 ...line_list[Math.min(line, line_list.length - 1)]
1484. 2389 };
1485. 2389 let mm;
1486. 2389 jslint_assert(typeof column === "number", `column=${column}`);
1487. 35 if (line >= line_list.length) {
1488. 35
1489. 35// Handle premature EOF.
1490. 35
1491. 35 warning.line = line_list.length - 1;
1492. 35 column = warning.line_source.length - 1;
1493. 35 }
1494. 2389 warning.column = (
1495. 2389
1496. 2389// Fudge column numbers in warning message.
1497. 2389
1498. 2389 jslint_fudge +
1499. 2389 Math.max(0, Math.min(column, warning.line_source.length - 1))
1500. 2389 );
1501. 2389 switch (code) {
1502. 2389
1503. 2389// The bundle contains the raw text messages that are generated by jslint. It
1504. 2389// seems that they are all error messages and warnings. There are no "Atta
1505. 2389// boy!" or "You are so awesome!" messages. There is no positive reinforcement
1506. 2389// or encouragement. This relentless negativity can undermine self-esteem and
1507. 2389// wound the inner child. But if you accept it as sound advice rather than as
1508. 2389// personal criticism, it can make your programs better.
1509. 2389
1510. 18 case "bad_assignment_a":
1511. 18 mm = `Bad assignment to '${a}'.`;
1512. 18 break;
1513. 2 case "bad_directive_a":
1514. 2 mm = `Bad directive '${a}'.`;
1515. 2 break;
1516. 1 case "bad_get":
1517. 1 mm = `A get function takes no parameters.`;
1518. 1 break;
1519. 1 case "bad_module_name_a":
1520. 1 mm = `Bad module name '${a}'.`;
1521. 1 break;
1522. 3 case "bad_option_a":
1523. 3 mm = `Bad option '${a}'.`;
1524. 3 break;
1525. 2 case "bad_set":
1526. 2 mm = `A set function takes one parameter.`;
1527. 2 break;
1528. 5 case "duplicate_a":
1529. 5 mm = `Duplicate '${a}'.`;
1530. 5 break;
1531. 140 case "empty_block":
1532. 140 mm = `Empty block.`;
1533. 140 break;
1534. 44 case "expected_a":
1535. 44 mm = `Expected '${a}'.`;
1536. 44 break;
1537. 404 case "expected_a_after_b":
1538. 404 mm = `Expected '${a}' after '${b}'.`;
1539. 404 break;
1540. 54 case "expected_a_at_b_c":
1541. 54 mm = (
1542. 54 `Expected '${a}' at column ${b + jslint_fudge},` +
1543. 54 ` not column ${c + jslint_fudge}.`
1544. 54 );
1545. 54 break;
1546. 11 case "expected_a_at_end":
1547. 11 mm = `Expected '${a}' at the end of its left operand's line.`;
1548. 11 break;
1549. 113 case "expected_a_b":
1550. 113 mm = `Expected '${a}' and instead saw '${b}'.`;
1551. 113 break;
1552. 21 case "expected_a_b_before_c_d":
1553. 21 mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`;
1554. 21 break;
1555. 2 case "expected_a_b_from_c_d":
1556. 2 mm = (
1557. 2 `Expected '${a}' to match '${b}' from line ${c}` +
1558. 2 ` and instead saw '${d}'.`
1559. 2 );
1560. 2 break;
1561. 40 case "expected_a_before_b":
1562. 40 mm = `Expected '${a}' before '${b}'.`;
1563. 40 break;
1564. 4 case "expected_digits_after_a":
1565. 4 mm = `Expected digits after '${a}'.`;
1566. 4 break;
1567. 4 case "expected_four_digits":
1568. 4 mm = `Expected four digits after '\\u'.`;
1569. 4 break;
1570. 37 case "expected_identifier_a":
1571. 37 mm = `Expected an identifier and instead saw '${a}'.`;
1572. 37 break;
1573. 35 case "expected_line_break_a_b":
1574. 35 mm = `Expected a line break between '${a}' and '${b}'.`;
1575. 35 break;
1576. 5 case "expected_regexp_factor_a":
1577. 5 mm = `Expected a regexp factor and instead saw '${a}'.`;
1578. 5 break;
1579. 75 case "expected_space_a_b":
1580. 75 mm = `Expected one space between '${a}' and '${b}'.`;
1581. 75 break;
1582. 2 case "expected_statements_a":
1583. 2 mm = `Expected statements before '${a}'.`;
1584. 2 break;
1585. 2 case "expected_string_a":
1586. 2 mm = `Expected a string and instead saw '${a}'.`;
1587. 2 break;
1588. 1 case "expected_type_string_a":
1589. 1 mm = `Expected a type string and instead saw '${a}'.`;
1590. 1 break;
1591. 12 case "freeze_exports":
1592. 12 mm = (
1593. 12 `Expected 'Object.freeze('. All export values should be frozen.`
1594. 12 );
1595. 12 break;
1596. 2389
1597. 2389// PR-378 - Relax warning "function_in_loop".
1598. 2389//
1599. 2389// case "function_in_loop":
1600. 2389// mm = `Don't create functions within a loop.`;
1601. 2389// break;
1602. 2389
1603. 2389// PR-390 - Add numeric-separator check.
1604. 2389
1605. 8 case "illegal_num_separator":
1606. 8 mm = `Illegal numeric separator '_' at column ${warning.column}.`;
1607. 8 break;
1608. 1 case "infix_in":
1609. 1 mm = (
1610. 1 `Unexpected 'in'. Compare with undefined,` +
1611. 1 ` or use Object.hasOwn() instead.`
1612. 1 );
1613. 1 break;
1614. 2 case "label_a":
1615. 2 mm = `'${a}' is a statement label.`;
1616. 2 break;
1617. 1 case "misplaced_a":
1618. 1 mm = `Place '${a}' at the outermost level.`;
1619. 1 break;
1620. 2 case "misplaced_directive_a":
1621. 2 mm = `Place the '/*${a}*/' directive before the first statement.`;
1622. 2 break;
1623. 6 case "missing_await_statement":
1624. 6 mm = `Expected await statement in async function.`;
1625. 6 break;
1626. 2 case "missing_m":
1627. 2 mm = `Expected 'm' flag on a multiline regular expression.`;
1628. 2 break;
1629. 4 case "naked_block":
1630. 4 mm = `Naked block.`;
1631. 4 break;
1632. 3 case "nested_comment":
1633. 3 mm = `Nested comment.`;
1634. 3 break;
1635. 3 case "not_label_a":
1636. 3 mm = `'${a}' is not a label.`;
1637. 3 break;
1638. 3 case "number_isNaN":
1639. 3 mm = `Use Number.isNaN function to compare with NaN.`;
1640. 3 break;
1641. 27 case "redefinition_a_b":
1642. 27 mm = `Redefinition of '${a}' from line ${b}.`;
1643. 27 break;
1644. 1 case "redefinition_global_a_b":
1645. 1 mm = `Redefinition of global ${a} variable '${b}'.`;
1646. 1 break;
1647. 6 case "required_a_optional_b":
1648. 6 mm = `Required parameter '${a}' after optional parameter '${b}'.`;
1649. 6 break;
1650. 1 case "reserved_a":
1651. 1 mm = `Reserved name '${a}'.`;
1652. 1 break;
1653. 1 case "subscript_a":
1654. 1 mm = `['${a}'] is better written in dot notation.`;
1655. 1 break;
1656. 29 case "temporal_dead_zone_a":
1657. 29 mm = `'${a}' was used before it was defined.`;
1658. 29 break;
1659. 22 case "todo_comment":
1660. 22 mm = `Unexpected TODO comment.`;
1661. 22 break;
1662. 26 case "too_long":
1663. 26 mm = `Line is longer than 80 characters.`;
1664. 26 break;
1665. 4 case "too_many_digits":
1666. 4 mm = `Too many digits.`;
1667. 4 break;
1668. 3 case "unassigned_var_a":
1669. 3 mm = `Unassigned variable '${a}'.`;
1670. 3 break;
1671. 4 case "unclosed_comment":
1672. 4 mm = `Unclosed comment.`;
1673. 4 break;
1674. 3 case "unclosed_disable":
1675. 3 mm = (
1676. 3 `Directive '/*jslint-disable*/' was not closed` +
1677. 3 ` with '/*jslint-enable*/'.`
1678. 3 );
1679. 3 break;
1680. 5 case "unclosed_mega":
1681. 5 mm = `Unclosed mega literal.`;
1682. 5 break;
1683. 2 case "unclosed_string":
1684. 2 mm = `Unclosed string.`;
1685. 2 break;
1686. 221 case "undeclared_a":
1687. 221 mm = `Undeclared '${a}'.`;
1688. 221 break;
1689. 380 case "unexpected_a":
1690. 380 mm = `Unexpected '${a}'.`;
1691. 380 break;
1692. 9 case "unexpected_a_after_b":
1693. 9 mm = `Unexpected '${a}' after '${b}'.`;
1694. 9 break;
1695. 4 case "unexpected_a_before_b":
1696. 4 mm = `Unexpected '${a}' before '${b}'.`;
1697. 4 break;
1698. 81 case "unexpected_at_top_level_a":
1699. 81 mm = `Expected '${a}' to be in a function.`;
1700. 81 break;
1701. 2 case "unexpected_char_a":
1702. 2 mm = `Unexpected character '${a}'.`;
1703. 2 break;
1704. 3 case "unexpected_comment":
1705. 3 mm = `Unexpected comment.`;
1706. 3 break;
1707. 2389
1708. 2389// PR-347 - Disable warning "unexpected_directive_a".
1709. 2389//
1710. 2389// case "unexpected_directive_a":
1711. 2389// mm = `When using modules, don't use directive '/\u002a${a}'.`;
1712. 2389// break;
1713. 2389
1714. 221 case "unexpected_expression_a":
1715. 221 mm = `Unexpected expression '${a}' in statement position.`;
1716. 221 break;
1717. 8 case "unexpected_label_a":
1718. 8 mm = `Unexpected label '${a}'.`;
1719. 8 break;
1720. 6 case "unexpected_parens":
1721. 6 mm = `Don't wrap function literals in parens.`;
1722. 6 break;
1723. 27 case "unexpected_space_a_b":
1724. 27 mm = `Unexpected space between '${a}' and '${b}'.`;
1725. 27 break;
1726. 2 case "unexpected_statement_a":
1727. 2 mm = `Unexpected statement '${a}' in expression position.`;
1728. 2 break;
1729. 8 case "unexpected_trailing_space":
1730. 8 mm = `Unexpected trailing space.`;
1731. 8 break;
1732. 1 case "unexpected_typeof_a":
1733. 1 mm = (
1734. 1 `Unexpected 'typeof'. Use '===' to compare directly with ${a}.`
1735. 1 );
1736. 1 break;
1737. 1 case "unopened_enable":
1738. 1 mm = (
1739. 1 `Directive '/*jslint-enable*/' was not opened` +
1740. 1 ` with '/*jslint-disable*/'.`
1741. 1 );
1742. 1 break;
1743. 1 case "unreachable_a":
1744. 1 mm = `Unreachable '${a}'.`;
1745. 1 break;
1746. 36 case "unregistered_property_a":
1747. 36 mm = `Unregistered property name '${a}'.`;
1748. 36 break;
1749. 28 case "unused_a":
1750. 28 mm = `Unused '${a}'.`;
1751. 28 break;
1752. 3 case "use_double":
1753. 3 mm = `Use double quotes, not single quotes.`;
1754. 3 break;
1755. 2389
1756. 2389// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
1757. 2389
1758. 7 case "use_function_not_fart":
1759. 7 mm = (
1760. 7 `Use 'function (...)', not '(...) =>' when arrow functions` +
1761. 7 ` become too complex.`
1762. 7 );
1763. 7 break;
1764. 9 case "use_open":
1765. 9 mm = (
1766. 9 `Wrap a ternary expression in parens,` +
1767. 9 ` with a line break after the left paren.`
1768. 9 );
1769. 9 break;
1770. 3 case "use_spaces":
1771. 3 mm = `Use spaces, not tabs.`;
1772. 3 break;
1773. 21 case "var_on_top_a_b":
1774. 21 mm = `Move ${a} declaration to top of ${b} or script.`;
1775. 21 break;
1776. 2 case "var_switch":
1777. 2 mm = `Don't declare variables in a switch.`;
1778. 2 break;
1779. 25 case "weird_condition_a":
1780. 25 mm = `Weird condition '${a}'.`;
1781. 25 break;
1782. 5 case "weird_expression_a":
1783. 5 mm = `Weird expression '${a}'.`;
1784. 5 break;
1785. 9 case "weird_loop":
1786. 9 mm = `Weird loop.`;
1787. 9 break;
1788. 10 case "weird_property_a":
1789. 10 mm = `Weird property name '${a}'.`;
1790. 10 break;
1791. 9 case "weird_relation_a":
1792. 9 mm = `Weird relation '${a}'.`;
1793. 9 break;
1794. 2389
1795. 2389// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
1796. 2389
1797. 3 case "wrap_fart_parameter":
1798. 3 mm = `Wrap the parameter before '=>' in parens.`;
1799. 3 break;
1800. 2 case "wrap_immediate":
1801. 2 mm = (
1802. 2 `Wrap an immediate function invocation in parentheses to` +
1803. 2 ` assist the reader in understanding that the expression is` +
1804. 2 ` the result of a function, and not the function itself.`
1805. 2 );
1806. 2 break;
1807. 18 case "wrap_regexp":
1808. 18 mm = `Wrap this regexp in parens to avoid confusion.`;
1809. 18 break;
1810. 15 case "wrap_subexpression_a_b":
1811. 15 mm = `Wrap the '${a}' subexpression beside '${b}' in parens.`;
1812. 15 break;
1813. 2 default:
1814. 2 jslint_assert(undefined, `unknown_warning_code=${code}`);
1815. 2387 }
1816. 2387
1817. 2387// Validate mm.
1818. 2387
1819. 2387 jslint_assert(mm, code);
1820. 2387 warning.message = mm;
1821. 2387
1822. 2387// PR-242 - Include stack_trace for jslint to debug itself for errors.
1823. 2387
1824. 2387 if (option_dict.trace) {
1825. 2217 warning.stack_trace = new Error().stack;
1826. 2387 }
1827. 2387 if (warning.directive_ignore_line) {
1828. 109
1829. 109// test_cause:
1830. 109// ["0 //jslint-ignore-line", "warn_at", "(live)", "", 0]
1831. 109
1832. 109 return warning;
1833. 2278 }
1834. 2278 warning_list.push(warning);
1835. 2278 return warning;
1836. 2278 }
1837. 1050
1838. 1050 try {
1839. 1050
1840. 1050// tokenize takes a source and produces from it an array of token objects.
1841. 1050// JavaScript is notoriously difficult to tokenize because of the horrible
1842. 1050// interactions between automatic semicolon insertion, regular expression
1843. 1050// literals, and now megastring literals. JSLint benefits from eliminating
1844. 1050// automatic semicolon insertion and nested megastring literals, which allows
1845. 1050// full tokenization to precede parsing.
1846. 1050
1847. 1050 option_dict = {
1848. 1050 ...option_dict
1849. 1050 };
1850. 1050 Object.assign(
1851. 1050 state,
1852. 1050 {
1853. 1050 artifact,
1854. 1050 block_list,
1855. 1050 block_stack,
1856. 1050 directive_list,
1857. 1050 export_dict,
1858. 1050 function_list,
1859. 1050 function_stack,
1860. 1050 global_dict,
1861. 1050 global_list,
1862. 1050 import_list,
1863. 1050 is_equal,
1864. 1050 is_weird,
1865. 1050 line_list,
1866. 1050 mode_json: false, // true if parsing JSON.
1867. 1050 mode_module: false, // true if import or export was used.
1868. 1050 mode_property: false, // true if directive /*property*/ is
1869. 1050 // ... used.
1870. 1050 mode_shebang: false, // true if #! is seen on the first line.
1871. 1050 option_dict,
1872. 1050 property_dict,
1873. 1050 scope_block_pop,
1874. 1050 scope_block_push,
1875. 1050 scope_function_pop,
1876. 1050 scope_function_push,
1877. 1050 scope_report_push,
1878. 1050 source,
1879. 1050 stop,
1880. 1050 stop_at,
1881. 1050 syntax_dict,
1882. 1050 tenure,
1883. 1050 token_global,
1884. 1050 token_list,
1885. 1050 token_nxt: token_global,
1886. 1050 warn,
1887. 1050 warn_at,
1888. 1050 warning_list
1889. 1050 }
1890. 1050 );
1891. 1050
1892. 1050// PHASE 1. Split <source> by newlines into <line_list>.
1893. 1050
1894. 1050 jslint_phase1_split(state);
1895. 1050
1896. 1050// PHASE 2. Lex <line_list> into <token_list>.
1897. 1050
1898. 1050 jslint_phase2_lex(state);
1899. 1050
1900. 1050// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
1901. 1050
1902. 1050 jslint_phase3_parse(state);
1903. 1050
1904. 1050// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
1905. 1050// recursive traversal. Each node may be processed on the way down
1906. 1050// (preaction) and on the way up (postaction).
1907. 1050
1908. 861 if (!state.mode_json) {
1909. 861 jslint_phase4_walk(state);
1910. 876 }
1911. 876
1912. 876// PHASE 5. Check whitespace between tokens in <token_list>.
1913. 876
1914. 876 if (
1915. 876 !state.mode_json &&
1916. 861 !warning_list.some(function ({
1917. 861 code
1918. 861 }) {
1919. 861 if (mode_autofix) {
1920. 861
1921. 861// PR-511 - jslint-autofix - Warning 'too_long' no longer blocks autofix.
1922. 861
1923. 861 return !jslint_autofix_warning_list.includes(code);
1924. 861 }
1925. 861 return true;
1926. 861 })
1927. 306 ) {
1928. 306 jslint_phase5_whitage(state);
1929. 876 }
1930. 876
1931. 876// PHASE 6. Autofix whitespace-warnings in <source>, and re-lint the result.
1932. 876
1933. 876 if (mode_autofix) {
1934. 67 autofixed = jslint_phase6_autofix(state) || state.source;
1935. 67 if (autofixed !== state.source) {
1936. 67
1937. 67// Recurse jslint.
1938. 67
1939. 67 result_autofix = jslint(
1940. 67 autofixed,
1941. 67 {
1942. 67 ...option_dict,
1943. 67 autofix: mode_autofix - 1
1944. 67 },
1945. 67 global_list
1946. 67 );
1947. 67
1948. 67// A stop in the re-lint can only be the fix's own doing - this pass parsed to
1949. 67// the end, or phase 6 would have had a non-whitespace warning to block on - so
1950. 67// discard the fix and report on <source> as-is, unfixed.
1951. 67
1952. 67 if (!result_autofix.stop) {
1953. 67 return {
1954. 67 autofixed,
1955. 67 ...result_autofix
1956. 67 };
1957. 67 }
1958. 67
1959. 67// Name the discarded fix in this pass's own report, or a fixer defect would
1960. 67// look exactly like an ordinary blocked file.
1961. 67
1962. 67 result_autofix.warnings.forEach(function (warning) {
1963. 67 if (warning.mode_stop) {
1964. 67 warning_list.push({
1965. 67 ...warning,
1966. 67 message: (
1967. 67 "[autofix discarded - line and column refer" +
1968. 67 " to the autofixed text] " + warning.message
1969. 67 )
1970. 67 });
1971. 67 }
1972. 67 });
1973. 67 }
1974. 846 }
1975. 846 if (option_dict.test_internal_error) {
1976. 5 jslint_assert(undefined, "test_internal_error");
1977. 841 }
1978. 841 if (option_dict.test_unknown_warning_code) {
1979. 2 warn_at("test_unknown_warning_code", jslint_fudge, 0);
1980. 2 }
1981. 181 } catch (err) {
1982. 181 mode_stop = true;
1983. 181 err.message = "[JSLint was unable to finish] " + err.message;
1984. 181 err.mode_stop = true;
1985. 181 if (err.name !== "JSLintError") {
1986. 181 err.column = jslint_fudge;
1987. 181 err.line = jslint_fudge;
1988. 181 err.line_source = "";
1989. 181 err.stack_trace = err.stack;
1990. 181 }
1991. 181 if (warning_list.indexOf(err) === -1) {
1992. 181 warning_list.push(err);
1993. 181 }
1994. 1020 }
1995. 1020
1996. 1020// Sort warning_list by mode_stop first, line, column respectively.
1997. 1020
1998. 1696 warning_list.sort(function (aa, bb) {
1999. 1696 return (
2000. 1592 Boolean(bb.mode_stop) - Boolean(aa.mode_stop) ||
2001. 1592 aa.line - bb.line ||
2002. 1560 aa.column - bb.column
2003. 1696 );
2004. 1696
2005. 1696// Update each warning with formatted_message ready-for-use by jslint_cli.
2006. 1696
2007. 1992 }).map(function ({
2008. 1992 column,
2009. 1992 line,
2010. 1992 line_source,
2011. 1992 message,
2012. 1992 stack_trace = ""
2013. 1992 }, ii, list) {
2014. 1992 list[ii].formatted_message = String(
2015. 1992 String(ii + 1).padStart(2, " ") +
2016. 1992 ". \u001b[31m" + message + "\u001b[39m" +
2017. 1992 " \u001b[90m\/\/ line " + line + ", column " + column +
2018. 1992 "\u001b[39m\n" +
2019. 1992 (" " + line_source.trim()).slice(0, 72) + "\n" +
2020. 1992 stack_trace
2021. 1992 ).trimEnd();
2022. 1992 });
2023. 1020
2024. 1020 return {
2025. 1020 directives: directive_list,
2026. 1020 edition: jslint_edition,
2027. 1020 exports: export_dict,
2028. 1020 froms: import_list,
2029. 1020 functions: function_list,
2030. 1020 global: token_global,
2031. 1020 id: "(JSLint)",
2032. 1020 json: state.mode_json,
2033. 1020 lines: line_list,
2034. 1020 module: state.mode_module === true,
2035. 1020 ok: warning_list.length === 0 && !mode_stop,
2036. 1050 option: option_dict,
2037. 1050 property: property_dict,
2038. 1050 shebang: (
2039. 1050 state.mode_shebang
2040. 2 ? line_list[jslint_fudge].line_source
2041. 1018 : undefined
2042. 1050 ),
2043. 1050 stop: mode_stop,
2044. 1050 tokens: token_list,
2045. 1050 tree: state.token_tree,
2046. 1050 warnings: warning_list
2047. 1050 };
2048. 1050}
2049. 23
2050. 23// PR-362 - Add API Doc.
2051. 23
2052. 1async function jslint_apidoc({
2053. 1 example_list,
2054. 1 github_repo,
2055. 1 module_list,
2056. 1 package_name,
2057. 1 pathname,
2058. 1 version
2059. 1}) {
2060. 1
2061. 1// This function will create API Doc from <module_list>.
2062. 1
2063. 1 let elem_ii = 0;
2064. 1 let html;
2065. 1
2066. 26 function elem_create(moduleObj, key, moduleName) {
2067. 26
2068. 26// This function will create a sub API Doc from elem <moduleObj>[<key>].
2069. 26
2070. 26 let example = "N/A";
2071. 26 let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
2072. 26 let name;
2073. 26 let signature;
2074. 26 let source;
2075. 26 name = htmlEscape((typeof moduleObj[key]) + " " + key);
2076. 2 if (typeof moduleObj[key] !== "function") {
2077. 2 return {
2078. 2 name,
2079. 2 signature: (`
2080. 2<a class="apidocElementLiA" href="#${id}">
2081. 2${name}
2082. 2</a>
2083. 2 `),
2084. 2 source: (`
2085. 2<li>
2086. 2 <h2>
2087. 2 <a href="#${id}" id="${id}">
2088. 2 ${name}
2089. 2 </a>
2090. 2 </h2>
2091. 2</li>
2092. 2 `)
2093. 2 };
2094. 24 }
2095. 24 // init source
2096. 24 source = htmlEscape(trim_start(moduleObj[key].toString()));
2097. 24 // init signature
2098. 24 source = source.replace((
2099. 24 /(\([\S\s]*?\)) \{/
2100. 24 ), function (match0, match1) {
2101. 24 signature = htmlEscape(
2102. 24 match1
2103. 24 .replace((/ *?\/\*[\S\s]*?\*\/ */g), "")
2104. 24 .replace((/ *?\/\/.*/g), "")
2105. 24 .replace((/\n{2,}/g), "\n")
2106. 24 );
2107. 24 return match0;
2108. 24 });
2109. 24 // init comment
2110. 24 source = source.replace((
2111. 24 /\n(?:\/\/.*?\n)+\n/
2112. 24 ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
2113. 24 // init example
2114. 54 example_list.some(function (example2) {
2115. 54 example2.replace(
2116. 54 new RegExp((
2117. 54 "((?:\\n.*?){8}(function )?)\\b" +
2118. 54 key +
2119. 54 "(\\((?:.*?\\n){8})"
2120. 54 ), "g"),
2121. 247 function (ignore, header, isDeclaration, footer) {
2122. 239 if (!isDeclaration) {
2123. 239 example = "..." + trim_start(
2124. 239 htmlEscape(header) +
2125. 239 "<span class=\"apidocCodeKeywordSpan\">" +
2126. 239 htmlEscape(key) +
2127. 239 "</span>" +
2128. 239 htmlEscape(footer)
2129. 239 ).trimEnd() + "\n...";
2130. 239 }
2131. 247 return "";
2132. 247 }
2133. 54 );
2134. 54 return example !== "N/A";
2135. 54 });
2136. 24 if (source.length > 2048) {
2137. 13 source = source.slice(0, 2048) + "...\n}\n";
2138. 24 }
2139. 24 return {
2140. 24 name,
2141. 24 signature: (`
2142. 24<a class="apidocElementLiA" href="#${id}">
2143. 24${name}<span class="apidocSignatureSpan">${signature}</span>
2144. 24</a>
2145. 24 `),
2146. 24 source: (`
2147. 24<li>
2148. 24 <h2>
2149. 24 <a href="#${id}" id="${id}">
2150. 24 ${name}<span class="apidocSignatureSpan">${signature}</span>
2151. 24 </a>
2152. 24 </h2>
2153. 24</li>
2154. 24<li>Description and source-code:<pre class="apidocCodePre">${source}</pre></li>
2155. 24<li>Example usage:<pre class="apidocCodePre">${example}</pre></li>
2156. 24 `)
2157. 24 };
2158. 24 }
2159. 1
2160. 263 function trim_start(str) {
2161. 263
2162. 263// This function will normalize whitespace before <str>.
2163. 263
2164. 263 let whitespace = "";
2165. 263 str.trim().replace((
2166. 263 /^ */gm
2167. 16476 ), function (match0) {
2168. 9991 if (whitespace === "" || match0.length < whitespace.length) {
2169. 9096 whitespace = match0;
2170. 9096 }
2171. 16476 return "";
2172. 16476 });
2173. 263 str = str.replace(new RegExp("^" + whitespace, "gm"), "");
2174. 263 return str;
2175. 263 }
2176. 1 await moduleFsInit();
2177. 1
2178. 1// Html-escape params.
2179. 1
2180. 1 github_repo = htmlEscape(github_repo);
2181. 1 package_name = htmlEscape(package_name);
2182. 1 version = htmlEscape(version);
2183. 1
2184. 1// Init example_list.
2185. 1
2186. 3 example_list = await Promise.all(example_list.map(async function (file) {
2187. 3
2188. 3// This function will read example from given file.
2189. 3
2190. 3 let result = await moduleFs.promises.readFile(file, "utf8");
2191. 3 result = (
2192. 3 "\n\n\n\n\n\n\n\n" +
2193. 3
2194. 3// bug-workaround - Truncate example to manageable size.
2195. 3
2196. 3 result.slice(0, 524288) +
2197. 3 "\n\n\n\n\n\n\n\n"
2198. 3 );
2199. 3 result = result.replace((
2200. 3 /\r\n?/g
2201. 3 ), "\n");
2202. 3 return result;
2203. 3 }));
2204. 1
2205. 1// Init module_list.
2206. 1
2207. 1 module_list = await Promise.all(module_list.map(async function ({
2208. 1 pathname
2209. 1 }) {
2210. 1 let moduleName = htmlEscape(JSON.stringify(pathname));
2211. 1 let moduleObj = await import(pathname);
2212. 1 if (moduleObj.default) {
2213. 1 moduleObj = moduleObj.default;
2214. 1 }
2215. 1 return {
2216. 26 elem_list: Object.keys(moduleObj).map(function (key) {
2217. 26 return elem_create(moduleObj, key, moduleName);
2218. 75 }).sort(function (aa, bb) {
2219. 75 return (
2220. 75 aa.name < bb.name
2221. 22 ? -1
2222. 53 : 1
2223. 75 );
2224. 26 }).map(function (elem) {
2225. 26 elem_ii += 1;
2226. 26
2227. 26// PR-503 - Fix codeql-warning - Incomplete string escaping or encoding.
2228. 26
2229. 26 elem.signature = elem.signature.replace(
2230. 26 (/<a[^>]*?>|">/),
2231. 26 `$&${elem_ii}. `
2232. 26 );
2233. 26 return elem;
2234. 26 }),
2235. 1 id: encodeURIComponent("apidoc.module." + moduleName),
2236. 1 moduleName
2237. 1 };
2238. 1 }));
2239. 1 html = (`
2240. 1<!DOCTYPE html>
2241. 1<html lang="en">
2242. 1<head>
2243. 1<meta charset="utf-8">
2244. 1<meta name="viewport" content="width=device-width, initial-scale=1">
2245. 1<meta name="description" content="${package_name} API Doc">
2246. 1<title>${package_name} apidoc</title>
2247. 1<style>
2248. 1/* jslint utility2:true */
2249. 1/*csslint*/
2250. 1body {
2251. 1 margin: 0;
2252. 1 padding: 20px;
2253. 1}
2254. 1.apidocCodeCommentSpan,
2255. 1.apidocCodeKeywordSpan {
2256. 1 background: royalblue;
2257. 1 color: white;
2258. 1}
2259. 1.apidocCodeCommentSpan {
2260. 1 display: block;
2261. 1}
2262. 1.apidocCodePre {
2263. 1 background: #eef;
2264. 1 border: 1px solid;
2265. 1 font-size: 14px;
2266. 1 overflow-wrap: break-word;
2267. 1 padding: 5px;
2268. 1 white-space: pre-wrap;
2269. 1}
2270. 1.apidocDiv {
2271. 1 color: #555;
2272. 1 font-family: sans-serif;
2273. 1}
2274. 1.apidocDiv a[href] {
2275. 1 color: royalblue;
2276. 1 text-decoration: none;
2277. 1}
2278. 1.apidocDiv a[href]:hover {
2279. 1 text-decoration: underline;
2280. 1}
2281. 1.apidocDiv li a {
2282. 1 display: inline-block;
2283. 1 padding: 8px 0;
2284. 1}
2285. 1.apidocDiv ul {
2286. 1 list-style: none;
2287. 1 padding-left: 20px;
2288. 1}
2289. 1.apidocFooterDiv {
2290. 1 margin-top: 20px;
2291. 1 text-align: center;
2292. 1}
2293. 1.apidocModuleA {
2294. 1 font-size: 24px;
2295. 1 font-weight: bold;
2296. 1}
2297. 1.apidocSectionDiv {
2298. 1 border-top: 1px solid;
2299. 1 margin-top: 20px;
2300. 1}
2301. 1.apidocSignatureSpan {
2302. 1 color: #666;
2303. 1 white-space: pre-wrap;
2304. 1}
2305. 1</style>
2306. 1</head>
2307. 1<body>
2308. 1<div class="apidocDiv">
2309. 1<h1>API Doc for <a href="${github_repo}">${package_name} (${version})</a></h1>
2310. 1<div class="apidocSectionDiv">
2311. 1 <a href="#apidocTableOfContents1" id="apidocTableOfContents1">
2312. 1 <h1>Table of Contents</h1>
2313. 1 </a>
2314. 1 <ul>
2315. 1 `) + module_list.map(function ({
2316. 1 elem_list,
2317. 1 id,
2318. 1 moduleName
2319. 1 }) {
2320. 1 return (
2321. 1 (`
2322. 1 <li>
2323. 1 <a class="apidocModuleA" href="#${id}">Module ${moduleName}</a>
2324. 1 <ul>
2325. 1 `) +
2326. 26 elem_list.map(function ({
2327. 26 signature
2328. 26 }) {
2329. 26 return "<li>\n" + signature + "\n</li>\n";
2330. 26 }).join("") +
2331. 1 (`
2332. 1 </ul>
2333. 1 </li>
2334. 1 `)
2335. 1 );
2336. 1 }).join("") + (`
2337. 1 </ul>
2338. 1</div>
2339. 1 `) + module_list.map(function ({
2340. 1 elem_list,
2341. 1 id,
2342. 1 moduleName
2343. 1 }) {
2344. 1 return (
2345. 1 (`
2346. 1<div class="apidocSectionDiv">
2347. 1 <h1><a href="#${id}" id="${id}">Module ${moduleName}</a></h1>
2348. 1 <ul>
2349. 1 `) +
2350. 26 elem_list.map(function ({
2351. 26 source
2352. 26 }) {
2353. 26 return source;
2354. 26 }).join("") +
2355. 1 (`
2356. 1 </ul>
2357. 1</div>
2358. 1 `)
2359. 1 );
2360. 1 }).join("") + (`
2361. 1<div class="apidocFooterDiv">
2362. 1 [
2363. 1 This document was created with
2364. 1 <a href="https://github.com/jslint-org/jslint">JSLint</a>
2365. 1 ]
2366. 1</div>
2367. 1</div>
2368. 1</body>
2369. 1</html>
2370. 1 `);
2371. 1
2372. 1// PR-503 - Fix codeql-warning - Polynomial regular expression used on
2373. 1// uncontrolled data.
2374. 1
2375. 1 html = html.trim().replace((/(?<! ) +$/gm), "") + "\n";
2376. 1 await fsWriteFileWithParents(pathname, html);
2377. 1}
2378. 23
2379. 139865function jslint_assert(condition, message) {
2380. 139865
2381. 139865// This function will throw <message> if <condition> is falsy.
2382. 139865
2383. 139865 let error;
2384. 139858 if (condition) {
2385. 139858 return;
2386. 139858 }
2387. 7 error = new Error(
2388. 7 `This was caused by a bug in JSLint.
2389. 7Please open an issue with this stack-trace (and possible example-code) at
2390. 7https://github.com/jslint-org/jslint/issues.
2391. 7edition = "${jslint_edition}";
2392. 7${String(message).slice(0, 2000)}`
2393. 7 );
2394. 7 switch (message) {
2395. 7 case "test_internal_error":
2396. 7 case "unknown_warning_code=test_unknown_warning_code":
2397. 7 error = {};
2398. 7 break;
2399. 7 }
2400. 7 console.error(error);
2401. 7 throw error;
2402. 7}
2403. 23
2404. 79async function jslint_cli({
2405. 79 console_error,
2406. 79 console_log,
2407. 79 file,
2408. 79 import_meta_url,
2409. 79 mode_cli,
2410. 79 mode_noop,
2411. 79 option,
2412. 79 process_argv,
2413. 79 process_env,
2414. 79 process_exit,
2415. 79 source
2416. 79}) {
2417. 79
2418. 79// This function will run jslint from nodejs-cli.
2419. 79
2420. 79 let command;
2421. 79 let data;
2422. 79 let exit_code = 0;
2423. 79 let mode_autofix;
2424. 79 let mode_report;
2425. 79 let mode_wrapper_vim;
2426. 79 let result;
2427. 79
2428. 79// PR-509 - Add command jslint_autofix.
2429. 79
2430. 14 function autofix_embedded({
2431. 14 code,
2432. 14 file,
2433. 14 mode_conditional,
2434. 14 option,
2435. 14 rgx,
2436. 14 suffix,
2437. 14 suffix_file
2438. 14 }) {
2439. 14
2440. 14// This function will jslint each embedded block <rgx> matches, and return
2441. 14// <code> with every autofixed block spliced back between the SAME delimiters
2442. 14// - or undefined if no block was autofixed. <suffix> is the closing
2443. 14// delimiter, which the capture deliberately excludes.
2444. 14//
2445. 14// A BLOCKED BLOCK IS LEFT AS IT WAS AND THE REST ARE STILL REPAIRED, unlike a
2446. 14// blocked whole-file, which is not written at all.
2447. 14
2448. 31 const code_fixed = code.replace(rgx, function (match0, match1, ii) {
2449. 31 const result_embedded = jslint_from_file({
2450. 31 code: match1,
2451. 31 file: file + suffix_file,
2452. 31 line_offset: (
2453. 31 jslint_fudge +
2454. 31
2455. 31// Count line-terminators the way <jslint_rgx_crlf> splits them, without
2456. 31// materialising every preceding line.
2457. 31
2458. 31 (
2459. 31 code
2460. 31 .slice(0, ii)
2461. 31 .match(new RegExp(jslint_rgx_crlf, "g"))
2462. 30 ?.length ||
2463. 1 0
2464. 31 )
2465. 31 ),
2466. 31 mode_conditional,
2467. 31 option
2468. 31 });
2469. 31 if (
2470. 29 result_embedded === undefined ||
2471. 29 result_embedded.autofixed === undefined
2472. 25 ) {
2473. 25 return match0;
2474. 25 }
2475. 6 return (
2476. 6 match0.slice(
2477. 6 0,
2478. 6 match0.length - match1.length - suffix.length
2479. 6 ) +
2480. 6 result_embedded.autofixed +
2481. 6 suffix
2482. 6 );
2483. 6 });
2484. 14 return (
2485. 14 code_fixed === code
2486. 8 ? undefined
2487. 6 : code_fixed
2488. 14 );
2489. 14 }
2490. 79
2491. 70 function jslint_from_file({
2492. 70 code,
2493. 70 file,
2494. 70 line_offset = 0,
2495. 70 mode_conditional,
2496. 70 option = empty()
2497. 70 }) {
2498. 70
2499. 70// PR-513 - Pad with the block's OWN terminator. Phase 6 rejoins with the
2500. 70// first one it sees, so a "\n" pad turned a CRLF block's fixed lines into LF.
2501. 70
2502. 70 const line_pad = String(
2503. 63 jslint_rgx_crlf.exec(code)?.[0] || "\n"
2504. 70 ).repeat(line_offset);
2505. 70 let result_from_file;
2506. 70 if (
2507. 8 mode_conditional &&
2508. 8 !(
2509. 8 /^\/\*jslint\b/m
2510. 8 ).test(code.slice(0, 65536))
2511. 2 ) {
2512. 2 return;
2513. 68 }
2514. 68 option = {
2515. 68 ...option,
2516. 68 file
2517. 68 };
2518. 68 switch ((
2519. 68 /\.\w+?$|$/m
2520. 68 ).exec(file)[0]) {
2521. 68 case ".html":
2522. 6
2523. 6// Recursively jslint embedded "<script>\n...\n</script>".
2524. 6
2525. 6 return {
2526. 6 autofixed: autofix_embedded({
2527. 6 code,
2528. 6 file,
2529. 6 option: {
2530. 6 browser: true,
2531. 6 ...option
2532. 6 },
2533. 6 rgx: (
2534. 6 /^<script\b[^>]*?>(?:\n|\r\n?)([\S\s]*?(?:\n|\r\n?))<\/script>$/gm
2535. 6 ),
2536. 6 suffix: "</script>",
2537. 6 suffix_file: ".<script>.js"
2538. 6 })
2539. 6 };
2540. 4 case ".md":
2541. 4
2542. 4// Recursively jslint embedded "node --eval '\n...\n'".
2543. 4
2544. 4 return jslint_node_eval({
2545. 4 code,
2546. 4 file,
2547. 4 mode_conditional: true,
2548. 4 option
2549. 4 });
2550. 4 case ".sh":
2551. 4
2552. 4// Recursively jslint embedded "node --eval '\n...\n'".
2553. 4
2554. 4 return jslint_node_eval({
2555. 4 code,
2556. 4 file,
2557. 4 option
2558. 4 });
2559. 54 default:
2560. 54 result_from_file = jslint(line_pad + code, option);
2561. 54
2562. 54// The <line_pad> terminators prefixed above make the warnings absolute, and
2563. 54// they ride along in <autofixed> too. Strip exactly them back off, so the
2564. 54// caller splices the block back between its OWN delimiters.
2565. 54
2566. 54 if (line_offset > 0 && result_from_file.autofixed !== undefined) {
2567. 54 result_from_file.autofixed = (
2568. 54 result_from_file.autofixed.slice(line_pad.length)
2569. 54 );
2570. 54 }
2571. 54 }
2572. 54
2573. 54// Print only first 10 warnings to stderr.
2574. 54
2575. 54 if (result_from_file.warnings.length > 0) {
2576. 10 exit_code = 1;
2577. 10 console_error(
2578. 10 mode_wrapper_vim
2579. 10
2580. 10// PR-349 - Print warnings in format readable by vim.
2581. 10
2582. 10 ? result_from_file.warnings.slice(0, 10).map(function ({
2583. 10 column,
2584. 10 line,
2585. 10 message
2586. 10 }, ii) {
2587. 10 return (
2588. 10 file +
2589. 10 ":" + ii +
2590. 10 ":" + line +
2591. 10 ":" + column +
2592. 10 ":" + message
2593. 10 );
2594. 10 }).join("\n")
2595. 10
2596. 10// Print warnings in format readable by human.
2597. 10
2598. 10 : "\u001b[1mjslint " + file + "\u001b[22m\n" +
2599. 19 result_from_file.warnings.slice(0, 10).map(function ({
2600. 19 formatted_message
2601. 19 }) {
2602. 19 return formatted_message;
2603. 19 }).join("\n")
2604. 10 );
2605. 54 }
2606. 54 return result_from_file;
2607. 54 }
2608. 79
2609. 8 function jslint_node_eval({
2610. 8 code,
2611. 8 file,
2612. 8 mode_conditional,
2613. 8 option = empty()
2614. 8 }) {
2615. 8 return {
2616. 8 autofixed: autofix_embedded({
2617. 8 code,
2618. 8 file,
2619. 8 mode_conditional,
2620. 8 option: {
2621. 8 beta: Boolean(
2622. 6 process_env.JSLINT_BETA &&
2623. 6 !(
2624. 6 /0|false|null|undefined/
2625. 6 ).test(process_env.JSLINT_BETA)
2626. 8 ),
2627. 8 node: true,
2628. 8 ...option
2629. 8 },
2630. 8 rgx: (
2631. 8 /\bnode\b.*? (?:--eval|-e) '(?:\n|\r\n?)([\S\s]*?(?:\n|\r\n?))'/gm
2632. 8 ),
2633. 8 suffix: "'",
2634. 8 suffix_file: ".<node -e>.js"
2635. 8 })
2636. 8 };
2637. 8 }
2638. 79
2639. 79// PR-396 - window.jslint
2640. 79// Check import.meta.url for directive to export jslint to window-object.
2641. 79// Useful for ES5-era browser-scripts that rely on window.jslint,
2642. 79// like CodeMirror.
2643. 79//
2644. 79// Example usage:
2645. 79// <script type="module" src="./jslint.mjs?window_jslint=1"></script>
2646. 79
2647. 63 import_meta_url = import_meta_url || jslint_import_meta_url;
2648. 79 if (
2649. 4 jslint_rgx_url_search_window_jslint.test(import_meta_url) &&
2650. 4 (typeof globalThis === "object" && globalThis)
2651. 4 ) {
2652. 4 globalThis.jslint = jslint;
2653. 4 }
2654. 79
2655. 79// Feature-detect nodejs.
2656. 79
2657. 79 if (!(
2658. 79 (typeof process === "object" && process) &&
2659. 79 process.versions &&
2660. 79 typeof process.versions.node === "string" &&
2661. 79 !mode_noop
2662. 1 )) {
2663. 1 return exit_code;
2664. 78 }
2665. 78 console_error = console_error || console.error;
2666. 78 console_log = console_log || console.log;
2667. 45 process_argv = process_argv || process.argv;
2668. 71 process_env = process_env || process.env;
2669. 47 process_exit = process_exit || process.exit;
2670. 77 await moduleFsInit();
2671. 77 if (
2672. 77 !(
2673. 77
2674. 77// Feature-detect nodejs-cli.
2675. 77
2676. 77 process.execArgv.indexOf("--eval") === -1 &&
2677. 77 process.execArgv.indexOf("-e") === -1 &&
2678. 56 (
2679. 56 (
2680. 56 /[\/|\\]jslint(?:\.[cm]?js)?$/m
2681. 56 ).test(process_argv[1]) ||
2682. 56 mode_cli
2683. 56 ) &&
2684. 39 (
2685. 39 moduleUrl.fileURLToPath(import_meta_url) ===
2686. 39 modulePath.resolve(process_argv[1])
2687. 39 )
2688. 44 ) &&
2689. 44 !mode_cli
2690. 78 ) {
2691. 78 return exit_code;
2692. 78 }
2693. 78
2694. 78// init commmand
2695. 78
2696. 78 command = String(process_argv[2]).split("=");
2697. 78 command[1] = command.slice(1).join("=");
2698. 78
2699. 78 switch (command[0]) {
2700. 78
2701. 78// PR-362 - Add API Doc.
2702. 78
2703. 78 case "jslint_apidoc":
2704. 78 await jslint_apidoc({
2705. 78 ...JSON.parse(process_argv[3]),
2706. 78 pathname: command[1]
2707. 78 });
2708. 78 return;
2709. 79
2710. 79// PR-509 - Add command jslint_autofix.
2711. 79
2712. 15 case "jslint_autofix":
2713. 15 file = command[1];
2714. 15 mode_autofix = true;
2715. 15 option = {
2716. 15 ...option,
2717. 15 autofix: true
2718. 15 };
2719. 15 break;
2720. 79
2721. 79// PR-363 - Add command jslint_report.
2722. 79
2723. 7 case "jslint_report":
2724. 7 mode_report = command[1];
2725. 7 process_argv = process_argv.slice(1);
2726. 7 break;
2727. 79
2728. 79// COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
2729. 79
2730. 1 case "jslint_wrapper_vim":
2731. 1 mode_wrapper_vim = true;
2732. 1 process_argv = process_argv.slice(1);
2733. 1 break;
2734. 79
2735. 79// PR-364 - Add command v8_coverage_report.
2736. 79
2737. 8 case "v8_coverage_report":
2738. 8 await v8CoverageReportCreate({
2739. 8 consoleError: console_error,
2740. 8 coverageDir: command[1],
2741. 8 processArgv: process_argv.slice(3)
2742. 8 });
2743. 8 return;
2744. 57 }
2745. 57
2746. 57// Normalize file relative to process.cwd().
2747. 57
2748. 57 process_argv.slice(2).some(function (arg) {
2749. 57 if (!arg.startsWith("-")) {
2750. 57 file = file || arg;
2751. 57 return true;
2752. 57 }
2753. 57 });
2754. 57 if (!file) {
2755. 57 return;
2756. 57 }
2757. 57 file = modulePath.resolve(file) + "/";
2758. 57 if (file.startsWith(process.cwd() + "/")) {
2759. 57 file = file.replace(process.cwd() + "/", "").slice(0, -1) || ".";
2760. 57 }
2761. 57 file = file.replace((
2762. 57 /\\/g
2763. 57 ), "/").replace((
2764. 57 /\/$/g
2765. 57 ), "");
2766. 57 if (source) {
2767. 57 data = source;
2768. 57 } else {
2769. 57
2770. 57// jslint_cli - jslint directory.
2771. 57
2772. 57 try {
2773. 57 data = await moduleFs.promises.readdir(file, "utf8");
2774. 57 } catch (ignore) {}
2775. 57 if (data) {
2776. 57 await Promise.all(data.map(async function (file2) {
2777. 57 let code;
2778. 57 let result_dir;
2779. 57 let time_start = Date.now();
2780. 57
2781. 57// PR-409 - Skip dotfile (e.g. .foo.js) when reading directory.
2782. 57
2783. 57 if ((/^\./).test(file2)) {
2784. 57 return;
2785. 57 }
2786. 57 file2 = file + "/" + file2;
2787. 57 switch ((/\.\w+?$|$/m).exec(file2)[0]) {
2788. 57 case ".cjs":
2789. 57 case ".html":
2790. 57 case ".js":
2791. 57 case ".json":
2792. 57 case ".md":
2793. 57 case ".mjs":
2794. 57 case ".sh":
2795. 57 break;
2796. 57 default:
2797. 57 return;
2798. 57 }
2799. 57 try {
2800. 57 code = await moduleFs.promises.readFile(file2, "utf8");
2801. 57 } catch (ignore) {
2802. 57 return;
2803. 57 }
2804. 57 if (
2805. 57 (/(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/).test(file2) ||
2806. 57 !(code && code.length < 1048576)
2807. 57 ) {
2808. 57 return;
2809. 57 }
2810. 57 result_dir = jslint_from_file({
2811. 57 code,
2812. 57 file: file2,
2813. 57 option
2814. 57 });
2815. 57 if (mode_autofix && result_dir.autofixed !== undefined) {
2816. 57 await fsWriteFileWithParents(file2, result_dir.autofixed);
2817. 57 }
2818. 57 console_error(
2819. 57 "jslint - " + (Date.now() - time_start) + "ms - " + file2
2820. 57 );
2821. 57 }));
2822. 57 process_exit(exit_code);
2823. 57 return exit_code;
2824. 57 }
2825. 57
2826. 57// jslint_cli - jslint file.
2827. 57
2828. 57 try {
2829. 57 data = await moduleFs.promises.readFile(file, "utf8");
2830. 57 } catch (err) {
2831. 57 console_error(err);
2832. 57 exit_code = 1;
2833. 57 process_exit(exit_code);
2834. 57 return exit_code;
2835. 57 }
2836. 57 }
2837. 57 result = jslint_from_file({
2838. 57 code: data,
2839. 57 file,
2840. 57 option
2841. 57 });
2842. 57 if (mode_autofix && result.autofixed !== undefined) {
2843. 57 await fsWriteFileWithParents(file, result.autofixed);
2844. 57 }
2845. 57 if (mode_report) {
2846. 57
2847. 57// A container-file - .html, .md, .sh - lints its embedded blocks one by one
2848. 57// and returns no single lint-result, so there is nothing to report on.
2849. 57
2850. 57 if (result.warnings === undefined) {
2851. 57 console_error(`jslint_report - ${file} - not javascript file`);
2852. 57 exit_code = 1;
2853. 57 process_exit(exit_code);
2854. 57 return exit_code;
2855. 57 }
2856. 57 result = jslint.jslint_report(result);
2857. 57 result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
2858. 57 await fsWriteFileWithParents(mode_report, result);
2859. 57 }
2860. 57 process_exit(exit_code);
2861. 57 return exit_code;
2862. 57}
2863. 23
2864. 1050function jslint_phase1_split() {
2865. 1050
2866. 1050// PHASE 1. Split <source> by newlines into <line_list>.
2867. 1050
2868. 1050 return;
2869. 1050}
2870. 23
2871. 1050function jslint_phase2_lex(state) {
2872. 1050
2873. 1050// PHASE 2. Lex <line_list> into <token_list>.
2874. 1050
2875. 1050 const {
2876. 1050 artifact,
2877. 1050 directive_list,
2878. 1050 global_dict,
2879. 1050 global_list,
2880. 1050 line_list,
2881. 1050 option_dict,
2882. 1050 stop,
2883. 1050 stop_at,
2884. 1050 tenure,
2885. 1050 token_global,
2886. 1050 token_list,
2887. 1050 warn,
2888. 1050 warn_at,
2889. 1050 warning_list
2890. 1050 } = state;
2891. 1050 const mode_digits_numeric_separator = 1;
2892. 1050 const mode_digits_regexp_quantifier = 2;
2893. 1050 const mode_digits_unicode_escape = 3;
2894. 1050 const opener_stack = []; // Stack of opener tokens: (, [, {, ${.
2895. 1050 let char; // The current character being lexed.
2896. 1050 let column = 0; // The column number of the next character.
2897. 1050 let from; // The starting column number of the token.
2898. 1050 let from_mega; // The starting column of megastring.
2899. 1050 let line = 0; // The line number of the next character.
2900. 1050 let line_disable; // The starting line of "/*jslint-disable*/".
2901. 1050 let line_mega; // The starting line of megastring.
2902. 1050 let line_source = ""; // The remaining line source string.
2903. 1050 let line_whole = ""; // The whole line source string.
2904. 1050 let mode_directive = true; // true if directives are still allowed.
2905. 1050 let mode_mega = false; // true if currently parsing a megastring
2906. 1050 // ... literal.
2907. 1050 let mode_regexp; // true if regular expression literal seen on
2908. 1050 // ... this line.
2909. 1050 let opener_popped = empty(); // Last token popped from opener_stack.
2910. 1050 let snippet = ""; // A piece of string.
2911. 1050 let token_1; // The first token.
2912. 1050 let token_prv = token_global; // The previous token including
2913. 1050 // ... comments.
2914. 1050 let token_prv_expr = token_global; // The previous token excluding
2915. 1050 // ... comments.
2916. 1050
2917. 1050// Most tokens, including the identifiers, operators, and punctuators, can be
2918. 1050// found with a regular expression. Regular expressions cannot correctly match
2919. 1050// regular expression literals, so we will match those the hard way. String
2920. 1050// literals and number literals can be matched by regular expressions, but they
2921. 1050// don't provide good warnings. The functions char_after, char_before,
2922. 1050// read_digits, and char_after_escape help in the parsing of literals.
2923. 1050
2924. 248655 function char_after(match) {
2925. 248655
2926. 248655// Get the next character from the source line. Remove it from the line_source,
2927. 248655// and append it to the snippet. Optionally check that the previous character
2928. 248655// matched an expected value.
2929. 248655
2930. 7672 if (match !== undefined && char !== match) {
2931. 14 return (
2932. 14 char === ""
2933. 14
2934. 14// test_cause:
2935. 14// ["aa=/", "char_after", "expected_a_after_b", "/", 4]
2936. 14// ["aa=/(?-", "char_after", "expected_a_after_b", "-", 7]
2937. 14// ["aa=/[", "char_after", "expected_a_after_b", "[", 5]
2938. 14
2939. 14 ? stop_at(
2940. 14 "expected_a_after_b",
2941. 14 line,
2942. 14 column - 1,
2943. 14 match,
2944. 14
2945. 14// At EOL, <snippet> may have been reset or trimmed, so use <line_list[line]>.
2946. 14
2947. 14 line_list[line].line_source.slice(-1)
2948. 14 )
2949. 14
2950. 14// test_cause:
2951. 14// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8]
2952. 14// ["aa=/aa{/;", "char_after", "expected_a_b", "/", 8]
2953. 14
2954. 14 : stop_at("expected_a_b", line, column - 1, match, char)
2955. 14 );
2956. 248641 }
2957. 248641 char = line_source.slice(0, 1);
2958. 248641 snippet += char;
2959. 248641 column += char.length;
2960. 248641 line_source = line_source.slice(char.length);
2961. 248641 return char;
2962. 248641 }
2963. 1050
2964. 4248 function char_after_escape(escape_char_list) {
2965. 4248
2966. 4248// Validate char after escape "\\". <escape_char_list> lists the chars this
2967. 4248// caller may escape beyond the shared '/\`bfnrtu', such as a string's quote, a
2968. 4248// megastring's '${', or a regexp's metachars.
2969. 4248
2970. 4248 char_after("\\");
2971. 4248 switch (char) {
2972. 682 case "":
2973. 682 if (escape_char_list === "${") {
2974. 682
2975. 682// A megastring's '\' at end of line continues the line.
2976. 682
2977. 682// test_cause:
2978. 682// ["
2979. 682// `\
2980. 682// `
2981. 682// ", "char_after_escape", "(live)", "", 0]
2982. 682
2983. 682 return;
2984. 682 }
2985. 682
2986. 682// test_cause:
2987. 682// ["\"\\", "char_after_escape", "unclosed_string", "", 2]
2988. 682
2989. 682 return stop_at("unclosed_string", line, column - 0);
2990. 225 case "/":
2991. 225 return char_after();
2992. 384 case "\\":
2993. 384 return char_after();
2994. 30 case "`":
2995. 30 return char_after();
2996. 62 case "b":
2997. 62 return char_after();
2998. 7 case "f":
2999. 7 return char_after();
3000. 1343 case "n":
3001. 1343 return char_after();
3002. 104 case "r":
3003. 104 return char_after();
3004. 31 case "t":
3005. 31
3006. 31// test_cause:
3007. 31// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "(live)", "", 0]
3008. 31// ["`\\/\\\\\\`\\b\\f\\n\\r\\t`", "char_after_escape", "(live)", "", 0]
3009. 31
3010. 31 return char_after();
3011. 412 case "u":
3012. 412 if (char_after("u") === "{") {
3013. 412 if (state.mode_json) {
3014. 412
3015. 412// test_cause:
3016. 412// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5]
3017. 412
3018. 412 warn_at("unexpected_a", line, column - 1, char);
3019. 412 }
3020. 412 read_digits("x", mode_digits_unicode_escape);
3021. 412 if (char !== "}") {
3022. 412
3023. 412// test_cause:
3024. 412// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10]
3025. 412// ["\"\\u{12345\";", "char_after_escape", "expected_a_before_b", "\"", 10]
3026. 412// ["\"\\u{12\"", "char_after_escape", "expected_a_before_b", "\"", 7]
3027. 412// ["`\\u{12345`", "char_after_escape", "expected_a_before_b", "`", 10]
3028. 412// ["`\\u{12345`;", "char_after_escape", "expected_a_before_b", "`", 10]
3029. 412// ["`\\u{12`", "char_after_escape", "expected_a_before_b", "`", 7]
3030. 412
3031. 412 warn_at(
3032. 412 "expected_a_before_b",
3033. 412 line,
3034. 412 column - 1,
3035. 412 "}",
3036. 412 char
3037. 412 );
3038. 412 return;
3039. 412 }
3040. 412 return char_after();
3041. 412 }
3042. 412 char_before();
3043. 412 read_digits("x", mode_digits_unicode_escape);
3044. 412 return;
3045. 968 default:
3046. 968 if (escape_char_list && escape_char_list.indexOf(char) >= 0) {
3047. 968 return char_after();
3048. 968 }
3049. 968
3050. 968// test_cause:
3051. 968// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3]
3052. 968// ["`\\0`", "char_after_escape", "unexpected_a_before_b", "0", 3]
3053. 968
3054. 968 warn_at("unexpected_a_before_b", line, column - 1, "\\", char);
3055. 4248 }
3056. 4248 }
3057. 1050
3058. 11772 function char_before() {
3059. 11772
3060. 11772// Back up one character by moving a character from the end of the snippet to
3061. 11772// the front of the line_source.
3062. 11772
3063. 11772 line_source = char + line_source;
3064. 11772 column -= char.length;
3065. 11772
3066. 11772// Remove last character from snippet. At end of line <char> is "" and nothing
3067. 11772// was consumed, so nothing comes off column or snippet.
3068. 11772
3069. 11772 snippet = snippet.slice(0, snippet.length - char.length);
3070. 11772 return char;
3071. 11772 }
3072. 1050
3073. 9782 function check_numeric_separator(digits, column) {
3074. 9782
3075. 9782// This function will check for illegal numeric-separator in <digits>.
3076. 9782
3077. 9782 digits.replace((
3078. 9782 jslint_rgx_numeric_separator_illegal
3079. 6 ), function (ignore, ii) {
3080. 6
3081. 6// test_cause:
3082. 6// ["0x0_0_", "check_numeric_separator", "illegal_num_separator", "", 6]
3083. 6// ["0x0_0__0", "check_numeric_separator", "illegal_num_separator", "", 6]
3084. 6// ["aa=1_2_", "check_numeric_separator", "illegal_num_separator", "", 7]
3085. 6// ["aa=1_2__3", "check_numeric_separator", "illegal_num_separator", "", 7]
3086. 6// ["aa=1_2_n", "check_numeric_separator", "illegal_num_separator", "", 7]
3087. 6
3088. 6 warn_at("illegal_num_separator", line, column - 0 + ii);
3089. 6 return "";
3090. 6 });
3091. 9782 }
3092. 1050
3093. 15676 function lex_comment() {
3094. 15676 let body;
3095. 15676 let ii = 0;
3096. 15676 let jj = 0;
3097. 15676 let the_comment;
3098. 15676
3099. 15676// Create a comment object. Comments are not allowed in JSON text. Comments can
3100. 15676// include directives and notices of incompletion.
3101. 15676
3102. 15676// Create token from comment //....
3103. 15676
3104. 15551 if (snippet === "//") {
3105. 15551 snippet = line_source;
3106. 15551 line_source = "";
3107. 15551 the_comment = token_create("(comment)", snippet);
3108. 15551 if (mode_mega) {
3109. 15551
3110. 15551// test_cause:
3111. 15551// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
3112. 15551
3113. 15551 warn("unexpected_comment", the_comment, "`");
3114. 15551 }
3115. 15551
3116. 15551// Create token from comment /*...*/.
3117. 15551
3118. 15551 } else {
3119. 125 snippet = [];
3120. 125 if (line_source[0] === "/") {
3121. 125
3122. 125// test_cause:
3123. 125// ["/*/", "lex_comment", "unexpected_a", "/", 3]
3124. 125// ["/*/*", "lex_comment", "unexpected_a", "/", 3]
3125. 125
3126. 125 warn_at("unexpected_a", line, column - 0, "/");
3127. 125 }
3128. 125
3129. 125// Lex/loop through each line until "*/".
3130. 125
3131. 1873 while (true) {
3132. 1873 // jslint_rgx_star_slash
3133. 1873 ii = line_source.indexOf("*/");
3134. 1873 if (ii >= 0) {
3135. 1873 break;
3136. 1873 }
3137. 1873 // jslint_rgx_slash_star
3138. 1873 ii = line_source.indexOf("/*");
3139. 1873 if (ii >= 0) {
3140. 1873
3141. 1873// test_cause:
3142. 1873// ["/*/*", "lex_comment", "nested_comment", "", 3]
3143. 1873
3144. 1873 warn_at("nested_comment", line, column - 0 + ii);
3145. 1873 }
3146. 1873 snippet.push(line_source);
3147. 1873 line_source = read_line();
3148. 1873 if (line_source === undefined) {
3149. 1873
3150. 1873// test_cause:
3151. 1873// ["/*", "lex_comment", "unclosed_comment", "", 2]
3152. 1873
3153. 1873 return stop_at("unclosed_comment", line, column - 0);
3154. 1873 }
3155. 1873 }
3156. 125 jj = line_source.slice(0, ii).search(
3157. 125 jslint_rgx_slash_star_or_slash
3158. 125 );
3159. 125 if (jj >= 0) {
3160. 125
3161. 125// test_cause:
3162. 125// ["/*/**/", "lex_comment", "nested_comment", "", 3]
3163. 125
3164. 125 warn_at("nested_comment", line, column - 0 + jj);
3165. 125 }
3166. 125 snippet.push(line_source.slice(0, ii));
3167. 125 snippet = snippet.join(" ");
3168. 125 column += ii + 2;
3169. 125 line_source = line_source.slice(ii + 2);
3170. 125 the_comment = token_create("(comment)", snippet);
3171. 15672 }
3172. 15672
3173. 15672// Uncompleted work comment.
3174. 15672
3175. 15672 if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
3176. 22
3177. 22// test_cause:
3178. 22// [" //todo", "lex_comment", "todo_comment", "(comment)", 2] //jslint-ignore-line
3179. 22// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line
3180. 22
3181. 22 warn("todo_comment", the_comment);
3182. 15672 }
3183. 15672
3184. 15672// Lex directives in comment.
3185. 15672
3186. 15672 snippet.replace(jslint_rgx_directive, function (
3187. 15672 ignore,
3188. 15672 match1,
3189. 15672 match2
3190. 15672 ) {
3191. 15672 the_comment.directive = match1;
3192. 15672 body = match2;
3193. 15672 });
3194. 15672 if (the_comment.directive === undefined) {
3195. 15578 return the_comment;
3196. 15578 }
3197. 94 directive_list.push(the_comment);
3198. 94 if (!mode_directive) {
3199. 2
3200. 2// test_cause:
3201. 2// ["0; /*global aa*/", "lex_comment", "misplaced_directive_a", "global", 4]
3202. 2// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1]
3203. 2
3204. 2 warn_at("misplaced_directive_a", line, from, the_comment.directive);
3205. 2 return the_comment;
3206. 92 }
3207. 92
3208. 92// lex_directive();
3209. 92// JSLint recognizes three directives that can be encoded in comments. This
3210. 92// function processes one item, and calls itself recursively to process the
3211. 92// next one.
3212. 92
3213. 92// Lex/loop through each directive in /*...*/
3214. 92
3215. 92 ii = 0;
3216. 2034 body.replace(jslint_rgx_directive_part, function (
3217. 2034 match0,
3218. 2034 key,
3219. 2034 value,
3220. 2034 jj
3221. 2034 ) {
3222. 92 if (ii !== jj) {
3223. 92
3224. 92// test_cause:
3225. 92// [" /*jslint !*/", "lex_comment", "bad_directive_a", "!", 2]
3226. 92// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1]
3227. 92
3228. 92 warn("bad_directive_a", the_comment, body.slice(ii));
3229. 92 }
3230. 92 if (match0 === "") {
3231. 92 return "";
3232. 1942 }
3233. 1942 ii += match0.length;
3234. 1942 switch (the_comment.directive) {
3235. 1942 case "global":
3236. 92 if (value) {
3237. 92
3238. 92// test_cause:
3239. 92// [" /*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 2]
3240. 92// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1]
3241. 92
3242. 92 warn("bad_option_a", the_comment, key + ":" + value);
3243. 92 }
3244. 92 global_dict[key] = "user-defined";
3245. 92
3246. 92// PR-347 - Disable warning "unexpected_directive_a".
3247. 92//
3248. 92// state.mode_module = the_comment;
3249. 92
3250. 92 break;
3251. 118 case "jslint":
3252. 118 if (!option_set_item(key, value !== "false")) {
3253. 118
3254. 118// test_cause:
3255. 118// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1]
3256. 118
3257. 118 warn("bad_option_a", the_comment, key);
3258. 118 }
3259. 118 break;
3260. 1815 case "property":
3261. 1815 state.mode_property = true;
3262. 1815 tenure[key] = true;
3263. 1815 break;
3264. 1942 }
3265. 1942 return "";
3266. 1942 });
3267. 92 return the_comment;
3268. 92 }
3269. 1050
3270. 930 function lex_megastring() {
3271. 930 const warning_list_untagged = [];
3272. 930 let id;
3273. 930 let ii;
3274. 930 let match;
3275. 930
3276. 930// The token is a megastring. We don't allow any kind of mega nesting.
3277. 930
3278. 2 if (mode_mega) {
3279. 2
3280. 2// test_cause:
3281. 2// ["`${`", "lex_megastring", "expected_a_b", "`", 4]
3282. 2// ["`${`;", "lex_megastring", "expected_a_b", "`", 4]
3283. 2
3284. 2 return stop_at("expected_a_b", line, column - 1, "}", "`");
3285. 928 }
3286. 928 from_mega = from;
3287. 928 line_mega = line;
3288. 928 mode_mega = true;
3289. 928 snippet = "";
3290. 928
3291. 928// Parsing a mega literal is tricky. First create a ` token.
3292. 928
3293. 928 token_create("`").warning_list_untagged = warning_list_untagged;
3294. 928 from += 1;
3295. 928
3296. 928// Then loop, building up a string, possibly from many lines, until seeing
3297. 928// the end of file, a closing `, or a ${ indicting an expression within the
3298. 928// string.
3299. 928
3300. 7283 while (true) {
3301. 7283 match = line_source.match(jslint_rgx_mega) || {
3302. 7283 "0": "",
3303. 7283 index: 0
3304. 7283 };
3305. 7283 snippet += line_source.slice(0, match.index);
3306. 7283 column += match.index;
3307. 7283 line_source = line_source.slice(match.index);
3308. 7283 match = match[0];
3309. 7283 switch (match) {
3310. 7283 case "${":
3311. 7283
3312. 7283// if either ` or ${ was found, then the preceding joins the snippet to become
3313. 7283// a string token.
3314. 7283
3315. 7283 token_create("(string)", snippet).quote = "`";
3316. 7283 snippet = "";
3317. 7283
3318. 7283// If ${, then create tokens that will become part of an expression until
3319. 7283// a } token is made.
3320. 7283
3321. 7283 column += 2;
3322. 7283 line_source = line_source.slice(2);
3323. 7283 token_create("${");
3324. 7283
3325. 7283// Lex/loop through each token inside megastring-expression `${...}`.
3326. 7283
3327. 7283 while (true) {
3328. 7283 id = lex_token().id;
3329. 7283 if (id === "{") {
3330. 7283
3331. 7283// test_cause:
3332. 7283// ["`${{", "lex_megastring", "expected_a_b", "{", 4]
3333. 7283
3334. 7283 return stop_at(
3335. 7283 "expected_a_b",
3336. 7283 line,
3337. 7283 column - 1,
3338. 7283 "}",
3339. 7283 "{"
3340. 7283 );
3341. 7283 }
3342. 7283 if (id === "}") {
3343. 7283 break;
3344. 7283 }
3345. 7283 }
3346. 7283 break;
3347. 7283 case "\\":
3348. 7283
3349. 7283// PR-513 - Check the escape with <char_after_escape>, as a string does, but
3350. 7283// move its warnings to <warning_list_untagged>, since a tagged-megastring may
3351. 7283// hold any escape. '$' and '{' escape '${'. Push back the char it leaves in
3352. 7283// <char>, which may be the closing '`'.
3353. 7283
3354. 7283 ii = warning_list.length;
3355. 7283 char_after();
3356. 7283 char_after_escape("${");
3357. 7283 char_before();
3358. 7283 warning_list_untagged.push(...warning_list.splice(ii));
3359. 7283 break;
3360. 7283 case "`":
3361. 7283
3362. 7283// if either ` or ${ was found, then the preceding joins the snippet to become
3363. 7283// a string token.
3364. 7283
3365. 7283 token_create("(string)", snippet).quote = "`";
3366. 7283 snippet = "";
3367. 7283
3368. 7283// Terminate megastring with `.
3369. 7283
3370. 7283 column += 1;
3371. 7283 line_source = line_source.slice(1);
3372. 7283 mode_mega = false;
3373. 7283 return token_create("`");
3374. 7283 default:
3375. 7283
3376. 7283// If neither ` nor ${ is seen, then the whole line joins the snippet.
3377. 7283
3378. 7283 snippet += line_source + "\n";
3379. 7283 if (read_line() === undefined) {
3380. 7283
3381. 7283// test_cause:
3382. 7283// [" `0", "lex_megastring", "unclosed_mega", "", 2]
3383. 7283// ["`", "lex_megastring", "unclosed_mega", "", 1]
3384. 7283
3385. 7283 return stop_at("unclosed_mega", line_mega, from_mega);
3386. 7283 }
3387. 7283 }
3388. 7283 }
3389. 930 }
3390. 1050
3391. 9697 function lex_number() {
3392. 9697 let prefix = snippet;
3393. 9697
3394. 9697// PR-390 - Add numeric-separator check.
3395. 9697
3396. 9697 check_numeric_separator(prefix, column - prefix.length);
3397. 9697 char_after();
3398. 3381 switch (prefix === "0" && char.toLowerCase()) {
3399. 6 case "b":
3400. 12 case "o":
3401. 42 case "x":
3402. 42
3403. 42// An uppercase radix prefix '0B', '0O' or '0X' is valid, so warn and read the
3404. 42// digits as for its lowercase form.
3405. 42
3406. 42 if (char !== char.toLowerCase()) {
3407. 42
3408. 42// test_cause:
3409. 42// ["aa=0B1", "lex_number", "expected_a_b", "B", 5]
3410. 42// ["aa=0O7", "lex_number", "expected_a_b", "O", 5]
3411. 42// ["aa=0X1", "lex_number", "expected_a_b", "X", 5]
3412. 42
3413. 42 warn_at(
3414. 42 "expected_a_b",
3415. 42 line,
3416. 42 column - 1,
3417. 42 char.toLowerCase(),
3418. 42 char
3419. 42 );
3420. 42 }
3421. 42
3422. 42// test_cause:
3423. 42// ["aa=0b1", "lex_number", "(live)", "", 0]
3424. 42// ["aa=0o7", "lex_number", "(live)", "", 0]
3425. 42// ["aa=0x1", "lex_number", "(live)", "", 0]
3426. 42
3427. 42 read_digits(char.toLowerCase(), mode_digits_numeric_separator);
3428. 42
3429. 42// PR-351 - Ignore BigInt suffix 'n'.
3430. 42
3431. 42 if (char === "n") {
3432. 42
3433. 42// test_cause:
3434. 42// ["aa=0x1fn", "lex_number", "(live)", "", 0]
3435. 42
3436. 42 char_after("n");
3437. 42 }
3438. 42 break;
3439. 9655 default:
3440. 9655 if (char === ".") {
3441. 9655 read_digits("d", mode_digits_numeric_separator);
3442. 9655 }
3443. 9655 if (char === "E" || char === "e") {
3444. 9655
3445. 9655// test_cause:
3446. 9655// ["aa=1e+5", "lex_number", "(live)", "", 0]
3447. 9655// ["aa=1e5", "lex_number", "(live)", "", 0]
3448. 9655
3449. 9655 char_after(char);
3450. 9655 if (char !== "+" && char !== "-") {
3451. 9655
3452. 9655// test_cause:
3453. 9655// ["aa=1e5", "lex_number", "(live)", "", 0]
3454. 9655
3455. 9655 char_before();
3456. 9655 }
3457. 9655 read_digits("d", mode_digits_numeric_separator);
3458. 9655 }
3459. 9697 }
3460. 9697
3461. 9697// If the next character after a number is a digit or letter, then something
3462. 9697// unexpected is going on.
3463. 9697
3464. 9697 if (
3465. 1823 (char >= "0" && char <= "9") ||
3466. 9695 (char >= "a" && char <= "z") ||
3467. 9695 (char >= "A" && char <= "Z")
3468. 2 ) {
3469. 2
3470. 2// test_cause:
3471. 2// [" 0a", "lex_number", "unexpected_a_after_b", "0", 3]
3472. 2// [" 0a;", "lex_number", "unexpected_a_after_b", "0", 3]
3473. 2
3474. 2 return stop_at(
3475. 2 "unexpected_a_after_b",
3476. 2 line,
3477. 2 column - 1,
3478. 2 snippet.slice(-1),
3479. 2 snippet.slice(0, -1)
3480. 2 );
3481. 9695 }
3482. 9695 char_before();
3483. 9695 return token_create("(number)", snippet);
3484. 9695 }
3485. 1050
3486. 633 function lex_regexp() {
3487. 633
3488. 633// Regexp
3489. 633// Lex a regular expression literal.
3490. 633
3491. 633 let flag;
3492. 633 let mode_regexp_multiline;
3493. 633 let result;
3494. 633 let value;
3495. 633 mode_regexp = true;
3496. 633
3497. 242 function lex_regexp_bracketed() {
3498. 242 let mode_regexp_range;
3499. 242
3500. 242// RegExp
3501. 242// Match a class.
3502. 242
3503. 242 char_after("[");
3504. 56 if (char === "^") {
3505. 56 char_after("^");
3506. 56 }
3507. 994 while (true) {
3508. 994
3509. 994// RegExp
3510. 994// Match a character in a character class.
3511. 994
3512. 994 switch (char) {
3513. 994 case "":
3514. 994 case "]":
3515. 994
3516. 994// test_cause:
3517. 994// ["aa=/[", "lex_regexp_bracketed", "(live)", "", 0]
3518. 994// ["aa=/[]/", "lex_regexp_bracketed", "(live)", "", 0]
3519. 994
3520. 994 if (mode_regexp_range) {
3521. 994
3522. 994// test_cause:
3523. 994// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7]
3524. 994
3525. 994 warn_at("unexpected_a", line, column - 2, "-");
3526. 994 }
3527. 994 return char_after("]");
3528. 994
3529. 994// PR-362 - Relax regexp-warning against using <space>.
3530. 994//
3531. 994// case " ":
3532. 994//
3533. 994// // test_cause:
3534. 994// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6]
3535. 994//
3536. 994// warn_at("expected_a_b", line, column, "\\u0020", " ");
3537. 994// break;
3538. 994
3539. 994 case "-":
3540. 994 case "/":
3541. 994 case "[":
3542. 994 case "^":
3543. 994
3544. 994// test_cause:
3545. 994// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6]
3546. 994// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7]
3547. 994// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6]
3548. 994// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8]
3549. 994// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8]
3550. 994
3551. 994 warn_at(
3552. 994 "expected_a_before_b",
3553. 994 line,
3554. 994 column - 1,
3555. 994 "\\",
3556. 994 char
3557. 994 );
3558. 994 break;
3559. 994 case "\\":
3560. 994 char_after_escape("BbDdSsWw-[]^");
3561. 994 char_before();
3562. 994 break;
3563. 994 case "`":
3564. 994 if (mode_mega) {
3565. 994
3566. 994// test_cause:
3567. 994// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6]
3568. 994
3569. 994 warn_at("unexpected_a", line, column - 1, "`");
3570. 994 }
3571. 994 break;
3572. 994 }
3573. 994 char_after();
3574. 994 mode_regexp_range = false;
3575. 994 if (char === "-") {
3576. 994
3577. 994// RegExp
3578. 994// Match a range of subclasses.
3579. 994
3580. 994 mode_regexp_range = true;
3581. 994 char_after("-");
3582. 994 }
3583. 994 }
3584. 242 }
3585. 633
3586. 883 function lex_regexp_group() {
3587. 883
3588. 883// RegExp
3589. 883// Lex sequence of characters in regexp.
3590. 883
3591. 883 let modifier_seen = "";
3592. 883 switch (char) {
3593. 2 case "":
3594. 2
3595. 2// test_cause:
3596. 2// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3]
3597. 2
3598. 2 warn_at("expected_regexp_factor_a", line, column - 0, char);
3599. 2 break;
3600. 2 case ")":
3601. 2
3602. 2// test_cause:
3603. 2// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
3604. 2// ["aa=/);", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
3605. 2
3606. 2 warn_at("expected_regexp_factor_a", line, column - 1, char);
3607. 2 break;
3608. 1 case "]":
3609. 1
3610. 1// test_cause:
3611. 1// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5]
3612. 1
3613. 1 warn_at("expected_regexp_factor_a", line, column - 1, char);
3614. 1 break;
3615. 883 }
3616. 6440 while (true) {
3617. 6440 switch (char) {
3618. 6440 case "":
3619. 6440 case ")":
3620. 6440 case "/":
3621. 6440 case "]":
3622. 6440 return;
3623. 6440
3624. 6440// PR-362 - Relax regexp-warning against using <space>.
3625. 6440//
3626. 6440// case " ":
3627. 6440//
3628. 6440// // test_cause:
3629. 6440// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
3630. 6440//
3631. 6440// warn_at("expected_a_b", line, column, "\\s", " ");
3632. 6440// char_after();
3633. 6440// break;
3634. 6440
3635. 6440 case "$":
3636. 6440 if (line_source[0] !== "/") {
3637. 6440 mode_regexp_multiline = true;
3638. 6440 }
3639. 6440 char_after();
3640. 6440 break;
3641. 6440 case "(":
3642. 6440
3643. 6440// RegExp
3644. 6440// Match a group that starts with left paren.
3645. 6440
3646. 6440 char_after("(");
3647. 6440 switch (char) {
3648. 6440 case ":":
3649. 6440
3650. 6440// test_cause:
3651. 6440// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
3652. 6440
3653. 6440 warn_at(
3654. 6440 "expected_a_before_b",
3655. 6440 line,
3656. 6440 column - 1,
3657. 6440 "?",
3658. 6440 ":"
3659. 6440 );
3660. 6440 break;
3661. 6440 case "?":
3662. 6440 char_after("?");
3663. 6440 switch (char) {
3664. 6440
3665. 6440// ES1999-feature Negative lookahead assertion.
3666. 6440
3667. 6440 case "!":
3668. 6440
3669. 6440// PR-437 - Add grammar for regexp-named-capture-group.
3670. 6440
3671. 6440 case "<":
3672. 6440
3673. 6440// ES1999-feature Positive lookahead assertion.
3674. 6440
3675. 6440 case "=":
3676. 6440 char_after();
3677. 6440 break;
3678. 6440
3679. 6440// PR-499 - Add ES2025-feature RegExp Modifiers.
3680. 6440
3681. 6440 case "-":
3682. 6440 case "i":
3683. 6440 case "m":
3684. 6440 case "s":
3685. 6440 modifier_seen = char;
3686. 6440 char_after();
3687. 6440 while (true) {
3688. 6440
3689. 6440// A lone "-" adds and removes nothing, which v8 rejects - unlike "(?i-:".
3690. 6440
3691. 6440 if (char === ":" && modifier_seen !== "-") {
3692. 6440 char_after();
3693. 6440 break;
3694. 6440 }
3695. 6440 switch (char) {
3696. 6440 case "-":
3697. 6440 case "i":
3698. 6440 case "m":
3699. 6440 case "s":
3700. 6440 if (modifier_seen.includes(char)) {
3701. 6440
3702. 6440// test_cause:
3703. 6440// ["aa=/(?--:x)/", "lex_regexp_group", "unexpected_a_after_b", "(?-", 8]
3704. 6440// ["aa=/(?ii:x)/", "lex_regexp_group", "unexpected_a_after_b", "(?i", 8]
3705. 6440
3706. 6440 warn_at(
3707. 6440 "unexpected_a_after_b",
3708. 6440 line,
3709. 6440 column - 1,
3710. 6440 snippet.slice(-1),
3711. 6440 snippet.slice(0, -1)
3712. 6440 );
3713. 6440 }
3714. 6440 modifier_seen += char;
3715. 6440 char_after();
3716. 6440 break;
3717. 6440 default:
3718. 6440
3719. 6440// End of line inside (?flags - report the missing ":" the way char_after does.
3720. 6440
3721. 6440 if (char === "") {
3722. 6440 return char_after(":");
3723. 6440 }
3724. 6440
3725. 6440// test_cause:
3726. 6440// ["aa=/(?-.", "lex_regexp_group", "unexpected_a_after_b", "(?-", 8]
3727. 6440// ["aa=/(?-.)/", "lex_regexp_group", "unexpected_a_after_b", "(?-", 8]
3728. 6440// ["aa=/(?-:x)/", "lex_regexp_group", "unexpected_a_after_b", "(?-", 8]
3729. 6440
3730. 6440 return stop_at(
3731. 6440 "unexpected_a_after_b",
3732. 6440 line,
3733. 6440 column - 1,
3734. 6440 snippet.slice(-1),
3735. 6440 snippet.slice(0, -1)
3736. 6440 );
3737. 6440 }
3738. 6440 }
3739. 6440 break;
3740. 6440 default:
3741. 6440 char_after(":");
3742. 6440 }
3743. 6440 break;
3744. 6440 }
3745. 6440
3746. 6440// RegExp
3747. 6440// Recurse lex_regexp_group.
3748. 6440
3749. 6440 lex_regexp_group();
3750. 6440 char_after(")");
3751. 6440 break;
3752. 6440 case "*":
3753. 6440 case "+":
3754. 6440 case "?":
3755. 6440 case "{":
3756. 6440 case "}":
3757. 6440
3758. 6440// test_cause:
3759. 6440// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
3760. 6440// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
3761. 6440// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
3762. 6440// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
3763. 6440// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
3764. 6440
3765. 6440 warn_at(
3766. 6440 "expected_a_before_b",
3767. 6440 line, column - 1,
3768. 6440 "\\",
3769. 6440 char
3770. 6440 );
3771. 6440 char_after();
3772. 6440 break;
3773. 6440 case "[":
3774. 6440 lex_regexp_bracketed();
3775. 6440 break;
3776. 6440 case "\\":
3777. 6440
3778. 6440// test_cause:
3779. 6440// ["aa=/\\/", "lex_regexp_group", "(live)", "", 0]
3780. 6440
3781. 6440// PR-437 - Add grammar for regexp-named-backreference.
3782. 6440
3783. 6440 char_after_escape("BbDdSsWw^${}[]():=!.|*+?k");
3784. 6440 break;
3785. 6440 case "^":
3786. 6440 if (snippet !== "^") {
3787. 6440 mode_regexp_multiline = true;
3788. 6440 }
3789. 6440 char_after();
3790. 6440 break;
3791. 6440 case "`":
3792. 6440 if (mode_mega) {
3793. 6440
3794. 6440// test_cause:
3795. 6440// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
3796. 6440
3797. 6440 warn_at("unexpected_a", line, column - 1, "`");
3798. 6440 }
3799. 6440 char_after();
3800. 6440 break;
3801. 6440 default:
3802. 6440 char_after();
3803. 6440 }
3804. 6440
3805. 6440// RegExp
3806. 6440// Match an optional quantifier.
3807. 6440
3808. 6440 switch (char) {
3809. 6440 case "*":
3810. 6440 case "+":
3811. 6440 if (char_after(char) === "?") {
3812. 6440
3813. 6440// test_cause:
3814. 6440// ["aa=/.*?/", "lex_regexp_group", "(live)", "", 0]
3815. 6440// ["aa=/.+?/", "lex_regexp_group", "(live)", "", 0]
3816. 6440
3817. 6440 char_after("?");
3818. 6440 }
3819. 6440 break;
3820. 6440 case "?":
3821. 6440 if (char_after("?") === "?") {
3822. 6440
3823. 6440// test_cause:
3824. 6440// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
3825. 6440
3826. 6440 warn_at("unexpected_a", line, column - 1, char);
3827. 6440 char_after("?");
3828. 6440 }
3829. 6440 break;
3830. 6440 case "{":
3831. 6440 read_digits("d", mode_digits_regexp_quantifier);
3832. 6440 if (char_after("}") === "?") {
3833. 6440
3834. 6440// test_cause:
3835. 6440// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
3836. 6440
3837. 6440 warn_at("unexpected_a", line, column - 1, char);
3838. 6440 char_after("?");
3839. 6440 }
3840. 6440 break;
3841. 6440 }
3842. 6440 }
3843. 883 }
3844. 633
3845. 633// RegExp
3846. 633// Scan the regexp literal. Give a warning if the first character is = because
3847. 633// /= looks like a division assignment operator.
3848. 633
3849. 633 snippet = "";
3850. 633 char_after();
3851. 1 if (char === "=") {
3852. 1
3853. 1// test_cause:
3854. 1// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5]
3855. 1
3856. 1 warn_at("expected_a_before_b", line, column - 1, "\\", "=");
3857. 1 }
3858. 633 lex_regexp_group();
3859. 633
3860. 633// RegExp
3861. 633// Remove last character from snippet.
3862. 633
3863. 633 snippet = snippet.slice(0, -1);
3864. 633
3865. 633// RegExp
3866. 633// Make sure there is a closing slash.
3867. 633
3868. 633 value = snippet;
3869. 633 char_after("/");
3870. 633
3871. 633// RegExp
3872. 633// Create flag.
3873. 633
3874. 633 flag = empty();
3875. 633 while (
3876. 633
3877. 633// Regexp
3878. 633// char is a letter.
3879. 633
3880. 616 (char >= "a" && char <= "z\uffff") ||
3881. 616 (char >= "A" && char <= "Z\uffff")
3882. 512 ) {
3883. 512
3884. 512// RegExp
3885. 512// Process dangling flag letters.
3886. 512
3887. 512 switch (!flag[char] && char) {
3888. 512
3889. 512// PR-499 - Add ES2022-feature RegExp Match Indices.
3890. 512
3891. 512 case "d":
3892. 512
3893. 512// test_cause:
3894. 512// ["aa=/./d", "lex_regexp", "(live)", "", 0]
3895. 512
3896. 512 break;
3897. 512 case "g":
3898. 512 break;
3899. 512 case "i":
3900. 512 break;
3901. 512 case "m":
3902. 512 break;
3903. 512
3904. 512// PR-499 - Add ES2018-feature s (dotall) flag for regular expressions.
3905. 512
3906. 512 case "s":
3907. 512
3908. 512// test_cause:
3909. 512// ["aa=/./s", "lex_regexp", "(live)", "", 0]
3910. 512
3911. 512 break;
3912. 512 case "u":
3913. 512 if (
3914. 512 flag.v //jslint-ignore-line
3915. 512 ) {
3916. 512
3917. 512// test_cause:
3918. 512// ["aa=/./vu", "lex_regexp", "unexpected_a", "u", 8]
3919. 512// ["aa=/./vu;", "lex_regexp", "unexpected_a", "u", 8]
3920. 512
3921. 512 warn_at("unexpected_a", line, column - 1, char);
3922. 512 }
3923. 512 break;
3924. 512
3925. 512// PR-499 - Add ES2024-feature RegExp v flag with set-notation + str-properties.
3926. 512
3927. 512 case "v":
3928. 512
3929. 512// "u" and "v" select different grammars, so v8 rejects the pair.
3930. 512
3931. 512 if (
3932. 512 flag.u //jslint-ignore-line
3933. 512 ) {
3934. 512
3935. 512// test_cause:
3936. 512// ["aa=/./uv", "lex_regexp", "unexpected_a", "v", 8]
3937. 512
3938. 512 warn_at("unexpected_a", line, column - 1, char);
3939. 512 }
3940. 512 break;
3941. 512 case "y":
3942. 512
3943. 512// test_cause:
3944. 512// ["aa=/./gimuy", "lex_regexp", "(live)", "", 0]
3945. 512
3946. 512 break;
3947. 512 default:
3948. 512
3949. 512// test_cause:
3950. 512// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
3951. 512// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
3952. 512
3953. 512 warn_at("unexpected_a", line, column - 1, char);
3954. 512 }
3955. 512 flag[char] = true;
3956. 512 char_after();
3957. 616 }
3958. 616 char_before();
3959. 616 if (char === "/" || char === "*") {
3960. 1
3961. 1// test_cause:
3962. 1// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 7]
3963. 1
3964. 1 return stop_at("unexpected_a", line, column - 0, char);
3965. 615 }
3966. 615 result = token_create("(regexp)", char);
3967. 615 result.flag = flag;
3968. 615 result.value = value;
3969. 615 if (
3970. 615 mode_regexp_multiline &&
3971. 46 !flag.m //jslint-ignore-line
3972. 2 ) {
3973. 2
3974. 2// test_cause:
3975. 2// ["aa=/$^/", "lex_regexp", "missing_m", "", 7]
3976. 2// ["aa=/$^/;", "lex_regexp", "missing_m", "", 7]
3977. 2
3978. 2 warn_at("missing_m", line, column - 1);
3979. 615 }
3980. 615 return result;
3981. 615 }
3982. 1050
3983. 655 function lex_slash_or_regexp() {
3984. 655
3985. 655// The / can be a division operator or the beginning of a regular expression
3986. 655// literal. It is not possible to know which without doing a complete parse.
3987. 655// We want to complete the tokenization before we begin to parse, so we will
3988. 655// estimate. This estimator can fail in some cases. For example, it cannot
3989. 655// know if "}" is ending a block or ending an object literal, so it can
3990. 655// behave incorrectly in that case; it is not meaningful to divide an
3991. 655// object, so it is likely that we can get away with it. We avoided the worst
3992. 655// cases by eliminating automatic semicolon insertion.
3993. 655
3994. 655 let the_token;
3995. 655 switch (
3996. 24 token_prv_expr.identifier &&
3997. 24 !token_prv_expr.dot &&
3998. 22 token_prv_expr.id
3999. 655 ) {
4000. 1 case "case":
4001. 2 case "delete":
4002. 3 case "in":
4003. 4 case "instanceof":
4004. 5 case "new":
4005. 6 case "typeof":
4006. 7 case "void":
4007. 8 case "yield":
4008. 8 the_token = lex_regexp();
4009. 8
4010. 8// test_cause:
4011. 8// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
4012. 8// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
4013. 8// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4]
4014. 8// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12]
4015. 8// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5]
4016. 8// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
4017. 8// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
4018. 8// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7]
4019. 8
4020. 8 return stop("unexpected_a", the_token);
4021. 2 case "return":
4022. 2
4023. 2// test_cause:
4024. 2// ["function aa(){return /./;}", "lex_slash_or_regexp", "(live)", "", 0]
4025. 2
4026. 2 return lex_regexp();
4027. 645 }
4028. 645 switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) {
4029. 1 case "!":
4030. 2 case "%":
4031. 4 case "&":
4032. 5 case "*":
4033. 6 case "+":
4034. 7 case "-":
4035. 9 case "/":
4036. 10 case ";":
4037. 11 case "<":
4038. 12 case ">":
4039. 13 case "^":
4040. 16 case "{":
4041. 17 case "|":
4042. 18 case "}":
4043. 19 case "~":
4044. 19 the_token = lex_regexp();
4045. 19
4046. 19// test_cause:
4047. 19// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4048. 19// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4049. 19// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4050. 19// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4051. 19// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4052. 19// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
4053. 19// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
4054. 19// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4055. 19// ["</./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4056. 19// [">/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4057. 19// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4058. 19// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4059. 19// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4060. 19// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4061. 19// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
4062. 19
4063. 19 warn("wrap_regexp", the_token);
4064. 19 return the_token;
4065. 534 case "(":
4066. 550 case ",":
4067. 551 case ":":
4068. 602 case "=":
4069. 603 case "?":
4070. 604 case "[":
4071. 604
4072. 604// test_cause:
4073. 604// ["(/./", "lex_slash_or_regexp", "(live)", "", 0]
4074. 604// [",/./", "lex_slash_or_regexp", "(live)", "", 0]
4075. 604// [":/./", "lex_slash_or_regexp", "(live)", "", 0]
4076. 604// ["=/./", "lex_slash_or_regexp", "(live)", "", 0]
4077. 604// ["?/./", "lex_slash_or_regexp", "(live)", "", 0]
4078. 604// ["aa[/./", "lex_slash_or_regexp", "(live)", "", 0]
4079. 604
4080. 604 return lex_regexp();
4081. 22 }
4082. 22 if (line_source[0] === "=") {
4083. 3 snippet = "/=";
4084. 3 column += 1;
4085. 3 line_source = line_source.slice(1);
4086. 3
4087. 3// test_cause:
4088. 3// ["0/=0", "lex_slash_or_regexp", "unexpected_a", "/=", 2]
4089. 3
4090. 3 warn_at("unexpected_a", line, column - 2, "/=");
4091. 22 }
4092. 22 return token_create(snippet);
4093. 22 }
4094. 1050
4095. 24695 function lex_string(quote) {
4096. 24695
4097. 24695// Create a string token.
4098. 24695
4099. 24695 let the_token;
4100. 24693 if (!option_dict.single && quote === "'") {
4101. 3
4102. 3// test_cause:
4103. 3// [" ''", "lex_string", "use_double", "", 2]
4104. 3// ["''", "lex_string", "use_double", "", 1]
4105. 3
4106. 3 warn_at("use_double", line, column - 1);
4107. 3 }
4108. 24695 snippet = "";
4109. 24695 char_after();
4110. 24695
4111. 24695// Lex/loop through each character in "...".
4112. 24695
4113. 223136 while (true) {
4114. 223136 switch (char) {
4115. 223136 case "":
4116. 223136
4117. 223136// test_cause:
4118. 223136// ["\"", "lex_string", "unclosed_string", "", 1]
4119. 223136
4120. 223136 return stop_at("unclosed_string", line, column - 0);
4121. 223136 case "\\":
4122. 223136 char_after_escape(quote);
4123. 223136 break;
4124. 223136 case "`":
4125. 223136 if (mode_mega) {
4126. 223136
4127. 223136// test_cause:
4128. 223136// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
4129. 223136
4130. 223136 warn_at("unexpected_a", line, column - 1, "`");
4131. 223136 }
4132. 223136 char_after("`");
4133. 223136 break;
4134. 223136 case quote:
4135. 223136
4136. 223136// Remove last character from snippet.
4137. 223136
4138. 223136 snippet = snippet.slice(0, -1);
4139. 223136 the_token = token_create("(string)", snippet);
4140. 223136 the_token.quote = quote;
4141. 223136 return the_token;
4142. 223136 default:
4143. 223136 char_after();
4144. 223136 }
4145. 223136 }
4146. 24695 }
4147. 1050
4148. 282479 function lex_token() {
4149. 282479 let match;
4150. 282479
4151. 282479// Lex/loop through each whitespace.
4152. 282479
4153. 415959 while (true) {
4154. 415959
4155. 415959// Lex/loop through each blank-line.
4156. 415959
4157. 415959 while (!line_source) {
4158. 415959 line_source = read_line();
4159. 415959 from = 0;
4160. 415959 if (line_source === undefined) {
4161. 415959 return (
4162. 415959 mode_mega
4163. 415959
4164. 415959// test_cause:
4165. 415959// [" `${//}`", "lex_token", "unclosed_mega", "", 2]
4166. 415959// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
4167. 415959
4168. 415959 ? stop_at("unclosed_mega", line_mega, from_mega)
4169. 415959 : line_disable !== undefined
4170. 415959
4171. 415959// test_cause:
4172. 415959// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
4173. 415959
4174. 415959 ? stop_at("unclosed_disable", line_disable, column - 0)
4175. 415959 : token_create("(end)")
4176. 415959 );
4177. 415959 }
4178. 415959 }
4179. 415959 from = column;
4180. 415959 match = line_source.match(jslint_rgx_token);
4181. 415959
4182. 415959// match[1] token
4183. 415959// match[2] whitespace
4184. 415959// match[3] identifier
4185. 415959// match[4] number
4186. 415959// match[5] rest
4187. 415959
4188. 415959 if (!match) {
4189. 415959
4190. 415959// test_cause:
4191. 415959// ["#", "lex_token", "unexpected_char_a", "#", 1]
4192. 415959// ["aa=#0", "lex_token", "unexpected_char_a", "#", 4]
4193. 415959
4194. 415959 return stop_at(
4195. 415959 "unexpected_char_a",
4196. 415959 line,
4197. 415959 column - 0,
4198. 415959 line_source[0]
4199. 415959 );
4200. 415959 }
4201. 415959 snippet = match[1];
4202. 415959 column += snippet.length;
4203. 415959 line_source = line_source.slice(snippet.length);
4204. 415959 if (!match[2]) {
4205. 415959 break;
4206. 415959 }
4207. 415959 }
4208. 281462
4209. 281462// The token is an identifier.
4210. 281462
4211. 281462 if (match[3]) {
4212. 77109 return token_create(snippet, undefined, true);
4213. 204353 }
4214. 204353
4215. 204353// Create token from number.
4216. 204353
4217. 204353 if (match[4]) {
4218. 9697 return lex_number();
4219. 194656 }
4220. 194656
4221. 194656// Create token from string "..." or '...'.
4222. 194656
4223. 194656 if (snippet === "\"" || snippet === "'") {
4224. 24695 return lex_string(snippet);
4225. 169961 }
4226. 169961
4227. 169961// Create token from megastring `...`.
4228. 169961
4229. 169961 if (snippet === "`") {
4230. 930 return lex_megastring();
4231. 169031 }
4232. 169031
4233. 169031// Create token from comment /*...*/ or //....
4234. 169031
4235. 169031 if (snippet === "/*" || snippet === "//") {
4236. 15676 return lex_comment();
4237. 153355 }
4238. 153355
4239. 153355// Create token from slash /.
4240. 153355
4241. 153355 if (snippet === "/") {
4242. 655 return lex_slash_or_regexp();
4243. 152700 }
4244. 152700 return token_create(snippet);
4245. 152700 }
4246. 1050
4247. 4146 function option_set_item(key, value) {
4248. 4146
4249. 4146// These are the options that are recognized in the option object or that may
4250. 4146// appear in a /*jslint*/ directive. Most options will have a boolean value,
4251. 4146// usually true. Some options will also predefine some number of global
4252. 4146// variables.
4253. 4146
4254. 4146 switch (key) {
4255. 960 case "beta": // Enable experimental warnings.
4256. 963 case "bitwise": // Allow bitwise operator.
4257. 980 case "browser": // Assume browser environment.
4258. 986 case "convert": // Allow conversion operator.
4259. 989 case "couch": // Assume CouchDb environment.
4260. 996 case "devel": // Allow console.log() and friends.
4261. 3096 case "ecma": // Assume ECMAScript environment.
4262. 3103 case "eval": // Allow eval().
4263. 3106 case "fart": // Allow complex fat-arrow.
4264. 3107 case "for": // Allow for-statement (deprecated).
4265. 3119 case "getset": // Allow get() and set().
4266. 3123 case "indent2": // Use 2-space indent.
4267. 3125 case "long": // Allow long lines.
4268. 3199 case "node": // Assume Node.js environment.
4269. 3204 case "nomen": // Allow weird property name.
4270. 3206 case "single": // Allow single-quote strings.
4271. 3208 case "subscript": // Allow identifier in subscript-notation.
4272. 3213 case "test_internal_error": // Test jslint's internal-error
4273. 4146 // ... handling-ability.
4274. 3215 case "test_unknown_warning_code": // Test jslint's unknown-warning-code
4275. 4146 // ... handling-ability.
4276. 3217 case "this": // Allow 'this'.
4277. 3990 case "trace": // Include jslint stack-trace in warnings.
4278. 3994 case "unordered": // Allow unordered cases, params, properties,
4279. 4146 // ... variables, and exports.
4280. 3998 case "variable": // Allow unordered const and let declarations
4281. 4146 // ... not at top of function-scope.
4282. 4001 case "white": // Allow messy whitespace.
4283. 4001 option_dict[key] = value;
4284. 4001 break;
4285. 4146
4286. 4146// PR-404 - Alias "evil" to sub-directive "eval" for backwards-compat.
4287. 4146
4288. 3 case "evil":
4289. 3
4290. 3// test_cause:
4291. 3// ["/*jslint evil*/", "option_set_item", "(live)", "", 0]
4292. 3
4293. 3 return option_set_item("eval", value);
4294. 4146
4295. 4146// PR-404 - Alias "nomen" to sub-directive "name" for backwards-compat.
4296. 4146
4297. 3 case "name":
4298. 3
4299. 3// test_cause:
4300. 3// ["/*jslint name*/", "option_set_item", "(live)", "", 0]
4301. 3
4302. 3 return option_set_item("nomen", value);
4303. 139 default:
4304. 139 return false;
4305. 4001 }
4306. 4001
4307. 4001// Initialize global-variables.
4308. 4001
4309. 4001 switch (value && key) {
4310. 16 case "browser":
4311. 19 case "couch":
4312. 26 case "devel":
4313. 2126 case "ecma":
4314. 2200 case "node":
4315. 15400 Object.entries(jslint_global_dict_all).forEach(function ([
4316. 15400 dict_name, dict
4317. 15400 ]) {
4318. 15400 if (
4319. 11026 dict_name.startsWith(key) ||
4320. 11026 (key === "browser" && dict_name === "node_auto")
4321. 4390 ) {
4322. 167133 Object.entries(dict).forEach(function ([name, is_global]) {
4323. 7020 if (dict_name === "node_auto") {
4324. 7020 switch (dict_name === "node_auto" && key) {
4325. 7020 case "browser":
4326. 7020 if (is_global.includes("brow")) {
4327. 7020 global_dict[name] = "browser";
4328. 7020 }
4329. 7020 break;
4330. 7020 case "node":
4331. 7020 if (is_global.includes("node")) {
4332. 7020 global_dict[name] = "Node.js";
4333. 7020 }
4334. 7020 break;
4335. 7020 }
4336. 160113 } else {
4337. 160113 if (is_global) {
4338. 160113 global_dict[name] = (
4339. 160113 dict_name.startsWith("browser")
4340. 160113 ? "browser"
4341. 160113 : dict_name.startsWith("couch")
4342. 160113
4343. 160113// test_cause:
4344. 160113// ["/*jslint couch*/", "option_set_item", "(live)", "", 0]
4345. 160113
4346. 160113 ? "CouchDb"
4347. 160113 : dict_name.startsWith("devel")
4348. 160113 ? "development"
4349. 160113 : dict_name.startsWith("ecma")
4350. 160113 ? "ECMAScript"
4351. 160113 : "Node.js"
4352. 160113 );
4353. 160113 }
4354. 160113 }
4355. 167133 });
4356. 4390 }
4357. 15400 });
4358. 2200 break;
4359. 4001 }
4360. 4001 return true;
4361. 4001 }
4362. 1050
4363. 541 function read_digits(base, mode) {
4364. 541 let digits = line_source.match(
4365. 541 base === "b"
4366. 541
4367. 541// test_cause:
4368. 541// ["aa=0b1", "read_digits", "(live)", "", 0]
4369. 541
4370. 6 ? jslint_rgx_digits_bits
4371. 535 : base === "o"
4372. 535
4373. 535// test_cause:
4374. 535// ["aa=0o7", "read_digits", "(live)", "", 0]
4375. 535
4376. 535 ? jslint_rgx_digits_octals
4377. 535 : base === "x"
4378. 535 ? jslint_rgx_digits_hexs
4379. 535 : jslint_rgx_digits_decimals
4380. 541 )[0];
4381. 541 if (
4382. 541 (
4383. 16 digits.length === 0 &&
4384. 16 (
4385. 16 mode === mode_digits_numeric_separator ||
4386. 16 (mode === mode_digits_unicode_escape && char === "{")
4387. 16 )
4388. 538 ) ||
4389. 538 digits[0] === "_"
4390. 4 ) {
4391. 4
4392. 4// test_cause:
4393. 4// ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
4394. 4// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
4395. 4// ["\"\\u{}\"", "read_digits", "expected_digits_after_a", "\\u{", 4]
4396. 4// ["`\\u{}`", "read_digits", "expected_digits_after_a", "\\u{", 4]
4397. 4
4398. 4 warn_at("expected_digits_after_a", line, column - 1, snippet);
4399. 4 }
4400. 541
4401. 541// PR-390 - Add numeric-separator check.
4402. 541
4403. 85 if (mode === mode_digits_numeric_separator) {
4404. 85 check_numeric_separator(digits, column);
4405. 456 } else if (digits.indexOf("_") >= 0) {
4406. 456
4407. 456// test_cause:
4408. 456// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6]
4409. 456// ["`\\u{1_2}`", "read_digits", "illegal_num_separator", "", 6]
4410. 456
4411. 456 warn_at(
4412. 456 "illegal_num_separator",
4413. 456 line,
4414. 456 column + digits.indexOf("_")
4415. 456 );
4416. 456 }
4417. 541 snippet += digits;
4418. 541 column += digits.length;
4419. 541 line_source = line_source.slice(digits.length);
4420. 541 switch (mode) {
4421. 35 case mode_digits_regexp_quantifier:
4422. 35 if (digits.length === 0) {
4423. 35
4424. 35// test_cause:
4425. 35// ["aa=/aa{/", "read_digits", "expected_a_before_b", ",", 8]
4426. 35
4427. 35 warn_at("expected_a_before_b", line, column - 0, "0", ",");
4428. 35 }
4429. 35 if (line_source[0] === ",") {
4430. 35
4431. 35// test_cause:
4432. 35// ["aa=/.{,/", "read_digits", "(live)", "", 0]
4433. 35
4434. 35 char_after();
4435. 35
4436. 35// Recurse read_digits.
4437. 35
4438. 35 read_digits("d", undefined);
4439. 35 return;
4440. 35 }
4441. 35 break;
4442. 541
4443. 412 case mode_digits_unicode_escape:
4444. 412
4445. 412// PR-513 - Check the code point's value, not its digit count. '\u{10FFFF}' and
4446. 412// '\u{000041}' are legal. Above 10FFFF a string is a SyntaxError, and a regexp
4447. 412// without flag 'u' reads '\u{110000}' as 'u' repeated. Both lint on, so warn.
4448. 412// A megastring's '\u' gets here too. <char> is '{' only for '\u{...}'.
4449. 412
4450. 412 if (char !== "{") {
4451. 412 if (digits.length < 4) {
4452. 412
4453. 412// test_cause:
4454. 412// ["\"\\u0\"", "read_digits", "expected_four_digits", "", 5]
4455. 412// ["\"\\u0\";", "read_digits", "expected_four_digits", "", 5]
4456. 412// ["`\\u0`", "read_digits", "expected_four_digits", "", 5]
4457. 412
4458. 412 warn_at("expected_four_digits", line, column - 0);
4459. 412 }
4460. 412 } else if (Number.parseInt(digits, 16) > 0x10ffff) {
4461. 412
4462. 412// test_cause:
4463. 412// ["\"\\u{110000}\"", "read_digits", "too_many_digits", "", 11]
4464. 412// ["`\\u{110000}`", "read_digits", "too_many_digits", "", 11]
4465. 412// ["aa=/\\u{110000}/", "read_digits", "too_many_digits", "", 14]
4466. 412
4467. 412 warn_at("too_many_digits", line, column - 0);
4468. 412 }
4469. 412 break;
4470. 532 }
4471. 532 char_after();
4472. 532 }
4473. 1050
4474. 121433 function read_line() {
4475. 121433
4476. 121433// Put the next line of source in line_source. If the line contains tabs,
4477. 121433// replace them with spaces and give a warning. Also warn if the line contains
4478. 121433// unsafe characters or is too damn long.
4479. 121433
4480. 121433 if (
4481. 121429 !option_dict.long &&
4482. 121429 line_whole.length > 80 &&
4483. 81 line_disable === undefined &&
4484. 81 !state.mode_json &&
4485. 48 token_1 &&
4486. 48 !mode_regexp
4487. 26 ) {
4488. 26
4489. 26// test_cause:
4490. 26// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line
4491. 26
4492. 26 warn_at("too_long", line, 0);
4493. 26 }
4494. 121433 column = 0;
4495. 121433 line += 1;
4496. 121433 mode_regexp = false;
4497. 121433 line_source = undefined;
4498. 121433 line_whole = "";
4499. 1020 if (line_list[line] === undefined) {
4500. 1020 return line_source;
4501. 120413 }
4502. 120413 line_source = line_list[line].line_source;
4503. 120413 line_whole = line_source;
4504. 120413
4505. 120413// Scan each line for following ignore-directives:
4506. 120413// "/*jslint-disable*/"
4507. 120413// "/*jslint-enable*/"
4508. 120413// "//jslint-ignore-line"
4509. 120413
4510. 120413 if (line_source === "/*jslint-disable*/") {
4511. 10
4512. 10// test_cause:
4513. 10// ["/*jslint-disable*/", "read_line", "(live)", "", 0]
4514. 10
4515. 10 line_disable = line;
4516. 120403 } else if (line_source === "/*jslint-enable*/") {
4517. 120403 if (line_disable === undefined) {
4518. 120403
4519. 120403// test_cause:
4520. 120403// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
4521. 120403
4522. 120403 return stop_at("unopened_enable", line, column - 0);
4523. 120403 }
4524. 120403 line_disable = undefined;
4525. 120403 } else if (
4526. 120403 line_source.endsWith(" //jslint-ignore-line") ||
4527. 120403 line_source.endsWith(" //jslint-quiet")
4528. 120403 ) {
4529. 120403
4530. 120403// test_cause:
4531. 120403// ["0 //jslint-ignore-line", "read_line", "(live)", "", 0]
4532. 120403
4533. 120403 line_list[line].directive_ignore_line = true;
4534. 120412 }
4535. 120412 if (line_disable !== undefined) {
4536. 1139
4537. 1139// test_cause:
4538. 1139// ["
4539. 1139// /*jslint-disable*/
4540. 1139// 0
4541. 1139// ", "read_line", "(live)", "", 0]
4542. 1139
4543. 1139 line_source = "";
4544. 120412 }
4545. 120412 // jslint_rgx_tab
4546. 120412 if (line_source.indexOf("\t") >= 0) {
4547. 3 if (!option_dict.white) {
4548. 3
4549. 3// test_cause:
4550. 3// [" \t0", "read_line", "use_spaces", "", 2]
4551. 3// ["\t", "read_line", "use_spaces", "", 1]
4552. 3
4553. 3 warn_at("use_spaces", line, line_source.indexOf("\t"));
4554. 3 }
4555. 3 line_source = line_source.replace(jslint_rgx_tab, " ");
4556. 120412 }
4557. 120412 if (!option_dict.white && line_source.endsWith(" ")) {
4558. 8
4559. 8// test_cause:
4560. 8// [" ", "read_line", "unexpected_trailing_space", "", 1]
4561. 8// ["0 ", "read_line", "unexpected_trailing_space", "", 2]
4562. 8
4563. 8 warn_at("unexpected_trailing_space", line, line_source.length - 1);
4564. 120412 }
4565. 120412 return line_source;
4566. 120412 }
4567. 1050
4568. 286028 function token_create(id, value, identifier) {
4569. 286028
4570. 286028// Create the token object and append it to token_list.
4571. 286028
4572. 286028 const the_token = {
4573. 286028 from,
4574. 286028 id,
4575. 286028 identifier: Boolean(identifier),
4576. 286028 line,
4577. 286028 nr: token_list.length,
4578. 286028 thru: column,
4579. 286028 value
4580. 286028 };
4581. 286028 token_list.push(the_token);
4582. 286028
4583. 286028// Directives must appear before the first statement.
4584. 286028
4585. 270356 if (id !== "(comment)" && id !== ";") {
4586. 252917 mode_directive = false;
4587. 252917 }
4588. 286028
4589. 286028// If this token is an identifier that touches a preceding number, or
4590. 286028// a "/", comment, or regular expression literal that touches a preceding
4591. 286028// comment or regular expression literal, then give a missing space warning.
4592. 286028// This warning is not suppressed by option_dict.white.
4593. 286028
4594. 286028 if (
4595. 206909 token_prv.line === line &&
4596. 206909 token_prv.thru === from &&
4597. 133073 (id === "(comment)" || id === "(regexp)" || id === "/") &&
4598. 252 (token_prv.id === "(comment)" || token_prv.id === "(regexp)")
4599. 1 ) {
4600. 1
4601. 1// test_cause:
4602. 1// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5]
4603. 1
4604. 1 warn(
4605. 1 "expected_space_a_b",
4606. 1 the_token,
4607. 1 artifact(token_prv),
4608. 1 artifact(the_token)
4609. 1 );
4610. 1 }
4611. 12912 if (token_prv.id === "." && id === "(number)") {
4612. 5
4613. 5// test_cause:
4614. 5// [" .0", "token_create", "expected_a_before_b", ".", 2]
4615. 5// [".0", "token_create", "expected_a_before_b", ".", 1]
4616. 5
4617. 5 warn("expected_a_before_b", token_prv, "0", ".");
4618. 5 }
4619. 12912 if (token_prv_expr.id === "." && the_token.identifier) {
4620. 12906 the_token.dot = true;
4621. 12906 }
4622. 286028
4623. 286028// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
4624. 286028// Farts are now detected by keeping a list of most recent "(" tokens at any
4625. 286028// given depth. When a "=>" token is encountered, the most recent "(" token at
4626. 286028// current depth is marked as a fart.
4627. 286028
4628. 286028 switch (id) {
4629. 286028
4630. 286028// PR-514 - Bugfix - Push '{' and '${', so a ';' in a function-body inside
4631. 286028// a for-loop-head 'for (const aa of function () {...}())' is not mistaken for
4632. 286028// a for-loop-semicolon.
4633. 286028
4634. 873 case "${":
4635. 20822 case "(":
4636. 25087 case "[":
4637. 36300 case "{":
4638. 36300 opener_stack.unshift(the_token);
4639. 36300 break;
4640. 19950 case ")":
4641. 24213 case "]":
4642. 24213
4643. 24213// PR-503 - Fix jslint crashing before warning about dangling ')' or ']'.
4644. 24213
4645. 24213 opener_popped = opener_stack.shift() || empty();
4646. 24213 if (
4647. 24213 (id === ")" && opener_popped.id !== "(") ||
4648. 24213 (id === "]" && opener_popped.id !== "[")
4649. 24213 ) {
4650. 24213
4651. 24213// test_cause:
4652. 24213// [" );", "token_create", "unexpected_a", ")", 2]
4653. 24213// [")", "token_create", "unexpected_a", ")", 1]
4654. 24213// [";(]", "token_create", "unexpected_a", "]", 3]
4655. 24213// [";[)", "token_create", "unexpected_a", ")", 3]
4656. 24213// ["]", "token_create", "unexpected_a", "]", 1]
4657. 24213
4658. 24213 return stop("unexpected_a", the_token);
4659. 24213 }
4660. 24213 break;
4661. 17439 case ";":
4662. 17439 if (opener_stack[0]?.for_loop) {
4663. 17439 opener_stack[0].for_loop.for_semicolon = [
4664. 17439 undefined,
4665. 17439 undefined,
4666. 17439 undefined
4667. 17439 ];
4668. 17439 }
4669. 17439 break;
4670. 286028
4671. 286028// PR-503 - Fix jslint unable to continue parsing 'async aa => 0'.
4672. 286028
4673. 33 case "=>":
4674. 33
4675. 33// PR-511 - Stop on line-break before '=>', which is a SyntaxError.
4676. 33
4677. 33 if (token_prv_expr.line !== the_token.line) {
4678. 33
4679. 33// test_cause:
4680. 33// ["()//\n =>0", "token_create", "unexpected_a", "=>", 2]
4681. 33// ["()\n =>0", "token_create", "unexpected_a", "=>", 2]
4682. 33// ["aa\n =>0", "token_create", "unexpected_a", "=>", 2]
4683. 33
4684. 33 return stop("unexpected_a", the_token);
4685. 33 }
4686. 33 if (token_prv_expr.identifier) {
4687. 33 token_prv_expr.fart = the_token;
4688. 33 }
4689. 33 break;
4690. 12078 case "}":
4691. 12078 if (opener_stack[0]?.id === "{" || opener_stack[0]?.id === "${") {
4692. 12078 opener_stack.shift();
4693. 12078 }
4694. 12078 break;
4695. 286020 }
4696. 286020 switch (token_prv_expr.id + " " + id) {
4697. 286020 case ") =>":
4698. 23 opener_popped.fart = the_token;
4699. 23 break;
4700. 241 case "] =":
4701. 241 opener_popped.assignment = the_token;
4702. 241 break;
4703. 286028
4704. 286028// PR-508 - Add ES2018-feature Asynchronous Iteration - for await...of.
4705. 286028
4706. 8 case "await (":
4707. 8 if (token_prv_expr.for_loop) {
4708. 8 the_token.for_loop = token_prv_expr.for_loop;
4709. 8 delete token_prv_expr.for_loop;
4710. 8 }
4711. 8 break;
4712. 90 case "for (":
4713. 286028
4714. 286028// PR-508 - Add ES2018-feature Asynchronous Iteration - for await...of.
4715. 286028
4716. 96 case "for await":
4717. 96 the_token.for_loop = token_prv_expr;
4718. 96 break;
4719. 286020 }
4720. 286020
4721. 286020// The previous token is used to detect adjacency problems.
4722. 286020
4723. 286020 token_prv = the_token;
4724. 286020
4725. 286020// The token_prv_expr token is a previous token that was not a comment.
4726. 286020// The token_prv_expr token
4727. 286020// is used to disambiguate "/", which can mean division or regular expression
4728. 286020// literal.
4729. 286020
4730. 286020 if (token_prv.id !== "(comment)") {
4731. 270348 token_prv_expr = token_prv;
4732. 286020 }
4733. 286020 return the_token;
4734. 286020 }
4735. 1050
4736. 1050// Init global_dict and option_dict.
4737. 1050
4738. 1050 option_set_item("ecma", true);
4739. 2972 Object.keys(option_dict).sort().forEach(function (key) {
4740. 2972 option_set_item(key, option_dict[key] === true);
4741. 2972 });
4742. 1050 object_assign_from_list(global_dict, global_list, "user-defined");
4743. 1050
4744. 1050// Scan first line for "#!" and ignore it.
4745. 1050
4746. 2 if (line_list[jslint_fudge].line_source.startsWith("#!")) {
4747. 2
4748. 2// test_cause:
4749. 2// ["#!/usr/bin/env node", "jslint_phase2_lex", "(live)", "", 0]
4750. 2
4751. 2 line += 1;
4752. 2 state.mode_shebang = true;
4753. 2 }
4754. 1050 token_1 = lex_token();
4755. 1015 state.mode_json = token_1.id === "{" || token_1.id === "[";
4756. 1050
4757. 1050// Lex/loop through each token until (end).
4758. 1050
4759. 278715 while (true) {
4760. 278715 if (lex_token().id === "(end)") {
4761. 278715 break;
4762. 278715 }
4763. 278715 }
4764. 1050}
4765. 23
4766. 994function jslint_phase3_parse(state) {
4767. 994
4768. 994// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
4769. 994
4770. 994// Parsing:
4771. 994
4772. 994// Parsing weaves the tokens into an abstract syntax tree. During that process,
4773. 994// a token may be given any of these properties:
4774. 994
4775. 994// arity string
4776. 994// name identifier
4777. 994// name_alias identifier
4778. 994// expression expressions
4779. 994// block statements
4780. 994// else statements (else, default, catch)
4781. 994
4782. 994// Specialized tokens may have additional properties.
4783. 994
4784. 994 const {
4785. 994 artifact,
4786. 994 block_stack,
4787. 994 export_dict,
4788. 994 function_list,
4789. 994 function_stack,
4790. 994 global_dict,
4791. 994 import_list,
4792. 994 is_equal,
4793. 994 line_list,
4794. 994 option_dict,
4795. 994 property_dict,
4796. 994 scope_block_pop,
4797. 994 scope_block_push,
4798. 994 scope_function_pop,
4799. 994 scope_function_push,
4800. 994 scope_report_push,
4801. 994 stop,
4802. 994 syntax_dict,
4803. 994 tenure,
4804. 994 token_global,
4805. 994 token_list,
4806. 994 warn,
4807. 994 warn_at,
4808. 994 warning_list
4809. 994 } = state;
4810. 994 let anon = "anonymous"; // The guessed name for anonymous functions.
4811. 994 let mode_var; // "var" if using var; "let" if using let.
4812. 994 let scope_block; // The current block-scope.
4813. 994 let scope_function; // The current function-scope.
4814. 994 let token_ii = 0; // The index of the next token in <token_list>.
4815. 994 let token_now = token_global; // The current token being examined in
4816. 994 // ... the parse.
4817. 994 let token_nxt = token_global; // The next token to be examined in
4818. 994 // ... <token_list>.
4819. 994 let token_prv = token_global; // The previous token examined.
4820. 994
4821. 270949 function advance(id, match) {
4822. 270949
4823. 270949// Produce the next token.
4824. 270949
4825. 270949// Attempt to give helpful names to anonymous functions.
4826. 270949
4827. 270949 if (
4828. 77046 token_now.identifier &&
4829. 77046 token_now.id !== "function" &&
4830. 74725 token_now.id !== "async"
4831. 74511 ) {
4832. 74511
4833. 74511// PR-516 - Bugfix - A keyword such as 'return' names no function, so reset
4834. 74511// <anon>, else it would keep an earlier identifier. Keep 'default', the name
4835. 74511// the spec gives 'export default function () {}', and a property name such as
4836. 74511// 'catch' in 'aa.catch' or '{catch: ...}', but not 'null' in 'aa ? null : ...'.
4837. 74511
4838. 74511 anon = (
4839. 74511 (
4840. 74511 syntax_dict[token_now.id] === undefined ||
4841. 74511 token_now.id === "default" ||
4842. 74511 token_prv.id === "." ||
4843. 74511 token_prv.id === "?." ||
4844. 74511 (
4845. 74511 token_nxt.id === ":" &&
4846. 74511 (token_prv.id === "{" || token_prv.id === ",")
4847. 74511 )
4848. 74511 )
4849. 74511 ? token_now.id
4850. 74511 : "anonymous"
4851. 74511 );
4852. 196438 } else if (
4853. 196438 token_now.id === "(string)" &&
4854. 196438 jslint_rgx_identifier.test(token_now.value)
4855. 196438 ) {
4856. 196438 anon = token_now.value;
4857. 196438 }
4858. 270949
4859. 270949// Attempt to match token_nxt with an expected id.
4860. 270949
4861. 134217 if (id !== undefined && token_nxt.id !== id) {
4862. 28 return (
4863. 28 match === undefined
4864. 28
4865. 28// test_cause:
4866. 28// ["{0:0}", "advance", "expected_a_b", "0", 2]
4867. 28
4868. 28 ? stop("expected_a_b", token_nxt, id, artifact())
4869. 28
4870. 28// test_cause:
4871. 28// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 7]
4872. 28
4873. 28 : stop(
4874. 28 "expected_a_b_from_c_d",
4875. 28 token_nxt,
4876. 28 id,
4877. 28 artifact(match),
4878. 28 match.line,
4879. 28 artifact()
4880. 28 )
4881. 28 );
4882. 270921 }
4883. 270921
4884. 270921// Promote the tokens, skipping comments.
4885. 270921
4886. 270921 token_prv = token_now;
4887. 270921 token_now = token_nxt;
4888. 286589 while (true) {
4889. 286589 token_nxt = token_list[token_ii];
4890. 286589 state.token_nxt = token_nxt;
4891. 286589 token_ii += 1;
4892. 286589 if (token_nxt.id !== "(comment)") {
4893. 286589 if (token_nxt.id === "(end)") {
4894. 286589 token_ii -= 1;
4895. 286589 }
4896. 286589 break;
4897. 286589 }
4898. 286589 if (state.mode_json) {
4899. 286589
4900. 286589// test_cause:
4901. 286589// ["[//]", "advance", "unexpected_a", "(comment)", 2]
4902. 286589
4903. 286589 warn("unexpected_a");
4904. 286589 }
4905. 286589 }
4906. 270949 }
4907. 994
4908. 15904 function assignment(id) {
4909. 15904
4910. 15904// Create an assignment operator. The one true assignment is different because
4911. 15904// its left side, when it is a variable, is not treated as an expression.
4912. 15904// That case is special because that is when a variable gets initialized. The
4913. 15904// other assignment operators can modify, but they cannot initialize.
4914. 15904
4915. 15904 const the_symbol = symbol(id, 20);
4916. 5338 the_symbol.led_infix = function parse_assignment_led(left) {
4917. 5338 const the_token = token_now;
4918. 5338 let right;
4919. 5338 the_token.arity = "assignment";
4920. 5338 right = parse_expression(20 - 1);
4921. 4562 if (id === "=" && left.arity === "variable") {
4922. 3037 the_token.expression = right;
4923. 3037 the_token.name_list = []; // 1. name_list for "aa = ..."
4924. 3037 name_declare(
4925. 3037 undefined, // scope_declared
4926. 3037 "variable", // role
4927. 3037 false, // readonly
4928. 3037 the_token.name_list, // name_list
4929. 3037 left, // name
4930. 3037 false // assigned
4931. 3037 );
4932. 3037 } else {
4933. 2297 the_token.expression = [left, right];
4934. 5334 }
4935. 5334 if (
4936. 5334 right.arity === "assignment" ||
4937. 5334 right.arity === "preassign" ||
4938. 5334 right.arity === "postassign"
4939. 1 ) {
4940. 1
4941. 1// test_cause:
4942. 1// ["aa=aa++", "parse_assignment_led", "unexpected_a", "++", 6]
4943. 1
4944. 1 warn("unexpected_a", right);
4945. 5334 }
4946. 5334 check_mutation(left);
4947. 5334 return the_token;
4948. 5334 };
4949. 15904 return the_symbol;
4950. 15904 }
4951. 994
4952. 6478 function block(special) {
4953. 6478
4954. 6478// Parse a block, a sequence of statements wrapped in braces.
4955. 6478// special "body" The block is a function body.
4956. 6478// "ignore" No warning on an empty block '{}'.
4957. 6478// "naked" No advance '{'.
4958. 6478// undefined An ordinary block.
4959. 6478
4960. 6478 const implicit = ( // No advance '{'. No advance '}'.
4961. 30 token_nxt.id !== "{" && special !== "body" && special !== "naked"
4962. 6478 );
4963. 6478 let parsed_block;
4964. 6478 let the_block;
4965. 25 if (implicit) {
4966. 25
4967. 25// PR-504 - Add implicit scope_block for:
4968. 25// - if-else
4969. 25// - for-loop
4970. 25// - while-loop
4971. 25// - do-while
4972. 25
4973. 25// test_cause:
4974. 25// ["do;while(0)", "block", "expected_a", "{", 3]
4975. 25// ["for(;;);", "block", "expected_a", "{", 8]
4976. 25// ["if(0);else if(0);else;", "block", "expected_a", "{", 17]
4977. 25// ["if(0);else if(0);else;", "block", "expected_a", "{", 22]
4978. 25// ["if(0);else if(0);else;", "block", "expected_a", "{", 6]
4979. 25// ["while(0);", "block", "expected_a", "{", 9]
4980. 25
4981. 25 warn("expected_a", token_nxt, "{");
4982. 25 }
4983. 6453 if (!implicit && special !== "naked") {
4984. 6449 advance("{");
4985. 6477 }
4986. 6477 the_block = token_now;
4987. 6477 the_block.scope_block = true;
4988. 6477 scope_block = scope_block_push(the_block, true);
4989. 6477 if (special !== "body") {
4990. 4139 check_ordered_var(the_block);
4991. 6477 }
4992. 6477 the_block.arity = "statement";
4993. 6477 the_block.function_body = special === "body";
4994. 6477
4995. 6477// Top level function bodies may include the "use strict" pragma.
4996. 6477
4997. 6477 if (
4998. 6477 special === "body" &&
4999. 2338 function_stack.length === 2 &&
5000. 437 token_nxt.value === "use strict"
5001. 4 ) {
5002. 4 token_nxt.statement = true;
5003. 4 advance("(string)");
5004. 4 semicolon();
5005. 6477 }
5006. 6477 parsed_block = parse_statement_block();
5007. 6477 the_block.block = parsed_block;
5008. 6477 if (parsed_block.length === 0) {
5009. 148 if (!option_dict.devel && special !== "ignore") {
5010. 148
5011. 148// test_cause:
5012. 148// ["function aa(){}", "block", "empty_block", "{", 14]
5013. 148
5014. 148 warn("empty_block", the_block);
5015. 148 }
5016. 148 the_block.disrupt = false;
5017. 6326 } else {
5018. 6326 the_block.disrupt = parsed_block[parsed_block.length - 1].disrupt;
5019. 6474 }
5020. 6474 scope_block = scope_block_pop();
5021. 6474 if (!implicit) {
5022. 6449 advance("}");
5023. 6474 }
5024. 6474 return the_block;
5025. 6474 }
5026. 994
5027. 25785 function check_left(left, right) {
5028. 25785
5029. 25785// Warn if the left is not one of these:
5030. 25785// ?.
5031. 25785// ?:
5032. 25785// e()
5033. 25785// e.b
5034. 25785// e[b]
5035. 25785// identifier
5036. 25785
5037. 25785 const id = left.id;
5038. 25785 if (
5039. 6987 !left.identifier &&
5040. 6987 (
5041. 6987 left.arity !== "ternary" ||
5042. 6987 (
5043. 6987 !check_left(left.expression[1]) &&
5044. 6987 !check_left(left.expression[2])
5045. 6987 )
5046. 6987 ) &&
5047. 6987 (
5048. 6987 left.arity !== "binary" ||
5049. 6987 (id !== "." && id !== "?." && id !== "(" && id !== "[")
5050. 6987 )
5051. 16 ) {
5052. 16 warn("unexpected_a", right || token_nxt);
5053. 16 return false;
5054. 25769 }
5055. 25769 return true;
5056. 25769 }
5057. 994
5058. 5344 function check_mutation(the_thing) {
5059. 5344
5060. 5344// The only expressions that may be assigned to are
5061. 5344// e.b
5062. 5344// e[b]
5063. 5344// v
5064. 5344// [destructure]
5065. 5344// {destructure}
5066. 5344
5067. 5344 const the_optional = optional_chain(the_thing);
5068. 5344 if (
5069. 1638 the_thing.arity !== "variable" &&
5070. 1638 the_thing.id !== "." &&
5071. 221 the_thing.id !== "[" &&
5072. 10 the_thing.id !== "{"
5073. 10 ) {
5074. 10
5075. 10// test_cause:
5076. 10// [" 0=0", "check_mutation", "bad_assignment_a", "0", 2]
5077. 10// ["0=0", "check_mutation", "bad_assignment_a", "0", 1]
5078. 10
5079. 10 warn("bad_assignment_a", the_thing);
5080. 10 return false;
5081. 5334 }
5082. 5334 if (the_optional) {
5083. 2
5084. 2// PR-514 - Bugfix - An optional-chain is never an assignment target, so
5085. 2// 'aa?.bb.cc = 0' and 'aa?.[bb] = 0' are SyntaxErrors too.
5086. 2
5087. 2// test_cause:
5088. 2// ["aa?.[aa]=0", "check_mutation", "bad_assignment_a", "?.", 5]
5089. 2// ["aa?.aa.aa=0", "check_mutation", "bad_assignment_a", "?.", 3]
5090. 2
5091. 2 warn("bad_assignment_a", the_optional, "?.");
5092. 2 return false;
5093. 5332 }
5094. 5332 return true;
5095. 5332 }
5096. 994
5097. 3107 function check_not_top_level(thing) {
5098. 3107
5099. 3107// Some features should not be at the outermost level.
5100. 3107
5101. 81 if (scope_function === token_global) {
5102. 81
5103. 81// test_cause:
5104. 81// [" for(;;){}", "check_not_top_level", "unexpected_at_top_level_a", "for", 2]
5105. 81// ["
5106. 81// while(0){}
5107. 81// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1]
5108. 81
5109. 81 warn("unexpected_at_top_level_a", thing);
5110. 81 }
5111. 3107 }
5112. 994
5113. 4245 function check_ordered(type, token_list) {
5114. 4245
5115. 4245// This function will warn if <token_list> is unordered.
5116. 4245
5117. 4245 token_list
5118. 4555 .filter(function (token) {
5119. 4555
5120. 4555// Issue #401 - Regression - Ignore tokens prefixed by ellipsis for sorting.
5121. 4555
5122. 4490 return token && !token.ellipsis;
5123. 4555 })
5124. 4483 .reduce(function (aa, token) {
5125. 4483 const bb = artifact(token);
5126. 4469 if (!option_dict.unordered && aa > bb) {
5127. 8 warn("expected_a_b_before_c_d", token, type, bb, type, aa);
5128. 8 }
5129. 4483 return bb;
5130. 4483 }, "");
5131. 4245 }
5132. 994
5133. 1591 function check_ordered_case(case_list) {
5134. 1591
5135. 1591// This function will warn if <case_list> is unordered.
5136. 1591
5137. 3235 case_list.filter(noop).map(function (token) {
5138. 3167 switch (token.identifier || token.id) {
5139. 46 case "(number)":
5140. 46 return {
5141. 46 order: 1,
5142. 46 token,
5143. 46 type: "number",
5144. 46 value: Number(artifact(token))
5145. 46 };
5146. 3109 case "(string)":
5147. 3109 return {
5148. 3109 order: 2,
5149. 3109 token,
5150. 3109 type: "string",
5151. 3109 value: artifact(token)
5152. 3109 };
5153. 68 case true:
5154. 68 return {
5155. 68 order: 3,
5156. 68 token,
5157. 68 type: "identifier",
5158. 68 value: artifact(token)
5159. 68 };
5160. 3235 }
5161. 3235 }).reduce(function (aa, bb) {
5162. 3235 if (
5163. 3235
5164. 3235// PR-419 - Hide warning about unordered case-statements behind beta-flag.
5165. 3235
5166. 3235 option_dict.beta &&
5167. 3227 !option_dict.unordered &&
5168. 3227 aa && bb &&
5169. 1638 (
5170. 1638 aa.order > bb.order ||
5171. 1638 (aa.order === bb.order && aa.value > bb.value)
5172. 1638 )
5173. 10 ) {
5174. 10 warn(
5175. 10 "expected_a_b_before_c_d",
5176. 10 bb.token,
5177. 10 `case-${bb.type}`,
5178. 10 bb.value,
5179. 10 `case-${aa.type}`,
5180. 10 aa.value
5181. 10 );
5182. 10 }
5183. 3235 return bb;
5184. 3235 }, undefined);
5185. 1591 }
5186. 994
5187. 27371 function check_ordered_var(the_variable) {
5188. 27371
5189. 27371// This function will check:
5190. 27371// - const and let declarations are on top of scope_block.
5191. 27371// - const and let declarations are ordered within scope_block
5192. 27371// - var declarations are on top of scope_function.
5193. 27371// - var declarations are ordered within scope_function
5194. 27371
5195. 2662 function check(variable_prv, bb) {
5196. 2662 switch (
5197. 1664 Boolean(variable_prv) &&
5198. 1664 !variable_prv.for_init &&
5199. 1663 variable_prv.id
5200. 2662 ) {
5201. 547 case "const":
5202. 1604 case "let":
5203. 1605 case "var":
5204. 1605 if (
5205. 1605 option_dict.beta &&
5206. 1605 !option_dict.unordered &&
5207. 1605 !option_dict.variable &&
5208. 1605 variable_prv &&
5209. 1605 (
5210. 1605 variable_prv.id + " " + variable_prv.name_list[0].id >
5211. 1605 the_variable.id + " " + the_variable.name_list[0].id
5212. 1605 )
5213. 1605 ) {
5214. 1605
5215. 1605// test_cause:
5216. 1605// ["const bb=0;const aa=0", "check", "expected_a_b_before_c_d", "aa", 12]
5217. 1605// ["let bb;let aa", "check", "expected_a_b_before_c_d", "aa", 8]
5218. 1605// ["var bb;var aa", "check", "expected_a_b_before_c_d", "aa", 8]
5219. 1605
5220. 1605 warn(
5221. 1605 "expected_a_b_before_c_d",
5222. 1605 the_variable,
5223. 1605 the_variable.id,
5224. 1605 the_variable.name_list[0].id,
5225. 1605 variable_prv.id,
5226. 1605 variable_prv.name_list[0].id
5227. 1605 );
5228. 1605 }
5229. 1605 break;
5230. 4 case "import":
5231. 1003 case false:
5232. 1003
5233. 1003// test_cause:
5234. 1003// ["import \"aa\";let aa", "check", "(live)", "", 0]
5235. 1003
5236. 1003 break;
5237. 54 default:
5238. 54 if (
5239. 54 (variable_prv.id !== "{" || the_variable.id === "var") &&
5240. 54 option_dict.beta &&
5241. 54 !option_dict.variable
5242. 54 ) {
5243. 54
5244. 54// test_cause:
5245. 54// ["String();const aa=0", "check", "var_on_top_a_b", "block", 10]
5246. 54// ["String();let aa", "check", "var_on_top_a_b", "block", 10]
5247. 54// ["String();var aa", "check", "var_on_top_a_b", "function", 10]
5248. 54// ["try{}catch{var aa}", "check", "var_on_top_a_b", "function", 12]
5249. 54// ["while(0){var aa}", "check", "var_on_top_a_b", "function", 10]
5250. 54
5251. 54 warn("var_on_top_a_b", the_variable, the_variable.id, bb);
5252. 54 }
5253. 2662 }
5254. 2662 }
5255. 27371 switch (the_variable.id) {
5256. 877 case "const":
5257. 2636 case "let":
5258. 2636 check(scope_block.variable_prv, "block");
5259. 2636 scope_block.variable_prv = the_variable;
5260. 2636 break;
5261. 26 case "var":
5262. 26 check(scope_function.variable_prv, "function");
5263. 26 scope_function.variable_prv = the_variable;
5264. 26 break;
5265. 24709 default:
5266. 24709 scope_block.variable_prv = the_variable;
5267. 24709 scope_function.variable_prv = the_variable;
5268. 27371 }
5269. 27371 }
5270. 994
5271. 3673 function condition() {
5272. 3673
5273. 3673// Parse the condition part of a do, if, while.
5274. 3673
5275. 3673 const the_paren = token_nxt;
5276. 3673 let the_value;
5277. 3673
5278. 3673// test_cause:
5279. 3673// ["do{}while()", "condition", "(live)", "", 0]
5280. 3673// ["if(){}", "condition", "(live)", "", 0]
5281. 3673// ["while(){}", "condition", "(live)", "", 0]
5282. 3673
5283. 3673 advance("(");
5284. 3673 the_value = parse_expression(0);
5285. 3673 advance(")");
5286. 1 if (the_value.wrapped === true) {
5287. 1
5288. 1// test_cause:
5289. 1// ["while((0)){}", "condition", "unexpected_a", "(", 6]
5290. 1
5291. 1 warn("unexpected_a", the_paren);
5292. 3669 }
5293. 3669
5294. 3669// Check for anticondition.
5295. 3669
5296. 3669 switch (the_value.id) {
5297. 3669 case "%":
5298. 11
5299. 11// test_cause:
5300. 11// ["if(0%0){}", "condition", "unexpected_a", "%", 5]
5301. 11
5302. 11 warn("unexpected_a", the_value);
5303. 11 break;
5304. 2 case "&":
5305. 2
5306. 2// test_cause:
5307. 2// ["if(0&0){}", "condition", "unexpected_a", "&", 5]
5308. 2
5309. 2 warn("unexpected_a", the_value);
5310. 2 break;
5311. 44 case "(number)":
5312. 44
5313. 44// test_cause:
5314. 44// ["if(0){}", "condition", "unexpected_a", "0", 4]
5315. 44
5316. 44 warn("unexpected_a", the_value);
5317. 44 break;
5318. 1 case "(string)":
5319. 1
5320. 1// test_cause:
5321. 1// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4]
5322. 1
5323. 1 warn("unexpected_a", the_value);
5324. 1 break;
5325. 1 case "*":
5326. 1
5327. 1// test_cause:
5328. 1// ["if(0*0){}", "condition", "unexpected_a", "*", 5]
5329. 1
5330. 1 warn("unexpected_a", the_value);
5331. 1 break;
5332. 1 case "+":
5333. 1
5334. 1// test_cause:
5335. 1// ["if(0+0){}", "condition", "unexpected_a", "+", 5]
5336. 1
5337. 1 warn("unexpected_a", the_value);
5338. 1 break;
5339. 1 case "-":
5340. 1
5341. 1// test_cause:
5342. 1// ["if(0-0){}", "condition", "unexpected_a", "-", 5]
5343. 1
5344. 1 warn("unexpected_a", the_value);
5345. 1 break;
5346. 1 case "/":
5347. 1
5348. 1// test_cause:
5349. 1// ["if(0/0){}", "condition", "unexpected_a", "/", 5]
5350. 1
5351. 1 warn("unexpected_a", the_value);
5352. 1 break;
5353. 1 case "<<":
5354. 1
5355. 1// test_cause:
5356. 1// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5]
5357. 1
5358. 1 warn("unexpected_a", the_value);
5359. 1 break;
5360. 1 case ">>":
5361. 1
5362. 1// test_cause:
5363. 1// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5]
5364. 1
5365. 1 warn("unexpected_a", the_value);
5366. 1 break;
5367. 1 case ">>>":
5368. 1
5369. 1// test_cause:
5370. 1// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5]
5371. 1
5372. 1 warn("unexpected_a", the_value);
5373. 1 break;
5374. 1 case "?":
5375. 1
5376. 1// test_cause:
5377. 1// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5]
5378. 1
5379. 1 warn("unexpected_a", the_value);
5380. 1 break;
5381. 1 case "^":
5382. 1
5383. 1// test_cause:
5384. 1// ["if(0^0){}", "condition", "unexpected_a", "^", 5]
5385. 1
5386. 1 warn("unexpected_a", the_value);
5387. 1 break;
5388. 1 case "typeof":
5389. 1
5390. 1// test_cause:
5391. 1// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4]
5392. 1
5393. 1 warn("unexpected_a", the_value);
5394. 1 break;
5395. 1 case "|":
5396. 1
5397. 1// test_cause:
5398. 1// ["if(0|0){}", "condition", "unexpected_a", "|", 5]
5399. 1
5400. 1 warn("unexpected_a", the_value);
5401. 1 break;
5402. 1 case "~":
5403. 1
5404. 1// test_cause:
5405. 1// ["if(~0){}", "condition", "unexpected_a", "~", 4]
5406. 1
5407. 1 warn("unexpected_a", the_value);
5408. 1 break;
5409. 3669 }
5410. 3669 return the_value;
5411. 3669 }
5412. 994
5413. 15904 function constant(id, type, value) {
5414. 15904
5415. 15904// Create a constant symbol.
5416. 15904
5417. 15904 const the_symbol = symbol(id);
5418. 15904 the_symbol.constant = true;
5419. 15904 the_symbol.nud_prefix = (
5420. 15904 typeof value === "function"
5421. 6958 ? value
5422. 20190 : function () {
5423. 20190 token_now.constant = true;
5424. 8946 if (value !== undefined) {
5425. 8946 token_now.value = value;
5426. 8946 }
5427. 20190 return token_now;
5428. 20190 }
5429. 15904 );
5430. 15904 the_symbol.type = type;
5431. 15904 the_symbol.value = value;
5432. 15904 return the_symbol;
5433. 15904 }
5434. 994
5435. 5 function constant_Function() {
5436. 2 if (!option_dict.eval) {
5437. 2
5438. 2// test_cause:
5439. 2// [" Function", "constant_Function", "unexpected_a", "Function", 2]
5440. 2// ["Function", "constant_Function", "unexpected_a", "Function", 1]
5441. 2
5442. 2 warn("unexpected_a", token_now);
5443. 3 } else if (token_nxt.id !== "(") {
5444. 3
5445. 3// test_cause:
5446. 3// ["
5447. 3// /*jslint eval*/
5448. 3// Function
5449. 3// ", "constant_Function", "expected_a_before_b", "(end)", 8]
5450. 3
5451. 3 warn("expected_a_before_b", token_nxt, "(", artifact());
5452. 3 }
5453. 5 return token_now;
5454. 5 }
5455. 994
5456. 2 function constant_arguments() {
5457. 2
5458. 2// test_cause:
5459. 2// [" arguments", "constant_arguments", "unexpected_a", "arguments", 2]
5460. 2// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1]
5461. 2
5462. 2 warn("unexpected_a", token_now);
5463. 2 return token_now;
5464. 2 }
5465. 994
5466. 5 function constant_eval() {
5467. 2 if (!option_dict.eval) {
5468. 2
5469. 2// test_cause:
5470. 2// [" eval", "constant_eval", "unexpected_a", "eval", 2]
5471. 2// ["eval", "constant_eval", "unexpected_a", "eval", 1]
5472. 2
5473. 2 warn("unexpected_a", token_now);
5474. 3 } else if (token_nxt.id !== "(") {
5475. 3
5476. 3// test_cause:
5477. 3// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 4]
5478. 3
5479. 3 warn("expected_a_before_b", token_nxt, "(", artifact());
5480. 3 }
5481. 5 return token_now;
5482. 5 }
5483. 994
5484. 2 function constant_ignore() {
5485. 2
5486. 2// test_cause:
5487. 2// [" ignore", "constant_ignore", "unexpected_a", "ignore", 2]
5488. 2// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1]
5489. 2
5490. 2 warn("unexpected_a", token_now);
5491. 2 return token_now;
5492. 2 }
5493. 994
5494. 2 function constant_isInfinite() {
5495. 2
5496. 2// test_cause:
5497. 2// [" isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 2]
5498. 2// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1]
5499. 2
5500. 2 warn("expected_a_b", token_now, "Number.isFinite", "isFinite");
5501. 2 return token_now;
5502. 2 }
5503. 994
5504. 2 function constant_isNaN() {
5505. 2
5506. 2// test_cause:
5507. 2// [" isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 2]
5508. 2// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1]
5509. 2
5510. 2 warn("number_isNaN", token_now);
5511. 2 return token_now;
5512. 2 }
5513. 994
5514. 4 function constant_this() {
5515. 2 if (!option_dict.this) {
5516. 2
5517. 2// test_cause:
5518. 2// [" this", "constant_this", "unexpected_a", "this", 2]
5519. 2// ["this", "constant_this", "unexpected_a", "this", 1]
5520. 2
5521. 2 warn("unexpected_a", token_now);
5522. 2 }
5523. 4 return token_now;
5524. 4 }
5525. 994
5526. 30814 function infix(bp, id, f) {
5527. 30814
5528. 30814// Create an infix operator.
5529. 30814
5530. 30814 const the_symbol = symbol(id, bp);
5531. 35729 the_symbol.led_infix = function (left) {
5532. 35729 const the_token = token_now;
5533. 35729 the_token.arity = "binary";
5534. 26047 if (f) {
5535. 26047 return f(left);
5536. 26047 }
5537. 9682 the_token.expression = [left, parse_expression(bp)];
5538. 9682 return the_token;
5539. 9682 };
5540. 30814 return the_symbol;
5541. 30814 }
5542. 994
5543. 12985 function infix_dot(left) {
5544. 12985 const the_token = token_now;
5545. 12985 let name = token_nxt;
5546. 12985 if (
5547. 12985 (
5548. 60 left.id !== "(string)" ||
5549. 60 (
5550. 60 name.id !== "at" &&
5551. 60 name.id !== "charCodeAt" &&
5552. 60 name.id !== "includes" &&
5553. 60 name.id !== "indexOf" &&
5554. 60 name.id !== "isWellFormed" &&
5555. 60 name.id !== "match" &&
5556. 60 name.id !== "repeat" &&
5557. 60 name.id !== "replace" &&
5558. 60 name.id !== "toLowerCase" &&
5559. 60 name.id !== "toUpperCase" &&
5560. 60 name.id !== "toWellFormed"
5561. 60 )
5562. 12928 ) &&
5563. 12928 (
5564. 12928 left.id !== "[" ||
5565. 12928 (
5566. 12928 name.id !== "at" &&
5567. 12928 name.id !== "concat" &&
5568. 12928 name.id !== "every" &&
5569. 12928 name.id !== "filter" &&
5570. 12928 name.id !== "find" &&
5571. 12928 name.id !== "findIndex" &&
5572. 12928 name.id !== "flat" &&
5573. 12928 name.id !== "flatMap" &&
5574. 12928 name.id !== "forEach" &&
5575. 12928 name.id !== "includes" &&
5576. 12928 name.id !== "join" &&
5577. 12928 name.id !== "map" &&
5578. 12928 name.id !== "reduce" &&
5579. 12928 name.id !== "some" &&
5580. 12928 name.id !== "toReversed" &&
5581. 12928 name.id !== "toSorted"
5582. 12928 )
5583. 12928 ) &&
5584. 12842 (left.id !== "+" || name.id !== "slice") &&
5585. 12837 (
5586. 12837 left.id !== "(regexp)" ||
5587. 12837 (name.id !== "exec" && name.id !== "test")
5588. 12837 )
5589. 12762 ) {
5590. 12762
5591. 12762// test_cause:
5592. 12762// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
5593. 12762// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
5594. 12762// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
5595. 12762// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
5596. 12762// ["new aa``.aa()", "check_left", "unexpected_a", ".", 9]
5597. 12762// ["new aa``?.aa()", "check_left", "unexpected_a", "?.", 9]
5598. 12762
5599. 12762 check_left(left, the_token);
5600. 12762 }
5601. 12985
5602. 12985// Issue #468 - Fix optional dynamic-property/function-call not recognized.
5603. 12985
5604. 12985 if (
5605. 81 the_token.id === "?." &&
5606. 81 (name.id === "[" || name.id === "(")
5607. 18 ) {
5608. 18
5609. 18// test_cause:
5610. 18// ["aa?.(bb)", "infix_dot", "(live)", "", 0]
5611. 18// ["aa?.[bb]", "infix_dot", "(live)", "", 0]
5612. 18
5613. 18// PR-514 - Bugfix - The '?.' token leaves the tree here, so mark the '[' or '('
5614. 18// that follows it, for <optional_chain>.
5615. 18
5616. 18 name.optional = true;
5617. 18 return left;
5618. 12967 }
5619. 12967 if (!name.identifier) {
5620. 6
5621. 6// test_cause:
5622. 6// ["(0+0)?.0", "infix_dot", "expected_identifier_a", "0", 8]
5623. 6// ["(0+0)?.0;", "infix_dot", "expected_identifier_a", "0", 8]
5624. 6// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4]
5625. 6// ["aa?.0", "infix_dot", "expected_identifier_a", "0", 5]
5626. 6
5627. 6 return stop("expected_identifier_a", name);
5628. 12961 }
5629. 12961 advance();
5630. 12961 survey(name);
5631. 12961
5632. 12961// The property name is not an expression.
5633. 12961
5634. 12961 the_token.name = name;
5635. 12961 the_token.expression = left;
5636. 12961 return the_token;
5637. 12961 }
5638. 994
5639. 4 function infix_fart_unwrapped() {
5640. 2 if (!token_prv.identifier) {
5641. 2
5642. 2// test_cause:
5643. 2// [" 0=>0", "infix_fart_unwrapped", "expected_identifier_a", "0", 2]
5644. 2// ["0=>0", "infix_fart_unwrapped", "expected_identifier_a", "0", 1]
5645. 2
5646. 2 return stop("expected_identifier_a", token_prv);
5647. 2 }
5648. 2
5649. 2// PR-499 - Update ES2015-feature arrow, to continue parsing unwrapped-form
5650. 2// with warning, instead of stopping.
5651. 2
5652. 2 return prefix_function(token_now, true, true);
5653. 2 }
5654. 994
5655. 13 function infix_grave(left) {
5656. 13 const the_optional = optional_chain(left);
5657. 13 const the_tick = prefix_tick(true);
5658. 1 if (the_optional) {
5659. 1
5660. 1// test_cause:
5661. 1// ["aa?.aa``", "infix_grave", "unexpected_a", "?.", 3]
5662. 1
5663. 1 warn("unexpected_a", the_optional, "?.");
5664. 1 }
5665. 13
5666. 13// test_cause:
5667. 13// ["0``", "check_left", "unexpected_a", "`", 2]
5668. 13
5669. 13 check_left(left, the_tick);
5670. 13 the_tick.expression = [left].concat(the_tick.expression);
5671. 13 return the_tick;
5672. 13 }
5673. 994
5674. 1540 function infix_lbracket(left) {
5675. 1540 const the_token = token_now;
5676. 1540 let name;
5677. 1540 let the_subscript = parse_expression(0);
5678. 1518 if (the_subscript.id === "(string)" || the_subscript.id === "`") {
5679. 24 name = survey(the_subscript);
5680. 24
5681. 24// PR-404 - Add new sub-directive "subscript" to play nice with Google Closure.
5682. 24
5683. 24 if (!option_dict.subscript && jslint_rgx_identifier.test(name)) {
5684. 24
5685. 24// test_cause:
5686. 24// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
5687. 24
5688. 24 warn("subscript_a", the_subscript, name);
5689. 24 }
5690. 24 }
5691. 1540
5692. 1540// test_cause:
5693. 1540// ["0[0]", "check_left", "unexpected_a", "[", 2]
5694. 1540
5695. 1540 check_left(left, the_token);
5696. 1540 the_token.expression = [left, the_subscript];
5697. 1540 advance("]");
5698. 1540 return the_token;
5699. 1540 }
5700. 994
5701. 11508 function infix_lparen(left) {
5702. 11508 const the_paren = token_now;
5703. 11508 let the_argument;
5704. 11469 if (left.id !== "function" && left.id !== "=>") {
5705. 11468
5706. 11468// test_cause:
5707. 11468// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
5708. 11468// ["0()", "check_left", "unexpected_a", "(", 2]
5709. 11468
5710. 11468 check_left(left, the_paren);
5711. 11468 }
5712. 11508 the_paren.expression = [left];
5713. 9757 if (token_nxt.id !== ")") {
5714. 9757
5715. 9757// Parse/loop through each token in expression (...).
5716. 9757
5717. 16162 while (true) {
5718. 16162 if (token_nxt.id === "...") {
5719. 16162
5720. 16162// test_cause:
5721. 16162// ["aa(...aa)", "infix_lparen", "(live)", "", 0]
5722. 16162
5723. 16162 the_argument = prefix_ellipsis();
5724. 16162 } else {
5725. 16162 the_argument = parse_expression(10);
5726. 16162 }
5727. 16162 the_paren.expression.push(the_argument);
5728. 16162 if (token_nxt.id !== ",") {
5729. 16162 break;
5730. 16162 }
5731. 16162 advance(",");
5732. 16162 }
5733. 11507 }
5734. 11507 advance(")", the_paren);
5735. 11507 if (the_paren.expression.length === 2) {
5736. 5558 if (
5737. 5558 the_argument.wrapped === true &&
5738. 5558
5739. 5558// PR-483 - Allow parenthesis after ellipsis inside a function call.
5740. 5558
5741. 5558 the_argument.ellipsis !== true
5742. 5558 ) {
5743. 5558
5744. 5558// test_cause:
5745. 5558// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
5746. 5558
5747. 5558 warn("unexpected_a", the_paren);
5748. 5558 }
5749. 5558 if (the_argument.id === "(") {
5750. 5558 the_argument.wrapped = true;
5751. 5558 }
5752. 11507 }
5753. 11507 return the_paren;
5754. 11507 }
5755. 994
5756. 994 function infixr(bp, id) {
5757. 994
5758. 994// Create a right associative infix operator.
5759. 994
5760. 994 const the_symbol = symbol(id, bp);
5761. 15 the_symbol.led_infix = function parse_infixr_led(left) {
5762. 15 const the_token = token_now;
5763. 15
5764. 15// test_cause:
5765. 15// ["0**0", "parse_infixr_led", "(live)", "", 0]
5766. 15
5767. 15 the_token.arity = "binary";
5768. 15 the_token.expression = [left, parse_expression(bp - 1)];
5769. 15 return the_token;
5770. 15 };
5771. 994 return the_symbol;
5772. 994 }
5773. 994
5774. 994// PR-502 - Unify name_list.push() logic with helper-function name_declare().
5775. 994
5776. 10585 function name_declare(
5777. 10585 scope_declared,
5778. 10585 role,
5779. 10585 readonly,
5780. 10585 name_list,
5781. 10585 name,
5782. 10585 assigned
5783. 10585 ) {
5784. 10585
5785. 10585// This function will:
5786. 10585// 1. Push <name> to <name_list>.
5787. 10585// 2. Set <name>.assigned, <name>.readonly and <name>.role from the arguments.
5788. 10585// An existing variable given a new value is marked by <post_a_assignment>.
5789. 10585// 3. Declare <name> in <scope_declared>.context, unless <scope_declared> is
5790. 10585// undefined, as for the plain assignment 'aa = 0'.
5791. 10585//
5792. 10585// Most calls to name_declare() are commented regarding thing being declared,
5793. 10585// and its lifecycle. Below is a copy of all such comments.
5794. 10585//
5795. 10585// 1.imp.1 - Mark 'declared', the import-name, during import-statement.
5796. 10585// 1.imp.2 - Mark 'alive', the import-name, after import-statement.
5797. 10585// 1.imp.3 - Mark 'assigned', the import-name, during import-statement.
5798. 10585// 1.imp.4 - Mark 'readonly', the import-name, during import-statement.
5799. 10585//
5800. 10585// 2.fun.1 - Mark 'declared', the function-name, during function-declaration.
5801. 10585// 2.fun.2 - Mark 'alive', the function-name, during function-declaration.
5802. 10585// 2.fun.3 - Mark 'assigned', the function-name, during function-declaration.
5803. 10585// 2.fun.4 - Mark 'readonly', the function-name, during function-declaration.
5804. 10585//
5805. 10585// 3.cat.1 - Mark 'declared', the catch-variable, before catch-block.
5806. 10585// 3.cat.2 - Mark 'alive', the catch-variable, before catch-block.
5807. 10585// 3.cat.3 - Mark 'assigned', the catch-variable, before catch-block.
5808. 10585// 3.cat.4 - Mark 'readonly', the catch-variable, before catch-block.
5809. 10585//
5810. 10585// 3.glo.1 - Mark 'declared', the global-variable, immediately.
5811. 10585// 3.glo.2 - Mark 'alive', the global-variable, immediately.
5812. 10585// 3.glo.3 - Mark 'assigned', the global-variable, immediately.
5813. 10585// 3.glo.4 - Mark 'readonly', the global-variable, immediately.
5814. 10585//
5815. 10585// 3.var.1 - Mark 'declared', the variable, during variable-declaration.
5816. 10585// 3.var.2 - Mark 'alive', the variable, after variable-declaration.
5817. 10585// 3.var.3 - Mark 'assigned', the variable, after assignment.
5818. 10585// 3.var.3 - Mark 'assigned', the variable, during destructuring.
5819. 10585// 3.var.3 - Mark 'assigned', the variable, during variable-declaration.
5820. 10585// 3.var.4 - Mark 'readonly', the variable, if const.
5821. 10585//
5822. 10585// 4.par.1 - Mark 'declared', the function-parameter, during destructuring.
5823. 10585// 4.par.1 - Mark 'declared', the function-parameter, if unwrapped.
5824. 10585// 4.par.2 - Mark 'alive', the function-parameter, after destructuring.
5825. 10585// 4.par.3 - Mark 'assigned', the function-parameter, during destructuring.
5826. 10585// 4.par.3 - Mark 'assigned', the function-parameter, if unwrapped.
5827. 10585//
5828. 10585// 5.lab.1 - Mark 'declared', the label-name, before control-flow-block.
5829. 10585// 5.lab.2 - Mark 'alive', the label-name, before control-flow-block.
5830. 10585// 5.lab.3 - Mark 'assigned', the label-name, before control-flow-block.
5831. 10585// 5.lab.4 - Mark 'readonly', the label-name, before control-flow-block.
5832. 10585//
5833. 10585// The 2.fun tags also cover a named function-expression, whose name is
5834. 10585// declared in <scope_name>, a scope outside its function.
5835. 10585//
5836. 10585// PR-514 - Deviations from the spec, reviewed 2026-09-29, each kept. A fixed
5837. 10585// one is commented at its site, and a Todo is in CHANGELOG.md.
5838. 10585//
5839. 10585// - 1.imp.2 - Kept. An import used above its import-statement warns
5840. 10585// temporal_dead_zone_a, though imports are hoisted.
5841. 10585// - 2.fun.1 - Kept. 'let aa;(function aa(){})' warns redefinition_a_b, but
5842. 10585// the reverse order does not. ESLint no-shadow is off by default.
5843. 10585// - 2.fun.4 - Kept. Reassigning a function warns, as ESLint no-func-assign.
5844. 10585// - 3.cat.4 - Kept. Reassigning a catch-variable warns, as ESLint no-ex-assign.
5845. 10585// - 3.glo.4 - Kept. Reassigning a global warns, as ESLint no-global-assign.
5846. 10585// - 3.var.1 - Kept. Unlike the spec, jslint gives a switch no block scope, so a
5847. 10585// 'let' in a case is seen after the switch. It is moot, since var_switch
5848. 10585// warns that 'let'.
5849. 10585// - 3.var.2 - Kept. A 'var' read above its declaration warns
5850. 10585// temporal_dead_zone_a, though its hoisted value 'undefined' is valid.
5851. 10585// - 3.var.2 - Kept. A function reading a 'let' declared below it warns. See
5852. 10585// the note in <name_lookup>.
5853. 10585// - 3.var.3 - Kept. '&&=' and '+=' do not assign an unassigned variable, which
5854. 10585// stays undefined or becomes NaN, so it warns unassigned_var_a. ESLint
5855. 10585// no-unassigned-vars counts every compound assignment as a write.
5856. 10585// - 4.par.1 - Kept. A parameter-default reading a body 'var' warns
5857. 10585// temporal_dead_zone_a, where the spec makes it undeclared. It still warns,
5858. 10585// and ESLint no-use-before-define likely does the same.
5859. 10585// - 5.lab.1 - Kept. A label shares the variable namespace, so a same-named
5860. 10585// variable warns redefinition_a_b, as ESLint no-label-var.
5861. 10585// - 5.lab.1 - Kept. A label is allowed only on do, for, switch and while.
5862. 10585// - 5.lab.1 - Kept. A label read as a variable warns label_a and counts as a
5863. 10585// use of the label, so it warns no unused_a as well.
5864. 10585
5865. 10585 const id = name.id;
5866. 10585 let earlier;
5867. 10585 name_list.push(name);
5868. 10585 name.assigned = assigned;
5869. 10585 name.readonly = readonly;
5870. 10585 name.role = role;
5871. 7534 if (role === "variable") {
5872. 7534 name.arity = "variable";
5873. 7534 }
5874. 3100 if (!scope_declared) {
5875. 3100 return;
5876. 7485 }
5877. 7485
5878. 7485// Declare a name into the current scope_declared's context. The role can be
5879. 7485// exception, label, parameter, or variable. We look for variable
5880. 7485// redefinition because it causes confusion.
5881. 7485
5882. 7485// Reserved words may not be declared.
5883. 7485
5884. 7485 if (syntax_dict[id] !== undefined && id !== "ignore") {
5885. 1
5886. 1// test_cause:
5887. 1// ["let undefined", "name_declare", "reserved_a", "undefined", 5]
5888. 1
5889. 1 warn("reserved_a", name);
5890. 1 return;
5891. 7484 }
5892. 7484
5893. 7484// Has the name been declared in this context?
5894. 7484
5895. 7484// PR-514 - Bugfix - Check <scope_declared>, which differs from <scope_block>
5896. 7484// for a var in a nested block. Else 'var bb' in an if-block replaced an
5897. 7484// earlier 'var bb', and a use between them warned temporal_dead_zone_a.
5898. 7484
5899. 7484 earlier = scope_block.context[id] || scope_declared.context[id];
5900. 6 if (earlier) {
5901. 6
5902. 6// test_cause:
5903. 6// ["let aa;(function aa(){})", "name_declare", "redefinition_a_b", "1", 18]
5904. 6// ["let aa;function aa(){}", "name_declare", "redefinition_a_b", "1", 17]
5905. 6// ["let aa;let aa", "name_declare", "redefinition_a_b", "1", 12]
5906. 6// ["var aa;if(0){var aa}", "name_declare", "redefinition_a_b", "1", 18]
5907. 6
5908. 6 warn("redefinition_a_b", name, id, earlier.line);
5909. 6 return;
5910. 7478 }
5911. 7478
5912. 7478// Has the name been declared in an outer context?
5913. 7478
5914. 34930 block_stack.slice(1).some(function (scope_block) {
5915. 34930 earlier = scope_block.context[id];
5916. 7478 switch (Boolean(earlier) && role) {
5917. 7478 case "label":
5918. 7478 if (earlier.role !== "label") {
5919. 7478
5920. 7478// test_cause:
5921. 7478// ["let aa;aa:while(0){}", "name_declare", "redefinition_a_b", "1", 8]
5922. 7478
5923. 7478 warn("redefinition_a_b", name, id, earlier.line);
5924. 7478 earlier = undefined;
5925. 7478 }
5926. 7478 break;
5927. 34767 case false:
5928. 34767 break;
5929. 7478 default:
5930. 7478 if (earlier.role === "label") {
5931. 7478
5932. 7478// test_cause:
5933. 7478// ["aa:while(0){let aa}", "name_declare", "redefinition_a_b", "1", 17]
5934. 7478
5935. 7478 warn("redefinition_a_b", name, id, earlier.line);
5936. 7478 earlier = undefined;
5937. 7478 }
5938. 34930 }
5939. 34930 return earlier;
5940. 34930 });
5941. 7478
5942. 7478// Declare it.
5943. 7478
5944. 7478 scope_declared.context[id] = name;
5945. 7478 name.scope_declared = scope_declared;
5946. 7478
5947. 7478// PR-516 - Bugfix - Record the function that owns <name>, since a let/const
5948. 7478// <scope_declared> may be a block inside it.
5949. 7478
5950. 7478 name.scope_function = scope_function;
5951. 7478 if (!scope_declared.name_only) {
5952. 7446
5953. 7446// PR-516 - Omit a named function expression's name from its declaring
5954. 7446// function. It sits in <scope_name>, a scope outside its own function.
5955. 7446
5956. 7446 scope_report_push(scope_function, name);
5957. 7478 }
5958. 7478
5959. 7478// Warn about variable redefinition.
5960. 7478
5961. 7478 if (earlier && id === "ignore") {
5962. 4 if (earlier.role === "variable") {
5963. 4
5964. 4// test_cause:
5965. 4// ["let ignore;(ignore)=>0", "name_declare", "redefinition_a_b", "1", 13]
5966. 4
5967. 4 warn("redefinition_a_b", name, id, earlier.line);
5968. 4 }
5969. 4 return;
5970. 7474 }
5971. 7474 if (
5972. 7474 earlier &&
5973. 156 role !== "parameter" &&
5974. 16 (role !== "exception" || earlier.role !== "exception")
5975. 16 ) {
5976. 16
5977. 16// test_cause:
5978. 16// ["for(let aa;;)let aa", "name_declare", "redefinition_a_b", "1", 18]
5979. 16// ["
5980. 16// function aa(){try{aa();}catch(aa){aa();}}
5981. 16// ", "name_declare", "redefinition_a_b", "1", 31]
5982. 16// ["function aa(){var aa}", "name_declare", "redefinition_a_b", "1", 19]
5983. 16// ["let aa;do let aa;while(0)", "name_declare", "redefinition_a_b", "1", 15]
5984. 16// ["let aa;do;while(0)let aa", "name_declare", "redefinition_a_b", "1", 23]
5985. 16// ["let aa;for(let aa;;){}", "name_declare", "redefinition_a_b", "1", 16]
5986. 16// ["
5987. 16// let aa;if(0);else if(0)let aa
5988. 16// ", "name_declare", "redefinition_a_b", "1", 28]
5989. 16// ["let aa;if(0);else let aa", "name_declare", "redefinition_a_b", "1", 23]
5990. 16// ["let aa;if(0)let aa", "name_declare", "redefinition_a_b", "1", 17]
5991. 16// ["let aa;while(0)let aa", "name_declare", "redefinition_a_b", "1", 20]
5992. 16
5993. 16 warn("redefinition_a_b", name, id, earlier.line);
5994. 16 return;
5995. 7458 }
5996. 7458 if (
5997. 7458 option_dict.beta &&
5998. 7270 global_dict[id] &&
5999. 17 role !== "parameter"
6000. 1 ) {
6001. 1
6002. 1// test_cause:
6003. 1// ["let Array", "name_declare", "redefinition_global_a_b", "Array", 5]
6004. 1
6005. 1 warn("redefinition_global_a_b", name, global_dict[id], id);
6006. 1 return;
6007. 1 }
6008. 10585 }
6009. 994
6010. 5529 function optional_chain(thing) {
6011. 5529
6012. 5529// PR-514 - This function will return the '?.' token, or the '[' or '(' token
6013. 5529// marked <optional> after a '?.', of an unwrapped optional-chain ending at
6014. 5529// <thing>. The spec forbids such a chain as an assignment target, the callee
6015. 5529// of 'new' and the tag of a template.
6016. 5529
6017. 7259 while (thing && !thing.wrapped) {
6018. 7259 if (thing.id === "?." || thing.optional === true) {
6019. 7259 return thing;
6020. 7259 }
6021. 7259 if (thing.arity !== "binary") {
6022. 7259 return;
6023. 7259 }
6024. 7259 switch (thing.id) {
6025. 7259 case "(":
6026. 7259 case "[":
6027. 7259 thing = thing.expression[0];
6028. 7259 break;
6029. 7259 case ".":
6030. 7259 thing = thing.expression;
6031. 7259 break;
6032. 7259 default:
6033. 7259
6034. 7259// test_cause:
6035. 7259// ["aa+aa=0", "optional_chain", "(live)", "", 0]
6036. 7259
6037. 7259 return;
6038. 7259 }
6039. 7259 }
6040. 5529 }
6041. 994
6042. 62622 function parse_expression(rbp, initial) {
6043. 62622
6044. 62622// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
6045. 62622// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
6046. 62622// is like .nud_prefix except that it is only used on the first token of a
6047. 62622// statement. Having .fud_stmt makes it much easier to define statement-oriented
6048. 62622// languages like JavaScript. I retained Pratt's nomenclature.
6049. 62622// They are elements of the parsing method called Top Down Operator Precedence.
6050. 62622
6051. 62622// .nud_prefix Null denotation. The prefix handler.
6052. 62622// .fud_stmt First null denotation. The statement handler.
6053. 62622// .led_infix Left denotation. The infix/postfix handler.
6054. 62622// lbp Left binding power of infix operator. It tells us how strongly
6055. 62622// the operator binds to the argument at its left.
6056. 62622// rbp Right binding power. Parser will continue consuming tokens while
6057. 62622// next token's lbp > rbp.
6058. 62622// initial Whether first token has already been advanced. If falsy,
6059. 62622// immediately calls advance().
6060. 62622
6061. 62622// It processes a nud_prefix (variable, constant, prefix operator). It will then
6062. 62622// process leds (infix operators) until the bind powers cause it to stop (it
6063. 62622// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
6064. 62622// means that it collects all tokens that bind together before returning to the
6065. 62622// operator that called it. It returns the expression's parse tree.
6066. 62622
6067. 62622// For example, "3 + 1 * 2 * 4 + 5"
6068. 62622// parses into
6069. 62622// {
6070. 62622// "id": "+",
6071. 62622// "expression": [
6072. 62622// {
6073. 62622// "id": "+",
6074. 62622// "expression": [
6075. 62622// {
6076. 62622// "id": "(number)",
6077. 62622// "value": "3"
6078. 62622// },
6079. 62622// {
6080. 62622// "id": "*",
6081. 62622// "expression": [
6082. 62622// {
6083. 62622// "id": "*",
6084. 62622// "expression": [
6085. 62622// {
6086. 62622// "id": "(number)",
6087. 62622// "value": "1"
6088. 62622// },
6089. 62622// {
6090. 62622// "id": "(number)",
6091. 62622// "value": "2"
6092. 62622// }
6093. 62622// ]
6094. 62622// },
6095. 62622// {
6096. 62622// "id": "(number)",
6097. 62622// "value": "4"
6098. 62622// }
6099. 62622// ]
6100. 62622// }
6101. 62622// ]
6102. 62622// },
6103. 62622// {
6104. 62622// "id": "(number)",
6105. 62622// "value": "5"
6106. 62622// }
6107. 62622// ]
6108. 62622// }
6109. 62622
6110. 62622 let left;
6111. 62622 let the_symbol;
6112. 62622
6113. 62622// Statements will have already advanced, so advance now only if the token is
6114. 62622// not the first of a statement.
6115. 62622
6116. 50823 if (!initial) {
6117. 50823 advance();
6118. 50823 }
6119. 62622 the_symbol = syntax_dict[token_now.id];
6120. 27182 if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
6121. 27117
6122. 27117// test_cause:
6123. 27117// ["0", "parse_expression", "(live)", "", 0]
6124. 27117
6125. 27117 left = the_symbol.nud_prefix();
6126. 35505 } else if (token_now.identifier) {
6127. 35505
6128. 35505// test_cause:
6129. 35505// ["aa", "parse_expression", "(live)", "", 0]
6130. 35505
6131. 35505 left = token_now;
6132. 35505 left.arity = "variable";
6133. 35505 } else {
6134. 35505
6135. 35505// test_cause:
6136. 35505// ["!", "parse_expression", "unexpected_a", "(end)", 1]
6137. 35505// ["/./", "parse_expression", "unexpected_a", "/", 1]
6138. 35505// ["let aa=`${}`", "parse_expression", "unexpected_a", "}", 11]
6139. 35505
6140. 35505 return stop("unexpected_a", token_now);
6141. 62579 }
6142. 62579
6143. 62579// Parse/loop through each symbol in expression.
6144. 62579
6145. 103965 while (true) {
6146. 103965 the_symbol = syntax_dict[token_nxt.id];
6147. 103965 if (
6148. 103965 the_symbol === undefined ||
6149. 103965 the_symbol.led_infix === undefined ||
6150. 103965 the_symbol.lbp <= rbp
6151. 103965 ) {
6152. 103965 break;
6153. 103965 }
6154. 103965
6155. 103965// PR-514 - Bugfix - A line break before a postfix '++' or '--' ends the
6156. 103965// expression, since the spec forbids one there and inserts a ';'. So 'aa' then
6157. 103965// '++bb' on the next line is 'aa; ++bb', not 'aa++; bb'.
6158. 103965
6159. 103965 if (
6160. 103965 (token_nxt.id === "++" || token_nxt.id === "--") &&
6161. 103965 token_nxt.line !== token_now.line
6162. 103965 ) {
6163. 103965
6164. 103965// test_cause:
6165. 103965// ["
6166. 103965// aa
6167. 103965// ++aa
6168. 103965// ", "parse_expression", "(live)", "", 0]
6169. 103965
6170. 103965 break;
6171. 103965 }
6172. 103965 advance();
6173. 103965 left = the_symbol.led_infix(left);
6174. 103965 }
6175. 62565 return left;
6176. 62565 }
6177. 994
6178. 12961 function parse_json() {
6179. 12961 let container;
6180. 12961 let is_dup;
6181. 12961 let name;
6182. 12961 let negative;
6183. 12961 switch (token_nxt.id) {
6184. 5260 case "(number)":
6185. 5260 if (!jslint_rgx_json_number.test(token_nxt.value)) {
6186. 5260
6187. 5260// test_cause:
6188. 5260// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
6189. 5260// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
6190. 5260
6191. 5260 warn("unexpected_a");
6192. 5260 }
6193. 5260 advance("(number)");
6194. 5260 return token_now;
6195. 1767 case "(string)":
6196. 1767 if (token_nxt.quote !== "\"") {
6197. 1767
6198. 1767// test_cause:
6199. 1767// ["['']", "parse_json", "unexpected_a", "'", 2]
6200. 1767
6201. 1767 warn("unexpected_a", token_nxt, token_nxt.quote);
6202. 1767 }
6203. 1767 advance("(string)");
6204. 1767 return token_now;
6205. 3 case "-":
6206. 3 negative = token_nxt;
6207. 3 negative.arity = "unary";
6208. 3 advance("-");
6209. 3
6210. 3// Recurse parse_json.
6211. 3
6212. 3 negative.expression = parse_json();
6213. 3 return negative;
6214. 1649 case "[":
6215. 1649
6216. 1649// test_cause:
6217. 1649// ["[]", "parse_json", "(live)", "", 0]
6218. 1649
6219. 1649 container = token_nxt;
6220. 1649 container.expression = [];
6221. 1649 advance("[");
6222. 1649 if (token_nxt.id !== "]") {
6223. 3299 while (true) {
6224. 3299
6225. 3299// Recurse parse_json.
6226. 3299
6227. 3299 container.expression.push(parse_json());
6228. 3299 if (token_nxt.id !== ",") {
6229. 3299 break;
6230. 3299 }
6231. 3299 advance(",");
6232. 3299 }
6233. 1649 }
6234. 1649 advance("]", container);
6235. 1649 return container;
6236. 509 case "false":
6237. 511 case "null":
6238. 896 case "true":
6239. 896
6240. 896// test_cause:
6241. 896// ["[false]", "parse_json", "(live)", "", 0]
6242. 896// ["[null]", "parse_json", "(live)", "", 0]
6243. 896// ["[true]", "parse_json", "(live)", "", 0]
6244. 896
6245. 896 advance();
6246. 896 return token_now;
6247. 3380 case "{":
6248. 3380
6249. 3380// test_cause:
6250. 3380// ["{}", "parse_json", "(live)", "", 0]
6251. 3380
6252. 3380 container = token_nxt;
6253. 3380
6254. 3380// Explicit empty-object required to detect "__proto__".
6255. 3380
6256. 3380 is_dup = empty();
6257. 3380 container.expression = [];
6258. 3380 advance("{");
6259. 3380 if (token_nxt.id !== "}") {
6260. 3380
6261. 3380// JSON
6262. 3380// Parse/loop through each property in {...}.
6263. 3380
6264. 9637 while (true) {
6265. 9637 if (token_nxt.quote !== "\"") {
6266. 9637
6267. 9637// test_cause:
6268. 9637// ["{...}", "parse_json", "unexpected_a", "...", 2]
6269. 9637// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
6270. 9637
6271. 9637 warn(
6272. 9637 "unexpected_a",
6273. 9637 token_nxt,
6274. 9637 token_nxt.quote
6275. 9637 );
6276. 9637 }
6277. 9637 name = token_nxt;
6278. 9637 advance("(string)");
6279. 9637 if (is_dup[token_now.value] !== undefined) {
6280. 9637
6281. 9637// test_cause:
6282. 9637// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
6283. 9637
6284. 9637 warn("duplicate_a", token_now);
6285. 9637 } else if (token_now.value === "__proto__") {
6286. 9637
6287. 9637// test_cause:
6288. 9637// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
6289. 9637
6290. 9637 warn("weird_property_a", token_now);
6291. 9637 } else {
6292. 9637 is_dup[token_now.value] = token_now;
6293. 9637 }
6294. 9637 advance(":");
6295. 9637 container.expression.push(
6296. 9637 Object.assign(
6297. 9637
6298. 9637// Recurse parse_json.
6299. 9637
6300. 9637 parse_json(),
6301. 9637 {
6302. 9637 name_alias: name
6303. 9637 }
6304. 9637 )
6305. 9637 );
6306. 9637 if (token_nxt.id !== ",") {
6307. 9637 break;
6308. 9637 }
6309. 9637 advance(",");
6310. 9637 }
6311. 3380 }
6312. 3380 advance("}", container);
6313. 3380 return container;
6314. 6 default:
6315. 6
6316. 6// test_cause:
6317. 6// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
6318. 6// ["[...]", "parse_json", "unexpected_a", "...", 2]
6319. 6// ["[.0]", "parse_json", "unexpected_a", ".", 2]
6320. 6// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
6321. 6
6322. 6 return stop("unexpected_a");
6323. 12961 }
6324. 12961 }
6325. 994
6326. 8883 function parse_statement_block() {
6327. 8883
6328. 8883// Parse a list of statements. Give a warning if an unreachable statement
6329. 8883// follows a disruptive statement.
6330. 8883
6331. 8883 const statement_list = [];
6332. 8883 let a_statement;
6333. 8883 let disrupt = false;
6334. 8883
6335. 8883// Parse/loop each statement until a statement-terminator is reached.
6336. 8883
6337. 31816 while (true) {
6338. 31816 switch (token_nxt.id) {
6339. 31816 case "(end)":
6340. 31816 case "case":
6341. 31816 case "default":
6342. 31816 case "else":
6343. 31816 case "}":
6344. 31816
6345. 31816// test_cause:
6346. 31816// [";", "parse_statement_block", "(live)", "", 0]
6347. 31816// ["case", "parse_statement_block", "(live)", "", 0]
6348. 31816// ["default", "parse_statement_block", "(live)", "", 0]
6349. 31816// ["else", "parse_statement_block", "(live)", "", 0]
6350. 31816// ["}", "parse_statement_block", "(live)", "", 0]
6351. 31816
6352. 31816 return statement_list;
6353. 31816 }
6354. 31816 a_statement = parse_statement_single();
6355. 31816 statement_list.push(a_statement);
6356. 31816 if (disrupt) {
6357. 31816
6358. 31816// test_cause:
6359. 31816// ["while(0){break;0}", "parse_statement_block", "unreachable_a", "0", 16]
6360. 31816
6361. 31816 warn("unreachable_a", a_statement);
6362. 31816 }
6363. 31816 disrupt = a_statement.disrupt;
6364. 31816 }
6365. 8883 }
6366. 994
6367. 23377 function parse_statement_single() {
6368. 23377
6369. 23377// Parse a statement. Any statement may have a label, but only four statements
6370. 23377// have use for one. A statement can be one of the standard statements, or
6371. 23377// an assignment expression, or an invocation expression.
6372. 23377
6373. 23377 let first;
6374. 23377 let the_statement;
6375. 23377 let the_symbol;
6376. 32 if (token_nxt.id === ";") {
6377. 32 semicolon();
6378. 32 return stmt_semicolon();
6379. 23345 }
6380. 23345 advance();
6381. 23345 if (token_now.identifier && token_nxt.id === ":") {
6382. 30 the_statement = stmt_label();
6383. 30 if (the_statement) {
6384. 30 return the_statement;
6385. 30 }
6386. 23323 }
6387. 23323
6388. 23323// Parse the statement.
6389. 23323
6390. 23323 first = token_now;
6391. 23323 first.statement = true;
6392. 23323 the_symbol = syntax_dict[first.id];
6393. 23323 if (
6394. 23323 the_symbol !== undefined &&
6395. 12480 the_symbol.fud_stmt !== undefined &&
6396. 12203
6397. 12203// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
6398. 12203
6399. 12203 !(the_symbol.id === "import" && token_nxt.id === "(")
6400. 12202 ) {
6401. 12202 the_symbol.disrupt = false;
6402. 12202 the_symbol.statement = true;
6403. 12202 token_now.arity = "statement";
6404. 12202 the_statement = the_symbol.fud_stmt();
6405. 12202 } else {
6406. 11121
6407. 11121// It is an expression statement.
6408. 11121
6409. 11121 the_statement = parse_expression(0, true);
6410. 11121 if (the_statement.wrapped && the_statement.id !== "(") {
6411. 11121
6412. 11121// test_cause:
6413. 11121// [" (0)", "parse_statement_single", "unexpected_a", "(", 2]
6414. 11121// ["(0)", "parse_statement_single", "unexpected_a", "(", 1]
6415. 11121
6416. 11121 warn("unexpected_a", first);
6417. 11121 }
6418. 11121 semicolon();
6419. 23214 }
6420. 23214 check_ordered_var(the_statement);
6421. 23214 return the_statement;
6422. 23214 }
6423. 994
6424. 1988 function postassign(id) {
6425. 1988
6426. 1988// Create one of the postassign operators.
6427. 1988
6428. 1988 const the_symbol = symbol(id, 155);
6429. 5 the_symbol.led_infix = function (left) {
6430. 5 token_now.expression = left;
6431. 5 token_now.arity = "postassign";
6432. 5 check_mutation(token_now.expression);
6433. 5 return token_now;
6434. 5 };
6435. 1988 return the_symbol;
6436. 1988 }
6437. 994
6438. 1988 function preassign(id) {
6439. 1988
6440. 1988// Create one of the preassign operators.
6441. 1988
6442. 1988 const the_symbol = symbol(id);
6443. 5 the_symbol.nud_prefix = function () {
6444. 5 const the_token = token_now;
6445. 5 the_token.arity = "preassign";
6446. 5 the_token.expression = parse_expression(150);
6447. 5 check_mutation(the_token.expression);
6448. 5 return the_token;
6449. 5 };
6450. 1988 return the_symbol;
6451. 1988 }
6452. 994
6453. 16898 function prefix(id, f) {
6454. 16898
6455. 16898// Create a prefix operator.
6456. 16898
6457. 16898 const the_symbol = symbol(id);
6458. 6900 the_symbol.nud_prefix = function () {
6459. 6900 const the_token = token_now;
6460. 6900 the_token.arity = "unary";
6461. 5870 if (f) {
6462. 5870 return f();
6463. 5870 }
6464. 1030 the_token.expression = parse_expression(150);
6465. 1030 return the_token;
6466. 1030 };
6467. 16898 return the_symbol;
6468. 16898 }
6469. 994
6470. 2 function prefix_assign_divide() {
6471. 2
6472. 2// test_cause:
6473. 2// [" /=", "prefix_assign_divide", "expected_a_b", "/=", 2]
6474. 2// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
6475. 2
6476. 2 return stop("expected_a_b", token_now, "/\\=", "/=");
6477. 2 }
6478. 994
6479. 154 function prefix_async() {
6480. 154 const the_async = token_now;
6481. 150 const the_function = token_nxt.fart || token_nxt;
6482. 154 the_function.async = 1;
6483. 154 token_nxt.arity = the_async.arity;
6484. 4 if (token_nxt.fart && token_nxt.identifier) {
6485. 1
6486. 1// PR-503 - Parse async fart (unwrapped).
6487. 1
6488. 1 advance();
6489. 1 advance("=>");
6490. 1
6491. 1// test_cause:
6492. 1// ["async aa=>0", "prefix_async", "(live)", "", 0]
6493. 1
6494. 1 prefix_function(the_function, true, true);
6495. 153 } else if (token_nxt.fart) {
6496. 153
6497. 153// PR-414 - Parse async fart.
6498. 153
6499. 153// test_cause:
6500. 153// ["aa=async(bb)=>bb", "prefix_async", "(live)", "", 0]
6501. 153
6502. 153 advance("(");
6503. 153 prefix_function(the_function, true, false);
6504. 153 } else {
6505. 153
6506. 153// Parse async function.
6507. 153
6508. 153 advance("function");
6509. 153 prefix_function(the_function, false, false);
6510. 153 }
6511. 6 if (the_function.async === 1) {
6512. 6
6513. 6// test_cause:
6514. 6// ["
6515. 6// async function aa(){}
6516. 6// ", "prefix_async", "missing_await_statement", "function", 7]
6517. 6
6518. 6 warn("missing_await_statement", the_function);
6519. 6 }
6520. 154 return the_function;
6521. 154 }
6522. 994
6523. 366 function prefix_await() {
6524. 366 const the_await = token_now;
6525. 366
6526. 366// PR-370 - Add top-level-await support.
6527. 366
6528. 10 if (scope_function.async === 0 && scope_function !== token_global) {
6529. 2
6530. 2// test_cause:
6531. 2// ["function aa(){aa=await 0}", "prefix_await", "unexpected_a", "await", 18]
6532. 2// ["function aa(){await 0}", "prefix_await", "unexpected_a", "await", 15]
6533. 2
6534. 2 warn("unexpected_a", the_await);
6535. 2 }
6536. 146 if (scope_function.async === 1) {
6537. 146 scope_function.async = 2;
6538. 146 }
6539. 366 the_await.expression = parse_expression(150);
6540. 232 if (the_await.arity === "statement") {
6541. 232
6542. 232// PR-405 - Bugfix - Fix expression after "await" mis-identified as statement.
6543. 232
6544. 232 semicolon();
6545. 232 }
6546. 366 return the_await;
6547. 366 }
6548. 994
6549. 2235 function prefix_destructure(
6550. 2235 scope_declared,
6551. 2235 role,
6552. 2235 readonly,
6553. 2235 name_list,
6554. 2235 the_function,
6555. 2235 the_function_toplevel
6556. 2235 ) {
6557. 2235 const is_assignment = scope_declared === undefined;
6558. 2235 const is_lbrace = token_now.id === "{";
6559. 2235 const sub_list = [];
6560. 2235 const the_destructure = token_now;
6561. 2235 let optional;
6562. 4017 function name_parse() {
6563. 4017 let name = token_nxt;
6564. 4017 switch (name.id) {
6565. 27 case "...":
6566. 27 advance("...");
6567. 27 name = token_nxt;
6568. 27 name.ellipsis = true;
6569. 27 if (name.id === "...") {
6570. 27
6571. 27// test_cause:
6572. 27// ["let[... ...]=0", "name_parse", "unexpected_a_after_b", "...", 9]
6573. 27
6574. 27 return stop("unexpected_a_after_b", name, name.id, "...");
6575. 27 }
6576. 27 if (optional) {
6577. 27
6578. 27// test_cause:
6579. 27// ["function aa(aa=0,...){}", "name_parse", "required_a_optional_b", "aa", 21]
6580. 27
6581. 27 warn("required_a_optional_b", name, name.id, optional.id);
6582. 27 }
6583. 27
6584. 27// test_cause:
6585. 27// ["let[...aa]=0", "name_parse", "(live)", "", 0]
6586. 27// ["let{...aa}=0", "name_parse", "(live)", "", 0]
6587. 27
6588. 27 name_parse();
6589. 27 return true;
6590. 90 case "[":
6591. 380 case "{":
6592. 380 if (the_function?.id === "=>" && !option_dict.fart) {
6593. 380
6594. 380// test_cause:
6595. 380// ["([aa])=>0", "name_parse", "use_function_not_fart", "=>", 7]
6596. 380// ["({aa})=>0", "name_parse", "use_function_not_fart", "=>", 7]
6597. 380
6598. 380 warn("use_function_not_fart", the_function);
6599. 380 }
6600. 380 if (optional) {
6601. 380
6602. 380// test_cause:
6603. 380// ["function aa(aa=0,[]){}", "name_parse", "required_a_optional_b", "aa", 18]
6604. 380// ["function aa(aa=0,{}){}", "name_parse", "required_a_optional_b", "aa", 18]
6605. 380
6606. 380 warn("required_a_optional_b", name, name.id, optional.id);
6607. 380 }
6608. 380
6609. 380// test_cause:
6610. 380// ["let[[aa]]=0", "name_parse", "(live)", "", 0]
6611. 380// ["let[{aa}]=0", "name_parse", "(live)", "", 0]
6612. 380
6613. 380 advance(token_nxt.id);
6614. 380 prefix_destructure(
6615. 380 scope_declared, // scope_declared
6616. 380 role, // role
6617. 380 readonly, // readonly
6618. 380 name_list, // name_list
6619. 380 the_function, // the_function
6620. 380 false // the_function_toplevel
6621. 380 );
6622. 380 return;
6623. 3610 }
6624. 3610 if (!name.identifier) {
6625. 13
6626. 13// test_cause:
6627. 13// ["function aa(...0){}", "name_parse", "expected_identifier_a", "0", 16]
6628. 13// ["function aa(0){}", "name_parse", "expected_identifier_a", "0", 13]
6629. 13// ["function aa([0]){}", "name_parse", "expected_identifier_a", "0", 14]
6630. 13// ["function aa({0}){}", "name_parse", "expected_identifier_a", "0", 14]
6631. 13// ["function aa({aa:0}){}", "name_parse", "expected_identifier_a", "0", 17]
6632. 13// ["let[...0]=0", "name_parse", "expected_identifier_a", "0", 8]
6633. 13// ["let[0]=0", "name_parse", "expected_identifier_a", "0", 5]
6634. 13// ["let{0}=0", "name_parse", "expected_identifier_a", "0", 5]
6635. 13
6636. 13 return stop("expected_identifier_a", name);
6637. 3597 }
6638. 3597 if (is_lbrace) {
6639. 1208 survey(name);
6640. 3597 }
6641. 3597 advance(token_nxt.id);
6642. 3597 if (is_lbrace && token_nxt.id === ":") {
6643. 22 advance(":");
6644. 22 if (!the_function_toplevel) {
6645. 22 the_destructure.open = true;
6646. 22 }
6647. 22 if (token_nxt.id === "...") {
6648. 22
6649. 22// test_cause:
6650. 22// ["let{aa:...aa}=0", "name_parse", "unexpected_a", "...", 8]
6651. 22
6652. 22 return stop("unexpected_a", token_nxt);
6653. 22 }
6654. 22 if (!token_nxt.identifier) {
6655. 22
6656. 22// test_cause:
6657. 22// ["let{aa:[aa]}=0", "name_parse", "(live)", "", 0]
6658. 22// ["let{aa:{aa}}=0", "name_parse", "(live)", "", 0]
6659. 22
6660. 22 name_parse();
6661. 22 return;
6662. 22 }
6663. 22 token_nxt.name_alias = name;
6664. 22 name = token_nxt;
6665. 22 name_declare(
6666. 22 scope_declared, // scope_declared
6667. 22 role, // role
6668. 22 readonly, // readonly
6669. 22 sub_list, // name_list
6670. 22 name, // name
6671. 22
6672. 22// 3.var.3 - Mark 'assigned', the variable, during destructuring.
6673. 22// 4.par.3 - Mark 'assigned', the function-parameter, during destructuring.
6674. 22
6675. 22 true // assigned
6676. 22 );
6677. 22 advance(token_nxt.id);
6678. 22 return;
6679. 3575 }
6680. 3575 name_declare(
6681. 3575 scope_declared, // scope_declared
6682. 3575 role, // role
6683. 3575 readonly, // readonly
6684. 3575 sub_list, // name_list
6685. 3575 name, // name
6686. 3575
6687. 3575// 3.var.3 - Mark 'assigned', the variable, during destructuring.
6688. 3575// 4.par.3 - Mark 'assigned', the function-parameter, during destructuring.
6689. 3575
6690. 3575 true // assigned
6691. 3575 );
6692. 3575 if (token_nxt.id === "=") {
6693. 131 optional = the_function_toplevel && token_now;
6694. 131 advance("=");
6695. 131 if (!the_function_toplevel) {
6696. 131 the_destructure.open = true;
6697. 131 }
6698. 131 name.expression = parse_expression(0);
6699. 131 if (is_assignment) {
6700. 131
6701. 131// PR-514 - Bugfix - No <post_s_var> walks a default in '[aa = bb] = ...', so
6702. 131// 'bb' was never used. It is pushed wrapped in an array, which has no <arity>,
6703. 131// so <prefix_lbracket> walks it and never looks it up as a target.
6704. 131
6705. 131// test_cause:
6706. 131// [";[aa=0]=0", "name_parse", "(live)", "", 0]
6707. 131
6708. 131 name_list.push([name.expression]);
6709. 131 }
6710. 131
6711. 131// test_cause:
6712. 131// ["function aa([aa=aa]){}", "name_parse", "(live)", "", 0]
6713. 131// ["function aa({aa=aa}){}", "name_parse", "(live)", "", 0]
6714. 131// ["let[aa=bb]=0;let bb", "name_parse", "(live)", "", 0]
6715. 131// ["let{aa=bb}=0;let bb", "name_parse", "(live)", "", 0]
6716. 131
6717. 131 return;
6718. 3444 }
6719. 3444 if (optional) {
6720. 2
6721. 2// test_cause:
6722. 2// ["function aa(aa=0,bb){}", "name_parse", "required_a_optional_b", "aa", 18]
6723. 2
6724. 2 warn("required_a_optional_b", name, name.id, optional.id);
6725. 2 }
6726. 4017 }
6727. 3982 while (true) {
6728. 3982 if (!is_lbrace && !the_function_toplevel && token_nxt.id === ",") {
6729. 3982
6730. 3982// test_cause:
6731. 3982// ["([,aa])=>0", "prefix_destructure", "(live)", "", 0]
6732. 3982// [";[,aa]=0", "prefix_destructure", "(live)", "", 0]
6733. 3982// ["let[,aa]=0", "prefix_destructure", "(live)", "", 0]
6734. 3982
6735. 3982 advance(",");
6736. 3982 }
6737. 3982 if (the_function_toplevel) {
6738. 3982 the_function.parameter_count += 1;
6739. 3982 }
6740. 3982
6741. 3982// test_cause:
6742. 3982// ["(,aa)=>0", "name_parse", "expected_identifier_a", ",", 2]
6743. 3982
6744. 3982 if (name_parse()) {
6745. 3982
6746. 3982// Break early from ellipsis.
6747. 3982
6748. 3982 if (the_function_toplevel) {
6749. 3982 the_function.parameter_rest = true;
6750. 3982 }
6751. 3982 break;
6752. 3982 }
6753. 3982 if (token_nxt.id !== ",") {
6754. 3982 break;
6755. 3982 }
6756. 3982 advance(",");
6757. 3982 }
6758. 2215 name_list.push(...sub_list);
6759. 2215 if (the_function_toplevel) {
6760. 1715 return the_destructure;
6761. 1715 }
6762. 500 if (is_lbrace) {
6763. 342
6764. 342// test_cause:
6765. 342// ["
6766. 342// function aa({bb,aa}){}
6767. 342// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
6768. 342// ["let{bb,aa}=0", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
6769. 342
6770. 342// PR-501 - Fix false-warning about ordering in nested-object-destructuring.
6771. 342
6772. 342 check_ordered(role, sub_list);
6773. 342 advance("}");
6774. 342 return the_destructure;
6775. 342 }
6776. 158 advance("]");
6777. 158 return the_destructure;
6778. 158 }
6779. 994
6780. 104 function prefix_ellipsis() {
6781. 104 let after_ellipsis;
6782. 104 advance("...");
6783. 104 after_ellipsis = parse_expression(0);
6784. 104 after_ellipsis.ellipsis = true;
6785. 104 return after_ellipsis;
6786. 104 }
6787. 994
6788. 2 function prefix_fart() {
6789. 2
6790. 2// test_cause:
6791. 2// [" =>0", "prefix_fart", "expected_a_before_b", "=>", 2]
6792. 2// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
6793. 2
6794. 2 return stop("expected_a_before_b", token_now, "()", "=>");
6795. 2 }
6796. 994
6797. 2368 function prefix_function(the_function, mode_fart, mode_fart_unwrapped) {
6798. 2342 const name = !mode_fart && token_nxt.identifier && token_nxt;
6799. 2368
6800. 2368// PR-504 - Change scope from scope_function to scope_block:
6801. 2368// - function-declaration
6802. 2368
6803. 2368 let scope_declared = scope_block;
6804. 2172 the_function = the_function || token_now;
6805. 2368 the_function.scope_name = {
6806. 2368 context: empty(),
6807. 2368 name_only: true
6808. 2368 };
6809. 26 if (mode_fart) {
6810. 26 the_function.arity = "binary";
6811. 26 }
6812. 1249 if (the_function.arity === "statement") {
6813. 1249
6814. 1249// A function statement must have a name that will be in the parent's scope.
6815. 1249
6816. 1249 if (!token_nxt.identifier) {
6817. 1249
6818. 1249// test_cause:
6819. 1249// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
6820. 1249
6821. 1249 return stop("expected_identifier_a", token_nxt);
6822. 1249 }
6823. 1249
6824. 1249 if (scope_function.switch > 0 && scope_block.function_body) {
6825. 1249
6826. 1249// PR-514 - Warn a function-declaration directly in a case, as ESLint
6827. 1249// no-case-declarations does. One nested in a block of the case already warns
6828. 1249// unexpected_a in <pre_s_function>.
6829. 1249
6830. 1249// test_cause:
6831. 1249// ["
6832. 1249// switch(0){case 0:function aa(){}}
6833. 1249// ", "prefix_function", "var_switch", "function", 18]
6834. 1249
6835. 1249 warn("var_switch", the_function);
6836. 1249 }
6837. 1249 } else {
6838. 1119
6839. 1119// A function expression may have an optional name.
6840. 1119
6841. 1119// PR-514 - Bugfix - Declare the name in <scope_name>, outside the function as
6842. 1119// in the spec, so a 'var' of that name in the body is a new binding.
6843. 1119
6844. 1119 scope_declared = the_function.scope_name;
6845. 2367 }
6846. 2367 if (name) {
6847. 1281 advance();
6848. 1281 name_declare(
6849. 1281
6850. 1281// 2.fun.1 - Mark 'declared', the function-name, during function-declaration.
6851. 1281
6852. 1281 scope_declared, // scope_declared
6853. 1281 "variable", // role
6854. 1281
6855. 1281// 2.fun.4 - Mark 'readonly', the function-name, during function-declaration.
6856. 1281
6857. 1281 true, // readonly
6858. 1281 [], // name_list
6859. 1281 name, // name
6860. 1281
6861. 1281// 2.fun.3 - Mark 'assigned', the function-name, during function-declaration.
6862. 1281
6863. 1281 true // assigned
6864. 1281 );
6865. 1281
6866. 1281// 2.fun.2 - Mark 'alive', the function-name, during function-declaration.
6867. 1281
6868. 1281 name.alive = true;
6869. 2367 }
6870. 2367
6871. 2367// Give the function properties for storing its names and for observing the
6872. 2367// depth of loops and switches.
6873. 2367
6874. 2367 the_function.async = the_function.async || 0;
6875. 2368 the_function.finally = 0;
6876. 2368 the_function.level = scope_function.level + 1;
6877. 2368 the_function.loop = 0;
6878. 2368
6879. 2368// PR-516 - Bugfix - Keep the name <property_parse> gives a method or accessor,
6880. 2368// 'aa' or 'get aa'. Else <anon> named it after the previous identifier.
6881. 2368
6882. 2368 the_function.name = (
6883. 1086 name ||
6884. 1086 the_function.name ||
6885. 1066 (
6886. 1066 mode_fart_unwrapped
6887. 1066 ? "anonymous"
6888. 1066 : anon
6889. 1066 )
6890. 2368 );
6891. 2368 the_function.parameter_count = Number(Boolean(mode_fart_unwrapped));
6892. 2368 the_function.signature = (
6893. 2368 mode_fart_unwrapped
6894. 3 ? token_prv.id
6895. 2364 : "("
6896. 2368 );
6897. 2368 the_function.switch = 0;
6898. 2368 the_function.try = 0;
6899. 2368
6900. 2368// PR-384 - Relax warning "function_in_loop".
6901. 2368//
6902. 2368// if (scope_function.loop > 0) {
6903. 2368
6904. 2368// // test_cause:
6905. 2368// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
6906. 2368//
6907. 2368// warn("function_in_loop", the_function);
6908. 2368// }
6909. 2368
6910. 2368// PR-504 - Add hidden scope_block for:
6911. 2368// - function-parameter
6912. 2368
6913. 2368// Push the current function context and establish a new one.
6914. 2368
6915. 2368 scope_block = scope_block_push(the_function.scope_name, false);
6916. 2368 scope_block = scope_block_push(the_function, true);
6917. 2368 scope_function = scope_function_push(the_function, true);
6918. 2368
6919. 2368// Parse the parameter list.
6920. 2368
6921. 2341 if (!mode_fart) {
6922. 2341 advance("(");
6923. 2341 token_now.arity = "function";
6924. 2367 }
6925. 2367 the_function.name_list = []; // 2. name_list for "function (aa)"
6926. 2367 if (mode_fart_unwrapped) {
6927. 3
6928. 3// test_cause:
6929. 3// [" aa=>0", "prefix_function", "wrap_fart_parameter", "aa", 2]
6930. 3// ["aa=>0", "prefix_function", "wrap_fart_parameter", "aa", 1]
6931. 3
6932. 3 warn("wrap_fart_parameter", token_prv);
6933. 3 name_declare(
6934. 3
6935. 3// 4.par.1 - Mark 'declared', the function-parameter, if unwrapped.
6936. 3
6937. 3 scope_function, // scope_declared
6938. 3 "parameter", // role
6939. 3 false, // readonly
6940. 3 the_function.name_list, // name_list
6941. 3 token_prv, // name
6942. 3
6943. 3// 4.par.3 - Mark 'assigned', the function-parameter, if unwrapped.
6944. 3
6945. 3 true // assigned
6946. 3 );
6947. 2364 } else {
6948. 2364 const lparen = token_now;
6949. 2364 let default_depth;
6950. 2364 let default_from;
6951. 2364 let default_megastring = false;
6952. 2364 let depth = 0;
6953. 2364 let signature = "";
6954. 2364 let token_before;
6955. 2364 if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
6956. 2364
6957. 2364// PR-500 - Unify ES2015-destructure-logic. - function ([aa]) {...}
6958. 2364
6959. 2364 prefix_destructure(
6960. 2364
6961. 2364// 4.par.1 - Mark 'declared', the function-parameter, during destructuring.
6962. 2364
6963. 2364 scope_function, // scope_declared
6964. 2364 "parameter", // role
6965. 2364 false, // readonly
6966. 2364 the_function.name_list, // name_list
6967. 2364 the_function, // the_function
6968. 2364 true // the_function_toplevel
6969. 2364 );
6970. 2364 }
6971. 2364 advance(")");
6972. 2364
6973. 2364// PR-516 - Bugfix - Copy <signature> from the source text of each token but
6974. 2364// comments, so it keeps default values. A space joins tokens apart in the
6975. 2364// source, except after an opener or before a comma or closer. A default value
6976. 2364// over 40 characters shows as an ellipsis, and so does one holding a
6977. 2364// megastring, whose tokens carry columns that do not match the source.
6978. 2364
6979. 10033 for (const token of token_list.slice(lparen.nr, token_now.nr + 1)) {
6980. 10033 if (token.id !== "(comment)") {
6981. 10033 const piece = (
6982. 10033 (
6983. 10033 token_before !== undefined &&
6984. 10033 (
6985. 10033 token_before.line !== token.line ||
6986. 10033 token_before.thru < token.from
6987. 10033 ) &&
6988. 10033 !["(", "[", "{"].includes(token_before.id) &&
6989. 10033 ![")", ",", "]", "}"].includes(token.id)
6990. 10033 )
6991. 10033 ? " "
6992. 10033 : ""
6993. 10033 ) + line_list[token.line].line_source.slice(
6994. 10033 token.from,
6995. 10033 token.thru
6996. 10033 );
6997. 10033 if ([")", "]", "}"].includes(token.id)) {
6998. 10033 depth -= 1;
6999. 10033 }
7000. 10033
7001. 10033// A default value ends at a comma or a closer of its own depth.
7002. 10033
7003. 10033 if (
7004. 10033 default_from !== undefined &&
7005. 10033 (
7006. 10033 depth < default_depth ||
7007. 10033 (depth === default_depth && token.id === ",")
7008. 10033 )
7009. 10033 ) {
7010. 10033 if (
7011. 10033 default_megastring ||
7012. 10033 signature.slice(default_from).trim().length > 40
7013. 10033 ) {
7014. 10033
7015. 10033// test_cause:
7016. 10033// ["function aa(bb=`cc`){return bb;}", "prefix_function", "(live)", "", 0]
7017. 10033
7018. 10033 signature = (
7019. 10033 signature.slice(0, default_from) + " \u2026"
7020. 10033 );
7021. 10033 }
7022. 10033 default_from = undefined;
7023. 10033 default_megastring = false;
7024. 10033 }
7025. 10033 signature += piece;
7026. 10033 if (token.id === "=" && default_from === undefined) {
7027. 10033 default_depth = depth;
7028. 10033 default_from = signature.length;
7029. 10033 }
7030. 10033 if (token.id === "`" && default_from !== undefined) {
7031. 10033
7032. 10033// test_cause:
7033. 10033// ["function aa(bb=`cc`){return bb;}", "prefix_function", "(live)", "", 0]
7034. 10033
7035. 10033 default_megastring = true;
7036. 10033 }
7037. 10033 if (["${", "(", "[", "{"].includes(token.id)) {
7038. 10033 depth += 1;
7039. 10033 }
7040. 10033 token_before = token;
7041. 10033 }
7042. 10033 }
7043. 2364 the_function.signature = signature;
7044. 2364 }
7045. 2357 if (mode_fart && !mode_fart_unwrapped) {
7046. 22 advance("=>");
7047. 2357 }
7048. 2357 if (mode_fart && token_nxt.id !== "{") {
7049. 18
7050. 18// The function's body is an expression.
7051. 18
7052. 18// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
7053. 18
7054. 18 if (
7055. 18 syntax_dict[token_nxt.id] &&
7056. 18 syntax_dict[token_nxt.id].fud_stmt
7057. 18 ) {
7058. 18
7059. 18// test_cause:
7060. 18// ["()=>delete aa", "prefix_function", "unexpected_a_after_b", "=>", 5]
7061. 18
7062. 18 return stop(
7063. 18 "unexpected_a_after_b",
7064. 18 token_nxt,
7065. 18 token_nxt.id,
7066. 18 "=>"
7067. 18 );
7068. 18 }
7069. 18 the_function.expression = parse_expression(0);
7070. 2339 } else if (mode_fart) {
7071. 2339
7072. 2339// The function's body is a block.
7073. 2339
7074. 2339 if (!option_dict.fart) {
7075. 2339
7076. 2339// test_cause:
7077. 2339// ["()=>{}", "prefix_function", "use_function_not_fart", "=>", 3]
7078. 2339
7079. 2339 warn("use_function_not_fart", the_function);
7080. 2339 }
7081. 2339 the_function.block = block("body");
7082. 2339 } else {
7083. 2339 the_function.block = block("body");
7084. 2339 if (
7085. 2339 the_function.arity === "statement" &&
7086. 2339 token_nxt.line === token_now.line &&
7087. 2339 !option_dict.white
7088. 2339 ) {
7089. 2339
7090. 2339// PR-503 - Fix jslint unable to continue parsing 'function aa(){}0'.
7091. 2339
7092. 2339// test_cause:
7093. 2339// ["function aa(){}0", "prefix_function", "expected_line_break_a_b", "0", 16]
7094. 2339// ["function aa(){}0;", "prefix_function", "expected_line_break_a_b", "0", 16]
7095. 2339
7096. 2339 warn(
7097. 2339 "expected_line_break_a_b",
7098. 2339 token_nxt,
7099. 2339 artifact(token_now),
7100. 2339 artifact(token_nxt)
7101. 2339 );
7102. 2339 }
7103. 2339 if (
7104. 2339 token_nxt.id === "." ||
7105. 2339 token_nxt.id === "?."
7106. 2339
7107. 2339// PR-459 - Allow destructuring-assignment after function-definition.
7108. 2339
7109. 2339 // || token_nxt.id === "["
7110. 2339 ) {
7111. 2339
7112. 2339// test_cause:
7113. 2339// [" function aa(){}\n .aa", "prefix_function", "unexpected_a", ".", 2]
7114. 2339// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
7115. 2339// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
7116. 2339
7117. 2339 warn("unexpected_a");
7118. 2339 }
7119. 2354 }
7120. 2354
7121. 2354// Check functions are ordered.
7122. 2354
7123. 2354// PR-514 - Bugfix - Check only function-declarations, since a named
7124. 2354// function-expression is not hoisted. Check an arrow-function's block-body too.
7125. 2354
7126. 2354// test_cause:
7127. 2354// ["
7128. 2354// ()=>{function bb(){}function aa(){}}
7129. 2354// ", "check_ordered", "expected_a_b_before_c_d", "aa", 30]
7130. 2354
7131. 2354 check_ordered(
7132. 2354 "function",
7133. 2354 function_list.slice(
7134. 2354 function_list.indexOf(the_function) + 1
7135. 2841 ).map(function ({
7136. 2841 arity,
7137. 2841 level,
7138. 2841 name
7139. 2841 }) {
7140. 2841 return (
7141. 2354 arity === "statement" &&
7142. 2354 level === the_function.level + 1 &&
7143. 2354 name
7144. 2841 );
7145. 2841 }).filter(function (name) {
7146. 2827 return option_dict.beta && name && name.id;
7147. 2841 })
7148. 2354 );
7149. 2354
7150. 2354// Restore the previous context.
7151. 2354
7152. 2354 scope_block = scope_block_pop();
7153. 2354 scope_block = scope_block_pop();
7154. 2354 scope_function = scope_function_pop();
7155. 2354 return the_function;
7156. 2354 }
7157. 994
7158. 690 function prefix_lbrace() {
7159. 690
7160. 690// PR-514 - Bugfix - <seen> maps a property-name to true, or to false if only
7161. 690// an accessor has it. Accessor-keys like 'get aa' live apart in
7162. 690// <seen_getset>, so a string-key named get aa cannot collide with them.
7163. 690
7164. 690 const seen = empty();
7165. 690 const seen_getset = empty();
7166. 690 const the_brace = token_now;
7167. 2182 function property_parse() {
7168. 2182 let extra;
7169. 2182 let full;
7170. 2182 let id;
7171. 2182 let name = token_nxt;
7172. 2182 let the_colon;
7173. 2182 let value;
7174. 157 if (!name.identifier && name.id === "`") {
7175. 1
7176. 1// test_cause:
7177. 1// ["aa={`aa`:0}", "property_parse", "unexpected_a", "`", 5]
7178. 1
7179. 1 return stop("unexpected_a", name);
7180. 2181 }
7181. 2181
7182. 2181// Issue #401 - Add ES2018-syntax for object-literal-spread-operator.
7183. 2181
7184. 2181 if (!name.identifier && token_nxt.id === "...") {
7185. 65
7186. 65// test_cause:
7187. 65// ["aa={...aa}", "property_parse", "(live)", "", 0]
7188. 65
7189. 65 value = prefix_ellipsis();
7190. 65 return value;
7191. 2116 }
7192. 2116 advance();
7193. 2116 if (
7194. 2116 (name.id === "get" || name.id === "set") &&
7195. 19 token_nxt.identifier
7196. 18 ) {
7197. 18 if (!option_dict.getset) {
7198. 18
7199. 18// test_cause:
7200. 18// ["aa={get aa(){}}", "property_parse", "unexpected_a", "get", 5]
7201. 18
7202. 18 warn("unexpected_a", name);
7203. 18 }
7204. 18 extra = name.id;
7205. 18 full = extra + " " + token_nxt.id;
7206. 18 name.getset = true;
7207. 18 name = token_nxt;
7208. 18 advance();
7209. 18 id = survey(name);
7210. 18 if (seen_getset[full] === true || seen[id] === true) {
7211. 18
7212. 18// test_cause:
7213. 18// ["aa={get aa(){},get aa(){}}", "property_parse", "duplicate_a", "aa", 20]
7214. 18
7215. 18 warn("duplicate_a", name);
7216. 18 }
7217. 18 seen[id] = false;
7218. 18 seen_getset[full] = true;
7219. 2098 } else {
7220. 2098 id = survey(name);
7221. 2098 if (typeof seen[id] === "boolean") {
7222. 2098
7223. 2098// test_cause:
7224. 2098// ["aa={aa,aa}", "property_parse", "duplicate_a", "aa", 8]
7225. 2098
7226. 2098 warn("duplicate_a", name);
7227. 2098 }
7228. 2098 seen[id] = true;
7229. 2115 }
7230. 2115 if (!name.identifier) {
7231. 90
7232. 90// test_cause:
7233. 90// ["aa={\"aa\":0}", "property_parse", "(live)", "", 0]
7234. 90
7235. 90 advance(":");
7236. 90 value = parse_expression(0);
7237. 90 value.name_alias = name;
7238. 90 return value;
7239. 2025 }
7240. 2025 if (token_nxt.id === "}" || token_nxt.id === ",") {
7241. 679 if (typeof extra === "string") {
7242. 679
7243. 679// test_cause:
7244. 679// ["aa={get aa}", "property_parse", "(live)", "", 0]
7245. 679
7246. 679 advance("(");
7247. 679 }
7248. 679 value = parse_expression(Infinity, true);
7249. 1346 } else if (token_nxt.id === "(") {
7250. 1346
7251. 1346// test_cause:
7252. 1346// ["aa={aa()}", "property_parse", "(live)", "", 0]
7253. 1346// ["aa={get aa(){}}", "property_parse", "(live)", "", 0]
7254. 1346
7255. 1346// PR-516 - Bugfix - Name an accessor 'get aa' in the report, not 'get'.
7256. 1346
7257. 1346 value = prefix_function(
7258. 1346 {
7259. 1346 arity: "unary",
7260. 1346 from: name.from,
7261. 1346 id: "function",
7262. 1346 line: name.line,
7263. 1346 name: (
7264. 1346 typeof extra === "string"
7265. 1346 ? full
7266. 1346 : id
7267. 1346 ),
7268. 1346 thru: name.from
7269. 1346 },
7270. 1346 false,
7271. 1346 false
7272. 1346 );
7273. 1346 } else {
7274. 1346 if (typeof extra === "string") {
7275. 1346
7276. 1346// test_cause:
7277. 1346// ["aa={get aa.aa}", "property_parse", "(live)", "", 0]
7278. 1346
7279. 1346 advance("(");
7280. 1346 }
7281. 1346 the_colon = token_nxt;
7282. 1346 advance(":");
7283. 1346 value = parse_expression(0);
7284. 1346 if (
7285. 1346 value.id === name.id &&
7286. 1346 value.id !== "function"
7287. 1346 ) {
7288. 1346
7289. 1346// test_cause:
7290. 1346// ["aa={aa:aa}", "property_parse", "unexpected_a", ": aa", 7]
7291. 1346
7292. 1346 warn("unexpected_a", the_colon, ": " + name.id);
7293. 1346 }
7294. 2022 }
7295. 2022 value.name_alias = name;
7296. 2022 if (typeof extra === "string") {
7297. 16 value.extra = extra;
7298. 2022 }
7299. 2022 return value;
7300. 2022 }
7301. 690 the_brace.expression = [];
7302. 625 if (token_nxt.id !== "}") {
7303. 625
7304. 625// Parse/loop through each property in {...}.
7305. 625
7306. 2182 while (true) {
7307. 2182 the_brace.expression.push(property_parse());
7308. 2182 if (token_nxt.id !== ",") {
7309. 2182 break;
7310. 2182 }
7311. 2182 advance(",");
7312. 2182 if (token_nxt.id === "}") {
7313. 2182
7314. 2182// test_cause:
7315. 2182// ["aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 9]
7316. 2182
7317. 2182 warn("unexpected_a", token_now);
7318. 2182 break;
7319. 2182 }
7320. 2182 }
7321. 685 }
7322. 685
7323. 685// test_cause:
7324. 685// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
7325. 685
7326. 685 check_ordered(
7327. 685 "property",
7328. 2177 the_brace.expression.map(function ({
7329. 2177 name_alias
7330. 2177 }) {
7331. 2177 return name_alias;
7332. 2177 })
7333. 685 );
7334. 685 advance("}");
7335. 685 return the_brace;
7336. 685 }
7337. 994
7338. 936 function prefix_lbracket() {
7339. 936 let element;
7340. 936 let the_token = token_now;
7341. 936 the_token.expression = [];
7342. 24 if (the_token.assignment) {
7343. 24 the_token = token_now.assignment;
7344. 24 the_token.name_list = []; // 3. name_list for "[aa] = ..."
7345. 24
7346. 24// PR-500 - Unify ES2015-destructure-logic. - [aa] = ...;
7347. 24
7348. 24 element = prefix_destructure(
7349. 24 undefined, // scope_declared
7350. 24 "variable", // role
7351. 24 false, // readonly
7352. 24 the_token.name_list, // name_list
7353. 24 undefined, // the_function
7354. 24 false // the_function_toplevel
7355. 24 );
7356. 24
7357. 24// PR-514 - Walk defaults as expressions, and leave only variables in
7358. 24// <name_list> for <post_a_assignment> to look up.
7359. 24
7360. 66 element.expression = the_token.name_list.filter(function (name) {
7361. 66 return name.arity !== "variable";
7362. 66 });
7363. 66 the_token.name_list = the_token.name_list.filter(function (name) {
7364. 66 return name.arity === "variable";
7365. 66 });
7366. 24 advance("=");
7367. 24 symbol("=").led_infix(element);
7368. 24 return the_token;
7369. 912 }
7370. 912 if (token_nxt.id !== "]") {
7371. 537
7372. 537// Parse/loop through each element in [...].
7373. 537
7374. 1755 while (true) {
7375. 1755 if (!state.mode_json && token_nxt.id === "...") {
7376. 1755
7377. 1755// test_cause:
7378. 1755// ["aa=[...aa]", "prefix_lbracket", "(live)", "", 0]
7379. 1755
7380. 1755 the_token.expression.push(prefix_ellipsis());
7381. 1755
7382. 1755// Issue #401 - Regression - Allow multiple-ellipsis in array-literal.
7383. 1755
7384. 1755 } else {
7385. 1755 the_token.expression.push(parse_expression(10));
7386. 1755 }
7387. 1755 if (token_nxt.id !== ",") {
7388. 1755 break;
7389. 1755 }
7390. 1755 advance(",");
7391. 1755 if (token_nxt.id === "]") {
7392. 1755
7393. 1755// test_cause:
7394. 1755// ["aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 6]
7395. 1755
7396. 1755 warn("unexpected_a", token_now);
7397. 1755 break;
7398. 1755 }
7399. 1755 }
7400. 912 }
7401. 912 advance("]");
7402. 912 return the_token;
7403. 912 }
7404. 994
7405. 1952 function prefix_lparen() {
7406. 1952 let the_paren = token_now;
7407. 1952 let the_value;
7408. 1952
7409. 1952// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
7410. 1952
7411. 20 if (token_now.fart) {
7412. 20 return prefix_function(token_now.fart, true, false);
7413. 1932 }
7414. 1932
7415. 1932// test_cause:
7416. 1932// ["(0)", "prefix_lparen", "(live)", "", 0]
7417. 1932
7418. 1932 the_value = parse_expression(0);
7419. 1932 if (the_value.wrapped === true) {
7420. 2
7421. 2// test_cause:
7422. 2// [" ((0))", "prefix_lparen", "unexpected_a", "(", 2]
7423. 2// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
7424. 2
7425. 2 warn("unexpected_a", the_paren);
7426. 1932 }
7427. 1932 the_value.wrapped = true;
7428. 1932 advance(")", the_paren);
7429. 1932 return the_value;
7430. 1932 }
7431. 994
7432. 170 function prefix_new() {
7433. 170 const the_new = token_now;
7434. 170 let right;
7435. 170 right = parse_expression(160);
7436. 170
7437. 170// PR-514 - Bugfix - In 'new aa`bb`()' the tagged-megastring belongs to the
7438. 170// callee, 'new (aa`bb`)()', but '`' shares lbp 160 with the call's '(', so rbp
7439. 170// 160 stops before it. Parse it, and any member after it, into the callee.
7440. 170
7441. 3 while (token_nxt.id === "`") {
7442. 3 advance("`");
7443. 3 right = infix_grave(right);
7444. 3 while ([".", "?.", "["].includes(token_nxt.id)) {
7445. 3 advance();
7446. 3 right = syntax_dict[token_now.id].led_infix(right);
7447. 3 }
7448. 3 }
7449. 2 if (optional_chain(right)) {
7450. 2
7451. 2// PR-514 - Bugfix - Rbp 160 lets an optional-chain into the callee, since '?.'
7452. 2// binds at 170.
7453. 2
7454. 2// test_cause:
7455. 2// ["new aa?.aa()", "prefix_new", "unexpected_a", "?.", 7]
7456. 2
7457. 2 warn("unexpected_a", optional_chain(right), "?.");
7458. 2 }
7459. 2 if (token_nxt.id !== "(") {
7460. 2
7461. 2// test_cause:
7462. 2// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 6]
7463. 2
7464. 2 warn("expected_a_before_b", token_nxt, "()", artifact());
7465. 2 }
7466. 170 the_new.expression = right;
7467. 170 return the_new;
7468. 170 }
7469. 994
7470. 919 function prefix_tick(mode_tagged) {
7471. 919 const the_tick = token_now;
7472. 906 if (!mode_tagged) {
7473. 906 warning_list.push(...the_tick.warning_list_untagged);
7474. 906 }
7475. 919 the_tick.value = [];
7476. 919 the_tick.expression = [];
7477. 919 if (token_nxt.id !== "`") {
7478. 919
7479. 919// Parse/loop through each token in `${...}`.
7480. 919
7481. 1784 while (true) {
7482. 1784 advance("(string)");
7483. 1784 the_tick.value.push(token_now);
7484. 1784 if (token_nxt.id !== "${") {
7485. 1784 break;
7486. 1784 }
7487. 1784 advance("${");
7488. 1784
7489. 1784// test_cause:
7490. 1784// ["let aa=`${}`", "prefix_tick", "(live)", "", 0]
7491. 1784
7492. 1784 the_tick.expression.push(parse_expression(0));
7493. 1784 advance("}");
7494. 1784 }
7495. 917 }
7496. 917 advance("`");
7497. 917 return the_tick;
7498. 917 }
7499. 994
7500. 4 function prefix_void() {
7501. 4 const the_void = token_now;
7502. 4
7503. 4// test_cause:
7504. 4// [" void 0", "prefix_void", "unexpected_a", "void", 2]
7505. 4// ["void 0", "prefix_void", "unexpected_a", "void", 1]
7506. 4// ["void", "prefix_void", "unexpected_a", "void", 1]
7507. 4
7508. 4 warn("unexpected_a", the_void);
7509. 4
7510. 4// PR-514 - Bugfix - Parse the operand at rbp 150, like every unary operator,
7511. 4// since the spec reads 'void UnaryExpression'. At rbp 0, 'void aa ** 2', a
7512. 4// SyntaxError, parsed as 'void (aa ** 2)'.
7513. 4
7514. 4 the_void.expression = parse_expression(150);
7515. 4 return the_void;
7516. 4 }
7517. 994
7518. 17805 function semicolon() {
7519. 17805
7520. 17805// Try to match a semicolon.
7521. 17805
7522. 17408 if (token_nxt.id === ";") {
7523. 17408 advance(";");
7524. 17408 } else {
7525. 397
7526. 397// test_cause:
7527. 397// [" 0//c", "semicolon", "expected_a_after_b", "0", 2]
7528. 397// ["0", "semicolon", "expected_a_after_b", "0", 1]
7529. 397
7530. 397 warn_at(
7531. 397 "expected_a_after_b",
7532. 397 token_now.line,
7533. 397
7534. 397// <thru> is EXCLUSIVE - one past the token's last char - so it marked the
7535. 397// character AFTER <b>. At end of line the clamp pulled it back onto <b> and
7536. 397// hid that; off end of line it did not. <expected_a_after_b> exists so the
7537. 397// message names the character the marker covers, so mark <b> itself.
7538. 397
7539. 397 token_now.thru - 1,
7540. 397 ";",
7541. 397 artifact(token_now)
7542. 397 );
7543. 397 }
7544. 17805 anon = "anonymous";
7545. 17805 }
7546. 994
7547. 22862 function stmt(id, fud_stmt) {
7548. 22862
7549. 22862// Create a statement.
7550. 22862
7551. 22862 const the_symbol = symbol(id);
7552. 22862 the_symbol.fud_stmt = fud_stmt;
7553. 22862 return the_symbol;
7554. 22862 }
7555. 994
7556. 1199 function stmt_break() {
7557. 1199 const the_break = token_now;
7558. 1199 let the_label;
7559. 1199 if (
7560. 1192 (scope_function.loop < 1 && scope_function.switch < 1) ||
7561. 1192 scope_function.finally > 0
7562. 7 ) {
7563. 7
7564. 7// test_cause:
7565. 7// [" break", "stmt_break", "unexpected_a", "break", 2]
7566. 7// ["break", "stmt_break", "unexpected_a", "break", 1]
7567. 7
7568. 7 warn("unexpected_a", the_break);
7569. 7 }
7570. 1199 the_break.disrupt = true;
7571. 11 if (token_nxt.identifier && token_now.line === token_nxt.line) {
7572. 28 block_stack.some(function (scope_block) {
7573. 28
7574. 28// PR-514 - Bugfix - Stop at the function boundary, since 'break aa' cannot
7575. 28// reach a label in an enclosing function, which is a SyntaxError.
7576. 28
7577. 11 if (scope_block === scope_function) {
7578. 11 return true;
7579. 25 }
7580. 25 the_label = scope_block.context[token_nxt.id];
7581. 25 if (the_label?.role !== "label") {
7582. 17 the_label = undefined;
7583. 25 }
7584. 25 if (the_label) {
7585. 11 the_label.used = true;
7586. 11 return true;
7587. 11 }
7588. 28 });
7589. 11 if (!the_label) {
7590. 11
7591. 11// test_cause:
7592. 11// ["
7593. 11// aa:while(0){(function(){while(0){break aa}}())}
7594. 11// ", "stmt_break", "not_label_a", "aa", 40]
7595. 11// ["aa:while(0){}break aa", "stmt_break", "not_label_a", "aa", 20]
7596. 11// ["break aa", "stmt_break", "not_label_a", "aa", 7]
7597. 11
7598. 11 warn("not_label_a", token_nxt);
7599. 11 }
7600. 11 the_break.label_break = token_nxt;
7601. 11 advance();
7602. 11 }
7603. 1199 semicolon();
7604. 1199 return the_break;
7605. 1199 }
7606. 994
7607. 3 function stmt_continue() {
7608. 3 const the_continue = token_now;
7609. 2 if (scope_function.loop < 1 || scope_function.finally > 0) {
7610. 2
7611. 2// test_cause:
7612. 2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
7613. 2// ["
7614. 2// while(0){try{}finally{continue}}
7615. 2// ", "stmt_continue", "unexpected_a", "continue", 23]
7616. 2
7617. 2 warn("unexpected_a", the_continue);
7618. 2 }
7619. 3 check_not_top_level(the_continue);
7620. 3 the_continue.disrupt = true;
7621. 3
7622. 3// test_cause:
7623. 3// ["
7624. 3// function aa(){while(aa){continue;}}
7625. 3// ", "stmt_continue", "unexpected_a", "continue", 25]
7626. 3
7627. 3 warn("unexpected_a", the_continue);
7628. 3 semicolon();
7629. 3 return the_continue;
7630. 3 }
7631. 994
7632. 4 function stmt_debugger() {
7633. 4 const the_debug = token_now;
7634. 2 if (!option_dict.devel) {
7635. 2
7636. 2// test_cause:
7637. 2// [" debugger", "stmt_debugger", "unexpected_a", "debugger", 2]
7638. 2// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
7639. 2
7640. 2 warn("unexpected_a", the_debug);
7641. 2 }
7642. 4 semicolon();
7643. 4 return the_debug;
7644. 4 }
7645. 994
7646. 64 function stmt_delete() {
7647. 64 const the_token = token_now;
7648. 64 const the_value = parse_expression(0);
7649. 64
7650. 64// PR-514 - Bugfix - In 'delete aa[bb] || cc', the parse at rbp 0 swallows the
7651. 64// '||'. Warn on the operator, not on a missing '.'. Rbp 150 would stop the
7652. 64// lint on the leftover '|| cc'.
7653. 64
7654. 64 if (
7655. 3 ["&&", "??", "||"].includes(the_value.id) &&
7656. 3 [".", "?.", "["].includes(the_value.expression[0].id) &&
7657. 3 the_value.expression[0].arity === "binary"
7658. 3 ) {
7659. 3
7660. 3// test_cause:
7661. 3// ["delete aa[aa]&&aa", "stmt_delete", "unexpected_a", "&&", 14]
7662. 3// ["delete aa[aa]??aa", "stmt_delete", "unexpected_a", "??", 14]
7663. 3// ["delete aa[aa]||aa", "stmt_delete", "unexpected_a", "||", 14]
7664. 3
7665. 3 warn("unexpected_a", the_value);
7666. 61 } else if (
7667. 61 ![".", "?.", "["].includes(the_value.id) ||
7668. 61 the_value.arity !== "binary"
7669. 61 ) {
7670. 61
7671. 61// test_cause:
7672. 61// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
7673. 61// ["delete 0;", "stmt_delete", "expected_a_b", "0", 8]
7674. 61
7675. 61 warn("expected_a_b", the_value, ".", artifact(the_value));
7676. 61 }
7677. 64 the_token.expression = the_value;
7678. 64 semicolon();
7679. 64 return the_token;
7680. 64 }
7681. 994
7682. 9 function stmt_do() {
7683. 9 const the_do = token_now;
7684. 9 check_not_top_level(the_do);
7685. 9 scope_function.loop += 1;
7686. 9 the_do.block = block();
7687. 9 advance("while");
7688. 9 the_do.expression = condition();
7689. 9 semicolon();
7690. 2 if (the_do.block.disrupt === true) {
7691. 2
7692. 2// test_cause:
7693. 2// ["function aa(){ do{break}while(0)}", "stmt_do", "weird_loop", "do", 16]
7694. 2// ["function aa(){do{break}while(0)}", "stmt_do", "weird_loop", "do", 15]
7695. 2
7696. 2 warn("weird_loop", the_do);
7697. 4 }
7698. 4 scope_function.loop -= 1;
7699. 4 return the_do;
7700. 4 }
7701. 994
7702. 48 function stmt_export() {
7703. 48 let export_list = [];
7704. 48 let the_export = token_now;
7705. 48 let the_id;
7706. 48 let the_name;
7707. 48 let the_thing;
7708. 48
7709. 48 the_export.expression = [];
7710. 31 if (token_nxt.id === "default") {
7711. 31 if (export_dict.default !== undefined) {
7712. 31
7713. 31// test_cause:
7714. 31// ["
7715. 31// export default 0;export default 0
7716. 31// ", "stmt_export", "duplicate_a", "default", 25]
7717. 31
7718. 31 warn("duplicate_a");
7719. 31 }
7720. 31 advance("default");
7721. 31 the_thing = parse_expression(0);
7722. 31 if (
7723. 31 the_thing.id !== "(" ||
7724. 31 the_thing.expression[0].id !== "." ||
7725. 31 the_thing.expression[0].expression.id !== "Object" ||
7726. 31 the_thing.expression[0].name.id !== "freeze"
7727. 31 ) {
7728. 31
7729. 31// test_cause:
7730. 31// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
7731. 31
7732. 31 warn("freeze_exports", the_thing);
7733. 31
7734. 31// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
7735. 31
7736. 31 } else {
7737. 31
7738. 31// test_cause:
7739. 31// ["
7740. 31// export default Object.freeze({})
7741. 31// ", "semicolon", "expected_a_after_b", ")", 32]
7742. 31
7743. 31 semicolon();
7744. 31 }
7745. 31 export_dict.default = the_thing;
7746. 31 the_export.expression.push(the_thing);
7747. 31 } else {
7748. 17
7749. 17// PR-439 - Add grammar for "export async function ...".
7750. 17
7751. 17 if (token_nxt.id === "function" || token_nxt.id === "async") {
7752. 17
7753. 17// test_cause:
7754. 17// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8]
7755. 17// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
7756. 17
7757. 17 warn("freeze_exports");
7758. 17 the_thing = parse_statement_single();
7759. 17 the_name = the_thing.name;
7760. 17 the_id = the_name.id;
7761. 17 the_name.used = true;
7762. 17
7763. 17// PR-517 - deadcode-confirmed - This duplicate is never shown. An earlier
7764. 17// export of <the_id> needs it declared at top level, so declaring it again
7765. 17// above warns redefinition_a_b on the same token first, and <warn> drops a
7766. 17// second warning on one token.
7767. 17//
7768. 17// deadcode-revive - Check for the duplicate before <parse_statement_single>
7769. 17// declares the name, or let a token carry more than one warning.
7770. 17//
7771. 17// if (export_dict[the_id] !== undefined) {
7772. 17// warn("duplicate_a", the_name);
7773. 17// }
7774. 17
7775. 17 export_dict[the_id] = the_thing;
7776. 17 the_export.expression.push(the_thing);
7777. 17 the_thing.statement = false;
7778. 17 the_thing.arity = "unary";
7779. 17 } else if (
7780. 17 token_nxt.id === "var" ||
7781. 17 token_nxt.id === "let" ||
7782. 17 token_nxt.id === "const"
7783. 17 ) {
7784. 17
7785. 17// test_cause:
7786. 17// ["export const", "stmt_export", "unexpected_a", "const", 8]
7787. 17// ["export let", "stmt_export", "unexpected_a", "let", 8]
7788. 17// ["export var", "stmt_export", "unexpected_a", "var", 8]
7789. 17
7790. 17 warn("unexpected_a");
7791. 17 parse_statement_single();
7792. 17 } else if (token_nxt.id === "{") {
7793. 17
7794. 17// test_cause:
7795. 17// ["export {}", "stmt_export", "(live)", "", 0]
7796. 17
7797. 17 advance("{");
7798. 17 while (true) {
7799. 17 if (!token_nxt.identifier) {
7800. 17
7801. 17// test_cause:
7802. 17// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
7803. 17// ["export {};", "stmt_export", "expected_identifier_a", "}", 9]
7804. 17
7805. 17 return stop("expected_identifier_a", token_nxt);
7806. 17 }
7807. 17 the_id = token_nxt.id;
7808. 17 export_list.push(token_nxt);
7809. 17 the_name = token_global.context[the_id];
7810. 17 if (the_name === undefined) {
7811. 17
7812. 17// test_cause:
7813. 17// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
7814. 17
7815. 17 warn("unexpected_a");
7816. 17 } else {
7817. 17 the_name.used = true;
7818. 17 if (export_dict[the_id] !== undefined) {
7819. 17
7820. 17// test_cause:
7821. 17// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
7822. 17
7823. 17 warn("duplicate_a");
7824. 17 }
7825. 17 export_dict[the_id] = the_name;
7826. 17 }
7827. 17 advance();
7828. 17 the_export.expression.push(the_thing);
7829. 17 if (token_nxt.id !== ",") {
7830. 17 break;
7831. 17 }
7832. 17 advance(",");
7833. 17 }
7834. 17
7835. 17// PR-439 - Check exported properties are ordered.
7836. 17
7837. 17// test_cause:
7838. 17// ["
7839. 17// let aa;let bb;export {bb, aa};
7840. 17// ", "check_ordered", "expected_a_b_before_c_d", "aa", 27]
7841. 17
7842. 17 check_ordered("export", export_list);
7843. 17 advance("}");
7844. 17 semicolon();
7845. 17 } else {
7846. 17
7847. 17// test_cause:
7848. 17// ["export", "stmt_export", "unexpected_a", "(end)", 6]
7849. 17
7850. 17 return stop("unexpected_a");
7851. 17 }
7852. 41 }
7853. 41 state.mode_module = true;
7854. 41 return the_export;
7855. 41 }
7856. 994
7857. 97 function stmt_for() {
7858. 97 const for_await = token_nxt.id === "await";
7859. 97 const the_for = token_now;
7860. 97 let the_operator;
7861. 97 let the_variable;
7862. 97 check_not_top_level(the_for);
7863. 97 scope_function.loop += 1;
7864. 97
7865. 97// PR-504 - Add hidden scope_block for:
7866. 97// - for-variable
7867. 97
7868. 97 scope_block = scope_block_push(the_for, true);
7869. 97
7870. 97// PR-508 - Add ES2018-feature Asynchronous Iteration - for await...of.
7871. 97
7872. 6 if (for_await) {
7873. 6 if (the_for.for_semicolon) {
7874. 6
7875. 6// test_cause:
7876. 6// ["
7877. 6// function aa(){for await(;;){}}
7878. 6// ", "stmt_for", "expected_a", "for await...of", 15]
7879. 6
7880. 6 warn("expected_a", the_for, "for await...of");
7881. 6 }
7882. 6 if (scope_function.async === 0 && scope_function !== token_global) {
7883. 6
7884. 6// test_cause:
7885. 6// ["()=>{for await(aa of aa){}}", "stmt_for", "unexpected_a", "await", 10]
7886. 6
7887. 6 warn("unexpected_a", token_nxt);
7888. 6 }
7889. 6 if (scope_function.async === 1) {
7890. 6
7891. 6// test_cause:
7892. 6// ["async function aa(bb){for await(aa of bb){}}", "stmt_for", "(live)", "", 0]
7893. 6
7894. 6 scope_function.async = 2;
7895. 6 }
7896. 6 advance("await");
7897. 6 }
7898. 97 advance("(");
7899. 19 if (the_for.for_semicolon) {
7900. 19 switch (token_nxt.id) {
7901. 19 case ";":
7902. 19
7903. 19// test_cause:
7904. 19// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 5]
7905. 19
7906. 19 warn("expected_a_b", token_nxt, "while (", "for (;");
7907. 19 break;
7908. 19 case "const":
7909. 19 case "var":
7910. 19
7911. 19// test_cause:
7912. 19// ["for(const aa;;){}", "stmt_for", "expected_a_b", "const", 5]
7913. 19// ["for(var aa;;){}", "stmt_for", "expected_a_b", "var", 5]
7914. 19
7915. 19 warn("expected_a_b", token_nxt, "let", token_nxt.id);
7916. 19 break;
7917. 19 case "let":
7918. 19
7919. 19// test_cause:
7920. 19// ["for(let aa;;){}", "stmt_for", "(live)", "", 0]
7921. 19
7922. 19 break;
7923. 19 default:
7924. 19
7925. 19// test_cause:
7926. 19// ["for(aa;;){}", "stmt_for", "expected_a", "let", 5]
7927. 19
7928. 19 warn("expected_a", token_nxt, "let");
7929. 19 }
7930. 19 token_nxt.for_init = true;
7931. 19 the_for.for_semicolon[0] = parse_statement_single();
7932. 19
7933. 19// PR-409 - jquery - Tolerate comma-expression in for_init `for(ii=0,jj=0;;)`.
7934. 19
7935. 19 while (token_nxt.id === ",") {
7936. 19
7937. 19// test_cause:
7938. 19// ["for(aa=0,bb=0;;){}", "stmt_for", "(live)", "", 0]
7939. 19
7940. 19 advance(",");
7941. 19 parse_expression(0);
7942. 19 semicolon();
7943. 19 }
7944. 19 token_nxt.for_init = true;
7945. 19 if (token_nxt.id === ";") {
7946. 19
7947. 19// test_cause:
7948. 19// ["for(;;){}", "stmt_for", "unexpected_a", ";", 6]
7949. 19
7950. 19 warn("unexpected_a", token_nxt);
7951. 19 } else {
7952. 19 the_for.for_semicolon[1] = parse_expression(0);
7953. 19 }
7954. 19 advance(";");
7955. 19 token_nxt.for_init = true;
7956. 19 if (token_nxt.id === ")") {
7957. 19
7958. 19// test_cause:
7959. 19// ["for(;;){}", "stmt_for", "unexpected_a", ")", 7]
7960. 19
7961. 19 warn("unexpected_a", token_nxt);
7962. 19 } else {
7963. 19 the_for.for_semicolon[2] = parse_expression(0);
7964. 19 }
7965. 77 } else {
7966. 77 switch (token_nxt.id) {
7967. 77 case "const":
7968. 77 case "let":
7969. 77
7970. 77// test_cause:
7971. 77// ["for(const aa in aa){}", "stmt_for", "(live)", "", 0]
7972. 77// ["for(const aa of aa){}", "stmt_for", "(live)", "", 0]
7973. 77// ["for(let aa in aa){}", "stmt_for", "(live)", "", 0]
7974. 77// ["for(let aa of aa){}", "stmt_for", "(live)", "", 0]
7975. 77
7976. 77 break;
7977. 77 case "var":
7978. 77
7979. 77// test_cause:
7980. 77// ["for(var aa in aa){}", "stmt_for", "expected_a_b", "var", 5]
7981. 77// ["for(var aa of aa){}", "stmt_for", "expected_a_b", "var", 5]
7982. 77
7983. 77 warn("expected_a_b", token_nxt, "const or let", "var");
7984. 77 break;
7985. 77 default:
7986. 77
7987. 77// test_cause:
7988. 77// ["for(aa in aa){}", "stmt_for", "expected_a", "const or let", 5]
7989. 77// ["for(aa of aa){}", "stmt_for", "expected_a", "const or let", 5]
7990. 77
7991. 77 warn("expected_a", token_nxt, "const or let");
7992. 77 }
7993. 77 token_nxt.for_init = true;
7994. 77 switch (token_nxt.id) {
7995. 77 case "const":
7996. 77 case "let":
7997. 77 case "var":
7998. 77 the_variable = parse_statement_single();
7999. 77 the_for.for_of = the_variable;
8000. 77 the_operator = the_variable.operator;
8001. 77 break;
8002. 77 default:
8003. 77
8004. 77// PR-514 - Bugfix - Parse the target at rbp 110, which stops before 'in' and
8005. 77// 'of', then their right side at rbp 0, as the spec does. Parsing the whole
8006. 77// head at rbp 0 let a looser operator like '||' in 'for (aa in bb || cc)'
8007. 77// wrap the 'in' node, and the lint stopped.
8008. 77
8009. 77 the_variable = parse_expression(110);
8010. 77 if (token_nxt.id !== "in" && token_nxt.id !== "of") {
8011. 77
8012. 77// test_cause:
8013. 77// ["for(aa 0){}", "stmt_for", "expected_a_b", "0", 8]
8014. 77// ["for(aa=aa){}", "stmt_for", "expected_a_b", "=", 7]
8015. 77
8016. 77 return stop(
8017. 77 "expected_a_b",
8018. 77 token_nxt,
8019. 77 "of",
8020. 77 artifact(token_nxt)
8021. 77 );
8022. 77 }
8023. 77 advance();
8024. 77 the_operator = token_now;
8025. 77 the_operator.arity = "binary";
8026. 77 the_operator.expression = [the_variable, parse_expression(0)];
8027. 77 the_variable = the_operator;
8028. 77 the_for.for_of = the_variable;
8029. 77 }
8030. 77 the_variable.for_init = true;
8031. 77 switch (the_operator.id) {
8032. 77 case "in":
8033. 77 if (for_await) {
8034. 77
8035. 77// test_cause:
8036. 77// ["for await(aa in aa){}", "stmt_for", "expected_a_b", "in", 14]
8037. 77
8038. 77 warn("expected_a_b", the_operator, "of", "in");
8039. 77 }
8040. 77
8041. 77// PR-514 - Suggest 'for...of Object.keys', since a plain object is not
8042. 77// iterable, so a bare 'for...of' would throw a TypeError.
8043. 77
8044. 77// test_cause:
8045. 77// ["function aa(){for(aa in aa){}}", "stmt_for", "expected_a_b", "for in", 15]
8046. 77// ["
8047. 77// function aa(){for(const aa in aa){}}
8048. 77// ", "stmt_for", "expected_a_b", "for in", 15]
8049. 77// ["
8050. 77// function aa(){for(let aa in aa){}}
8051. 77// ", "stmt_for", "expected_a_b", "for in", 15]
8052. 77// ["
8053. 77// function aa(){for(var aa in aa){}}
8054. 77// ", "stmt_for", "expected_a_b", "for in", 15]
8055. 77
8056. 77 warn("expected_a_b", the_for, "for...of Object.keys", "for in");
8057. 77 break;
8058. 77
8059. 77// PR-504 - Add ES2015-feature for..of.
8060. 77
8061. 77 case "of":
8062. 77
8063. 77// test_cause:
8064. 77// ["for(aa of aa){}", "stmt_for", "(live)", "", 0]
8065. 77// ["for(aa of aa||aa){}", "stmt_for", "(live)", "", 0]
8066. 77// ["for(const aa of aa){}", "stmt_for", "(live)", "", 0]
8067. 77// ["for(let aa of aa){}", "stmt_for", "(live)", "", 0]
8068. 77// ["for(var aa of aa){}", "stmt_for", "(live)", "", 0]
8069. 77
8070. 77 the_operator.for_init = true;
8071. 77 break;
8072. 77 default:
8073. 77
8074. 77// test_cause:
8075. 77// ["for(const aa=aa){}", "stmt_for", "expected_a_b", "=", 13]
8076. 77// ["for(let aa=aa){}", "stmt_for", "expected_a_b", "=", 11]
8077. 77// ["for(var aa=aa){}", "stmt_for", "expected_a_b", "=", 11]
8078. 77
8079. 77 return stop(
8080. 77 "expected_a_b",
8081. 77 the_operator,
8082. 77 "of",
8083. 77 the_operator.id
8084. 77 );
8085. 77 }
8086. 90 }
8087. 90 advance(")");
8088. 90 the_for.block = block();
8089. 90 if (the_for.block.disrupt === true) {
8090. 1
8091. 1// test_cause:
8092. 1// ["
8093. 1// /*jslint for*/
8094. 1// function aa(bb,cc){for(0;0;0){break;}}
8095. 1// ", "stmt_for", "weird_loop", "for", 20]
8096. 1
8097. 1 warn("weird_loop", the_for);
8098. 90 }
8099. 90 scope_block = scope_block_pop();
8100. 90 scope_function.loop -= 1;
8101. 90 return the_for;
8102. 90 }
8103. 994
8104. 3428 function stmt_if() {
8105. 3428 const the_if = token_now;
8106. 3428 let the_else;
8107. 3428 the_if.expression = condition();
8108. 3428 the_if.block = block();
8109. 447 if (token_nxt.id === "else") {
8110. 447 advance("else");
8111. 447 the_else = token_now;
8112. 447 the_if.else = (
8113. 447 token_nxt.id === "if"
8114. 447 ? parse_statement_single()
8115. 447 : block()
8116. 447 );
8117. 447
8118. 447// test_cause:
8119. 447// ["if(0){0}else if(0){0}", "stmt_if", "(live)", "", 0]
8120. 447// ["if(0){0}else{0}", "stmt_if", "(live)", "", 0]
8121. 447
8122. 447 if (the_if.block.disrupt === true) {
8123. 447 if (the_if.else.disrupt === true) {
8124. 447
8125. 447// test_cause:
8126. 447// ["if(0){break}else{break}", "stmt_if", "(live)", "", 0]
8127. 447
8128. 447 the_if.disrupt = true;
8129. 447 } else {
8130. 447
8131. 447// test_cause:
8132. 447// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
8133. 447
8134. 447 warn("unexpected_a", the_else);
8135. 447 }
8136. 447 }
8137. 3427 }
8138. 3427 return the_if;
8139. 3427 }
8140. 994
8141. 61 function stmt_import() {
8142. 61 const the_import = token_now;
8143. 61 let name;
8144. 61 the_import.name_list = []; // 4. name_list for "import ..."
8145. 61 state.mode_module = true;
8146. 63 while (true) {
8147. 63
8148. 63// PR-436 - Add grammar for side-effect import-statement.
8149. 63
8150. 63 if (token_nxt.id === "(string)") {
8151. 63
8152. 63// test_cause:
8153. 63// ["import \"aa\"", "stmt_import", "expected_a_b", "aa", 8]
8154. 63
8155. 63 warn("expected_a_b", token_nxt, "{", artifact());
8156. 63 advance();
8157. 63 semicolon();
8158. 63 return the_import;
8159. 63 }
8160. 63 if (token_nxt.id === "*") {
8161. 63 advance("*");
8162. 63 advance("as");
8163. 63 if (!token_nxt.identifier) {
8164. 63
8165. 63// test_cause:
8166. 63// ["import * as", "stmt_import", "expected_identifier_a", "(end)", 11]
8167. 63
8168. 63 return stop("expected_identifier_a", token_nxt);
8169. 63 }
8170. 63 }
8171. 63 if (token_nxt.identifier) {
8172. 63 name = token_nxt;
8173. 63 advance();
8174. 63 if (name.id === "ignore") {
8175. 63
8176. 63// test_cause:
8177. 63// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
8178. 63
8179. 63 warn("unexpected_a", name);
8180. 63 }
8181. 63 name_declare(
8182. 63
8183. 63// 1.imp.1 - Mark 'declared', the import-name, during import-statement.
8184. 63
8185. 63 scope_function, // scope_declared
8186. 63 "variable", // role
8187. 63
8188. 63// 1.imp.4 - Mark 'readonly', the import-name, during import-statement.
8189. 63
8190. 63 true, // readonly
8191. 63 the_import.name_list, // name_list
8192. 63 name, // name
8193. 63
8194. 63// 1.imp.3 - Mark 'assigned', the import-name, during import-statement.
8195. 63
8196. 63 true // assigned
8197. 63 );
8198. 63 } else {
8199. 63 advance("{");
8200. 63 if (token_nxt.id !== "}") {
8201. 63 while (true) {
8202. 63 if (!token_nxt.identifier) {
8203. 63
8204. 63// test_cause:
8205. 63// ["import {", "stmt_import", "expected_identifier_a", "(end)", 8]
8206. 63
8207. 63 return stop("expected_identifier_a", token_nxt);
8208. 63 }
8209. 63 name = token_nxt;
8210. 63 advance();
8211. 63 if (token_nxt.id === "as") {
8212. 63 advance("as");
8213. 63 if (!token_nxt.identifier) {
8214. 63
8215. 63// test_cause:
8216. 63// ["import {aa as 0}", "stmt_import", "expected_identifier_a", "0", 15]
8217. 63
8218. 63 return stop("expected_identifier_a", token_nxt);
8219. 63 }
8220. 63 name = token_nxt;
8221. 63 advance();
8222. 63 }
8223. 63 if (name.id === "ignore") {
8224. 63
8225. 63// test_cause:
8226. 63// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
8227. 63
8228. 63 warn("unexpected_a", name);
8229. 63 }
8230. 63 name_declare(
8231. 63
8232. 63// 1.imp.1 - Mark 'declared', the import-name, during import-statement.
8233. 63
8234. 63 scope_function, // scope_declared
8235. 63 "variable", // role
8236. 63
8237. 63// 1.imp.4 - Mark 'readonly', the import-name, during import-statement.
8238. 63
8239. 63 true, // readonly
8240. 63 the_import.name_list, // name_list
8241. 63 name, // name
8242. 63
8243. 63// 1.imp.3 - Mark 'assigned', the import-name, during import-statement.
8244. 63
8245. 63 true // assigned
8246. 63 );
8247. 63 if (token_nxt.id !== ",") {
8248. 63 break;
8249. 63 }
8250. 63
8251. 63// test_cause:
8252. 63// ["import {aa,bb} from \"aa\"", "stmt_import", "(live)", "", 0]
8253. 63
8254. 63 advance(",");
8255. 63 }
8256. 63 }
8257. 63 advance("}");
8258. 63 }
8259. 63
8260. 63// PR-347 - Disable warning "unexpected_directive_a".
8261. 63//
8262. 63// if (typeof state.mode_module === "object") {
8263. 63//
8264. 63// // test_cause:
8265. 63// // ["
8266. 63// // /*global aa*/
8267. 63// // import aa from "aa"
8268. 63// // ", "stmt_import", "unexpected_directive_a", "global", 1]
8269. 63//
8270. 63// warn(
8271. 63// "unexpected_directive_a",
8272. 63// state.mode_module,
8273. 63// state.mode_module.directive
8274. 63// );
8275. 63// }
8276. 63
8277. 63 if (token_nxt.id !== ",") {
8278. 63 break;
8279. 63 }
8280. 63
8281. 63// test_cause:
8282. 63// ["import aa,{bb} from \"aa\"", "stmt_import", "(live)", "", 0]
8283. 63
8284. 63 advance(",");
8285. 63 }
8286. 56 advance("from");
8287. 56 advance("(string)");
8288. 56 the_import.import = token_now;
8289. 56 if (!jslint_rgx_module.test(token_now.value)) {
8290. 1
8291. 1// test_cause:
8292. 1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
8293. 1
8294. 1 warn("bad_module_name_a", token_now);
8295. 56 }
8296. 56 import_list.push(token_now.value);
8297. 56
8298. 56// Issue #495 - Add ES2025-syntax "import ... with {...}".
8299. 56
8300. 56 if (token_nxt.id === "with") {
8301. 2
8302. 2// test_cause:
8303. 2// ["import aa from \"aa\" with {}", "stmt_import", "(live)", "", 0]
8304. 2
8305. 2 advance("with");
8306. 2 advance("{");
8307. 2 prefix_lbrace();
8308. 56 }
8309. 56 semicolon();
8310. 56 return the_import;
8311. 56 }
8312. 994
8313. 30 function stmt_label() {
8314. 30 const the_label = token_now;
8315. 30 let the_statement;
8316. 2 if (the_label.id === "ignore") {
8317. 2
8318. 2// test_cause:
8319. 2// [" ignore:", "stmt_label", "unexpected_a", "ignore", 2]
8320. 2// ["ignore:", "stmt_label", "unexpected_a", "ignore", 1]
8321. 2
8322. 2 warn("unexpected_a", the_label);
8323. 2 }
8324. 30 advance(":");
8325. 30 switch (token_nxt.id) {
8326. 2 case "do":
8327. 5 case "for":
8328. 7 case "switch":
8329. 22 case "while":
8330. 22
8331. 22// test_cause:
8332. 22// ["aa:do", "stmt_label", "(live)", "", 0]
8333. 22// ["aa:for", "stmt_label", "(live)", "", 0]
8334. 22// ["aa:switch", "stmt_label", "(live)", "", 0]
8335. 22// ["aa:while", "stmt_label", "(live)", "", 0]
8336. 22
8337. 22 break;
8338. 8 default:
8339. 8
8340. 8// test_cause:
8341. 8// ["()=>{aa:0}", "stmt_label", "unexpected_label_a", "aa", 6]
8342. 8// [";{aa:0}", "stmt_label", "unexpected_label_a", "aa", 3]
8343. 8// ["aa:", "stmt_label", "unexpected_label_a", "aa", 1]
8344. 8// ["aa:0", "stmt_label", "unexpected_label_a", "aa", 1]
8345. 8// ["aa:0?0:0", "stmt_label", "unexpected_label_a", "aa", 1]
8346. 8// ["aa:bb:0", "stmt_label", "unexpected_label_a", "aa", 1]
8347. 8
8348. 8 warn("unexpected_label_a", the_label);
8349. 8 advance();
8350. 8 return;
8351. 22 }
8352. 22
8353. 22// PR-504 - Add hidden scope_block for:
8354. 22// - label-name
8355. 22
8356. 22 scope_block = scope_block_push(the_label, true);
8357. 22 name_declare(
8358. 22
8359. 22// 5.lab.1 - Mark 'declared', the label-name, before control-flow-block.
8360. 22
8361. 22 scope_block, // scope_declared
8362. 22 "label", // role
8363. 22
8364. 22// 5.lab.4 - Mark 'readonly', the label-name, before control-flow-block.
8365. 22
8366. 22 true, // readonly
8367. 22 [], // name_list
8368. 22 the_label, // name
8369. 22
8370. 22// 5.lab.3 - Mark 'assigned', the label-name, before control-flow-block.
8371. 22
8372. 22 true // assigned
8373. 22 );
8374. 22
8375. 22// 5.lab.2 - Mark 'alive', the label-name, before control-flow-block.
8376. 22
8377. 22 the_label.alive = true;
8378. 22 the_statement = parse_statement_single();
8379. 22 the_statement.label_name = the_label;
8380. 22 check_ordered_var(the_statement);
8381. 22 scope_block = scope_block_pop();
8382. 22 return the_statement;
8383. 22 }
8384. 994
8385. 4 function stmt_lbrace() {
8386. 4
8387. 4// test_cause:
8388. 4// [";{}", "stmt_lbrace", "naked_block", "{", 2]
8389. 4// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
8390. 4
8391. 4 warn("naked_block", token_now);
8392. 4 return block("naked");
8393. 4 }
8394. 994
8395. 2449 function stmt_return() {
8396. 2449 const the_return = token_now;
8397. 2449 check_not_top_level(the_return);
8398. 1 if (scope_function.finally > 0) {
8399. 1
8400. 1// test_cause:
8401. 1// ["
8402. 1// function aa(){try{}finally{return;}}
8403. 1// ", "stmt_return", "unexpected_a", "return", 28]
8404. 1
8405. 1 warn("unexpected_a", the_return);
8406. 1 }
8407. 2449 the_return.disrupt = true;
8408. 1961 if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
8409. 1961 the_return.expression = parse_expression(10);
8410. 1961 }
8411. 2449 semicolon();
8412. 2449 return the_return;
8413. 2449 }
8414. 994
8415. 32 function stmt_semicolon() {
8416. 32
8417. 32// test_cause:
8418. 32// [" ;0", "stmt_semicolon", "unexpected_a", ";", 2]
8419. 32// [";", "stmt_semicolon", "unexpected_a", ";", 1]
8420. 32
8421. 32 warn("unexpected_a", token_now);
8422. 32 return token_now;
8423. 32 }
8424. 994
8425. 309 function stmt_switch() {
8426. 309 const the_cases = [];
8427. 309 const the_switch = token_now;
8428. 309 let dups = [];
8429. 309 let exp;
8430. 309 let last;
8431. 309 let parsed_block;
8432. 309 let the_case;
8433. 309 let the_default;
8434. 309 let the_disrupt = true;
8435. 309 let the_last;
8436. 23808 function is_dup(thing) {
8437. 23808 return is_equal(thing, exp);
8438. 23808 }
8439. 309 check_not_top_level(the_switch);
8440. 1 if (scope_function.finally > 0) {
8441. 1
8442. 1// test_cause:
8443. 1// ["
8444. 1// function aa(){try{}finally{switch(0){}}}
8445. 1// ", "stmt_switch", "unexpected_a", "switch", 28]
8446. 1
8447. 1 warn("unexpected_a", the_switch);
8448. 1 }
8449. 309 scope_function.switch += 1;
8450. 309 advance("(");
8451. 309 the_switch.expression = parse_expression(0);
8452. 309 the_switch.block = the_cases;
8453. 309 advance(")");
8454. 309 advance("{");
8455. 1285 while (true) {
8456. 1285
8457. 1285// Loop through cases with breaks.
8458. 1285
8459. 1285 the_case = token_nxt;
8460. 1285 the_case.arity = "statement";
8461. 1285 the_case.expression = [];
8462. 1954 while (true) {
8463. 1954
8464. 1954// Loop through fallthrough cases.
8465. 1954
8466. 1954 advance("case");
8467. 1954 token_now.switch = true;
8468. 1954 exp = parse_expression(0);
8469. 1954 if (dups.some(is_dup)) {
8470. 1954
8471. 1954// test_cause:
8472. 1954// ["
8473. 1954// switch(0){case 0:break;case 0:break}
8474. 1954// ", "stmt_switch", "unexpected_a", "0", 29]
8475. 1954
8476. 1954 warn("unexpected_a", exp);
8477. 1954 }
8478. 1954 dups.push(exp);
8479. 1954 the_case.expression.push(exp);
8480. 1954 advance(":");
8481. 1954 if (token_nxt.id !== "case") {
8482. 1954 break;
8483. 1954 }
8484. 1954 }
8485. 1285
8486. 1285// test_cause:
8487. 1285// ["
8488. 1285// switch(0){case 1:case 0:break;}
8489. 1285// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
8490. 1285// ["
8491. 1285// switch(0){case "aa":case 0:break;}
8492. 1285// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
8493. 1285// ["
8494. 1285// switch(0){case "bb":case "aa":break;}
8495. 1285// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
8496. 1285// ["
8497. 1285// switch(0){case aa:case "aa":break;}
8498. 1285// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
8499. 1285// ["
8500. 1285// switch(0){case bb:case aa:break;}
8501. 1285// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
8502. 1285
8503. 1285 check_ordered_case(the_case.expression);
8504. 1285 parsed_block = parse_statement_block();
8505. 1285 if (parsed_block.length < 1) {
8506. 1285
8507. 1285// test_cause:
8508. 1285// ["
8509. 1285// switch(0){case 0:default:}
8510. 1285// ", "stmt_switch", "expected_statements_a", "default", 18]
8511. 1285// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
8512. 1285
8513. 1285 warn("expected_statements_a");
8514. 1285
8515. 1285// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
8516. 1285
8517. 1285 break;
8518. 1285 }
8519. 1285 the_case.block = parsed_block;
8520. 1285 the_cases.push(the_case);
8521. 1285 last = parsed_block[parsed_block.length - 1];
8522. 1285 if (last.disrupt) {
8523. 1285 if (last.id === "break" && last.label_break === undefined) {
8524. 1285 the_disrupt = false;
8525. 1285 }
8526. 1285 } else {
8527. 1285
8528. 1285// test_cause:
8529. 1285// ["switch(0){case 0:;}", "stmt_switch", "expected_a_before_b", "}", 19]
8530. 1285
8531. 1285 warn("expected_a_before_b", token_nxt, "break;", artifact());
8532. 1285 }
8533. 1285 if (token_nxt.id !== "case") {
8534. 1285 break;
8535. 1285 }
8536. 1285 }
8537. 307
8538. 307// test_cause:
8539. 307// ["
8540. 307// switch(0){case 1:break;case 0:break;}
8541. 307// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
8542. 307// ["
8543. 307// switch(0){case "aa":break;case 0:break;}
8544. 307// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
8545. 307// ["
8546. 307// switch(0){case "bb":break;case "aa":break;}
8547. 307// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
8548. 307// ["
8549. 307// switch(0){case aa:break;case "aa":break;}
8550. 307// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
8551. 307// ["
8552. 307// switch(0){case bb:break;case aa:break;}
8553. 307// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
8554. 307
8555. 1282 check_ordered_case(the_cases.map(function ({
8556. 1282 expression
8557. 1282 }) {
8558. 1282 return expression[0];
8559. 1282 }));
8560. 307 dups = undefined;
8561. 307 if (token_nxt.id === "default") {
8562. 154 the_default = token_nxt;
8563. 154 advance("default");
8564. 154 token_now.switch = true;
8565. 154 advance(":");
8566. 154 the_switch.else = parse_statement_block();
8567. 154 if (the_switch.else.length < 1) {
8568. 154
8569. 154// test_cause:
8570. 154// ["
8571. 154// switch(0){case 0:break;default:}
8572. 154// ", "stmt_switch", "unexpected_a", "default", 24]
8573. 154
8574. 154 warn("unexpected_a", the_default);
8575. 154 the_disrupt = false;
8576. 154 } else {
8577. 154 the_last = the_switch.else[
8578. 154 the_switch.else.length - 1
8579. 154 ];
8580. 154 if (
8581. 154 the_last.id === "break" &&
8582. 154 the_last.label_break === undefined
8583. 154 ) {
8584. 154
8585. 154// test_cause:
8586. 154// ["
8587. 154// switch(0){case 0:break;default:break;}
8588. 154// ", "stmt_switch", "unexpected_a", "break", 32]
8589. 154
8590. 154 warn("unexpected_a", the_last);
8591. 154 the_last.disrupt = false;
8592. 154 }
8593. 154 the_disrupt = the_disrupt && the_last.disrupt;
8594. 154 }
8595. 154 } else {
8596. 153 the_disrupt = false;
8597. 307 }
8598. 307 advance("}", the_switch);
8599. 307 scope_function.switch -= 1;
8600. 307 the_switch.disrupt = the_disrupt;
8601. 307 return the_switch;
8602. 307 }
8603. 994
8604. 46 function stmt_throw() {
8605. 46 const the_throw = token_now;
8606. 46 the_throw.disrupt = true;
8607. 46 the_throw.expression = parse_expression(10);
8608. 46 semicolon();
8609. 1 if (scope_function.try > 0) {
8610. 1
8611. 1// test_cause:
8612. 1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
8613. 1
8614. 1 warn("unexpected_a", the_throw);
8615. 1 }
8616. 46 return the_throw;
8617. 46 }
8618. 994
8619. 78 function stmt_try() {
8620. 78 const the_try = token_now;
8621. 78 let ignored;
8622. 78 let the_catch;
8623. 78 let the_disrupt;
8624. 1 if (scope_function.try > 0) {
8625. 1
8626. 1// test_cause:
8627. 1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
8628. 1
8629. 1 warn("unexpected_a", the_try);
8630. 1 }
8631. 78 scope_function.try += 1;
8632. 1 if (token_nxt.id !== "{") {
8633. 1
8634. 1// test_cause:
8635. 1// ["try", "stmt_try", "expected_a_b", "(end)", 3]
8636. 1
8637. 1 return stop("expected_a_b", token_nxt, "{", artifact());
8638. 77 }
8639. 77 the_try.block = block();
8640. 77 the_disrupt = the_try.block.disrupt;
8641. 77 if (token_nxt.id === "catch") {
8642. 65 ignored = "ignore";
8643. 65 the_catch = token_nxt;
8644. 65 the_try.catch = the_catch;
8645. 65 advance("catch");
8646. 65
8647. 65// PR-504 - Add hidden scope_block for:
8648. 65// - catch-variable
8649. 65
8650. 65 scope_block = scope_block_push(the_catch, true);
8651. 65 if (token_nxt.id === "(") {
8652. 65 advance("(");
8653. 65 if (!token_nxt.identifier) {
8654. 65
8655. 65// test_cause:
8656. 65// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
8657. 65
8658. 65 return stop("expected_identifier_a", token_nxt);
8659. 65 }
8660. 65 if (token_nxt.id !== "ignore") {
8661. 65 ignored = undefined;
8662. 65 the_catch.name = token_nxt;
8663. 65 name_declare(
8664. 65
8665. 65// 3.cat.1 - Mark 'declared', the catch-variable, before catch-block.
8666. 65
8667. 65 scope_block, // scope_declared
8668. 65 "exception", // role
8669. 65
8670. 65// 3.cat.4 - Mark 'readonly', the catch-variable, before catch-block.
8671. 65
8672. 65 true, // readonly
8673. 65 [], // name_list
8674. 65 token_nxt, // name
8675. 65
8676. 65// 3.cat.3 - Mark 'assigned', the catch-variable, before catch-block.
8677. 65
8678. 65 true // assigned
8679. 65 );
8680. 65
8681. 65// 3.cat.2 - Mark 'alive', the catch-variable, before catch-block.
8682. 65
8683. 65 token_nxt.alive = true;
8684. 65 }
8685. 65 advance();
8686. 65 advance(")");
8687. 65 }
8688. 65
8689. 65 if (token_nxt.id !== "{") {
8690. 65
8691. 65// test_cause:
8692. 65// ["try{}catch(aa);", "stmt_try", "expected_a_b", ";", 15]
8693. 65// ["try{}catch(aa);;", "stmt_try", "expected_a_b", ";", 15]
8694. 65
8695. 65 return stop("expected_a_b", token_nxt, "{", artifact());
8696. 65 }
8697. 65 the_catch.block = block(ignored);
8698. 65 if (the_catch.block.disrupt !== true) {
8699. 65 the_disrupt = false;
8700. 65 }
8701. 65
8702. 65// Restore previous catch-scope after catch-block.
8703. 65
8704. 65 scope_block = scope_block_pop();
8705. 65
8706. 65// PR-404 - Relax warning about missing `catch` in `try...finally` statement.
8707. 65//
8708. 65// } else {
8709. 65//
8710. 65// // test_cause:
8711. 65// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
8712. 65//
8713. 65// warn("expected_a_before_b", token_nxt, "catch", artifact());
8714. 65
8715. 71 }
8716. 71 if (token_nxt.id === "finally") {
8717. 13 scope_function.finally += 1;
8718. 13 advance("finally");
8719. 13
8720. 13 if (token_nxt.id !== "{") {
8721. 13
8722. 13// test_cause:
8723. 13// ["try{}finally;", "stmt_try", "expected_a_b", ";", 13]
8724. 13
8725. 13 return stop("expected_a_b", token_nxt, "{", artifact());
8726. 13 }
8727. 13 the_try.finally = block();
8728. 13 the_disrupt = the_try.finally.disrupt;
8729. 13 scope_function.finally -= 1;
8730. 69 }
8731. 69 the_try.disrupt = the_disrupt;
8732. 69 scope_function.try -= 1;
8733. 69 return the_try;
8734. 69 }
8735. 994
8736. 2679 function stmt_var() {
8737. 2679 const for_init = token_now.for_init;
8738. 2679 const readonly = token_now.id === "const";
8739. 2679 const scope_declared = (
8740. 2679 token_now.id === "var"
8741. 27 ? scope_function
8742. 2679
8743. 2679// PR-504 - Change scope from scope_function to scope_block:
8744. 2679// - const-declaration
8745. 2679// - let-declaration
8746. 2679
8747. 2652 : scope_block
8748. 2679 );
8749. 2679 const the_variable = token_now;
8750. 2679 let name;
8751. 2679 let the_operator;
8752. 2670 function advance_operator() {
8753. 2670 switch (the_operator.id) {
8754. 1517 case "=":
8755. 1517 the_variable.operator = the_operator;
8756. 1517 advance("=");
8757. 1517 break;
8758. 13 case "in":
8759. 64 case "of":
8760. 64 if (for_init) {
8761. 64
8762. 64// test_cause:
8763. 64// ["for(let aa in 0){}", "advance_operator", "(live)", "", 0]
8764. 64// ["for(let aa of 0){}", "advance_operator", "(live)", "", 0]
8765. 64// ["for(let {aa} in 0){}", "advance_operator", "(live)", "", 0]
8766. 64// ["for(let {aa} of 0){}", "advance_operator", "(live)", "", 0]
8767. 64
8768. 64 the_variable.operator = the_operator;
8769. 64 advance();
8770. 64 break;
8771. 64 }
8772. 64
8773. 64// test_cause:
8774. 64// ["let aa in 0", "advance_operator", "(live)", "", 0]
8775. 64// ["let aa of 0", "advance_operator", "(live)", "", 0]
8776. 64// ["let {aa} in 0", "advance_operator", "(live)", "", 0]
8777. 64// ["let {aa} of 0", "advance_operator", "(live)", "", 0]
8778. 64
8779. 64 the_variable.operator = the_operator;
8780. 64 advance("=");
8781. 64 break;
8782. 1089 default:
8783. 1089 if (readonly) {
8784. 1089 the_variable.operator = the_operator;
8785. 1089 advance("=");
8786. 1089 }
8787. 2670 }
8788. 2670 }
8789. 2679 the_variable.name_list = []; // 5. name_list for "let [aa] = ..."
8790. 2679
8791. 2679// A program may use var or let, but not both.
8792. 2679
8793. 1800 if (!readonly) {
8794. 1800 if (mode_var === undefined) {
8795. 1800 mode_var = the_variable.id;
8796. 1800 } else if (the_variable.id !== mode_var) {
8797. 1800
8798. 1800// test_cause:
8799. 1800// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
8800. 1800
8801. 1800 warn("expected_a_b", the_variable, mode_var, the_variable.id);
8802. 1800 }
8803. 1800 }
8804. 2679
8805. 2679// We don't expect to see variables created in switch statements.
8806. 2679
8807. 2679// PR-514 - Kept 2026-09-29, broader than ESLint no-case-declarations. This
8808. 2679// also warns a 'var', a braced 'case 0: {let aa}' and a 'let' nested in a
8809. 2679// block of the case, all of which ESLint allows.
8810. 2679
8811. 1 if (scope_function.switch > 0) {
8812. 1
8813. 1// test_cause:
8814. 1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
8815. 1
8816. 1 warn("var_switch", the_variable);
8817. 1 }
8818. 2680 while (true) {
8819. 2680 if (token_nxt.id === "{" || token_nxt.id === "[") {
8820. 2680 if (the_variable.id === "var") {
8821. 2680
8822. 2680// test_cause:
8823. 2680// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
8824. 2680// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
8825. 2680
8826. 2680 warn("unexpected_a", the_variable);
8827. 2680 }
8828. 2680 advance();
8829. 2680
8830. 2680// PR-500 - Unify ES2015-destructure-logic. - let [aa] = ...;
8831. 2680
8832. 2680 prefix_destructure(
8833. 2680 scope_declared, // scope_declared
8834. 2680 "variable", // role
8835. 2680 readonly, // readonly
8836. 2680 the_variable.name_list, // name_list
8837. 2680 undefined, // the_function
8838. 2680 false // the_function_toplevel
8839. 2680 );
8840. 2680 the_operator = token_nxt;
8841. 2680 advance_operator();
8842. 2680 the_variable.expression = parse_expression(0);
8843. 2680 } else if (token_nxt.identifier) {
8844. 2680 name = token_nxt;
8845. 2680 advance();
8846. 2680 if (name.id === "ignore") {
8847. 2680
8848. 2680// test_cause:
8849. 2680// ["
8850. 2680// let ignore;function aa(ignore) {}
8851. 2680// ", "stmt_var", "unexpected_a", "ignore", 5]
8852. 2680
8853. 2680 warn("unexpected_a", name);
8854. 2680 }
8855. 2680 the_operator = token_nxt;
8856. 2680 advance_operator();
8857. 2680 switch (the_operator.id) {
8858. 2680 case "=":
8859. 2680 case "in":
8860. 2680 case "of":
8861. 2680 name.expression = parse_expression(0);
8862. 2680 break;
8863. 2680 }
8864. 2680 name_declare(
8865. 2680
8866. 2680// 3.var.1 - Mark 'declared', the variable, during variable-declaration.
8867. 2680
8868. 2680 scope_declared, // scope_declared
8869. 2680 "variable", // role
8870. 2680
8871. 2680// 3.var.4 - Mark 'readonly', the variable, if const.
8872. 2680
8873. 2680 readonly, // readonly
8874. 2680 the_variable.name_list, // name_list
8875. 2680 name, // name
8876. 2680
8877. 2680// 3.var.3 - Mark 'assigned', the variable, during variable-declaration.
8878. 2680
8879. 2680 Boolean(name.expression) // assigned
8880. 2680 );
8881. 2680 } else {
8882. 2680
8883. 2680// test_cause:
8884. 2680// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
8885. 2680// ["let 0;", "stmt_var", "expected_identifier_a", "0", 5]
8886. 2680
8887. 2680 return stop("expected_identifier_a", token_nxt);
8888. 2680 }
8889. 2680 if (token_nxt.id !== ",") {
8890. 2680 break;
8891. 2680 }
8892. 2680
8893. 2680// test_cause:
8894. 2680// ["let aa,bb", "stmt_var", "expected_a_b", ",", 7]
8895. 2680
8896. 2680 warn("expected_a_b", token_nxt, ";", ",");
8897. 2680 advance(",");
8898. 2680 }
8899. 2662 if (!for_init || token_nxt.id === ";") {
8900. 2599 semicolon();
8901. 2662 }
8902. 2662 return the_variable;
8903. 2662 }
8904. 994
8905. 240 function stmt_while() {
8906. 240 const the_while = token_now;
8907. 240 check_not_top_level(the_while);
8908. 240 scope_function.loop += 1;
8909. 240 the_while.expression = condition();
8910. 240 the_while.block = block();
8911. 6 if (the_while.block.disrupt === true) {
8912. 6
8913. 6// test_cause:
8914. 6// ["function aa(){ while(0){break}}", "stmt_while", "weird_loop", "while", 16]
8915. 6// ["function aa(){while(0){break}}", "stmt_while", "weird_loop", "while", 15]
8916. 6
8917. 6 warn("weird_loop", the_while);
8918. 238 }
8919. 238 scope_function.loop -= 1;
8920. 238 return the_while;
8921. 238 }
8922. 994
8923. 2 function stmt_with() {
8924. 2
8925. 2// test_cause:
8926. 2// [" with", "stmt_with", "unexpected_a", "with", 2]
8927. 2// ["with", "stmt_with", "unexpected_a", "with", 1]
8928. 2
8929. 2 return stop("unexpected_a", token_now);
8930. 2 }
8931. 994
8932. 16309 function survey(name) {
8933. 16309 let id = name.id;
8934. 16309
8935. 16309// Tally the property name. If it is a string, only tally strings that conform
8936. 16309// to the identifier rules.
8937. 16309
8938. 16309 switch (id) {
8939. 1 case "(number)":
8940. 1
8941. 1// PR-409 - jquery - Tolerate numeric object-key `{0: 200}` - warn, do not stop.
8942. 1// Like a non-identifier string-key, it is not tallied for /*property*/.
8943. 1
8944. 1// test_cause:
8945. 1// ["String({0:0})", "survey", "unexpected_a", "0", 9]
8946. 1
8947. 1 warn("unexpected_a", name);
8948. 1 return name.value;
8949. 111 case "(string)":
8950. 111 id = name.value;
8951. 111 if (!jslint_rgx_identifier.test(id)) {
8952. 111 return id;
8953. 111 }
8954. 111 break;
8955. 2 case "`":
8956. 2 if (name.value.length === 1) {
8957. 2 id = name.value[0].value;
8958. 2 if (!jslint_rgx_identifier.test(id)) {
8959. 2 return id;
8960. 2 }
8961. 2 }
8962. 2 break;
8963. 16195 default:
8964. 16195 if (!name.identifier) {
8965. 16195
8966. 16195// test_cause:
8967. 16195// ["String({+:0})", "survey", "expected_identifier_a", "+", 9]
8968. 16195
8969. 16195 return stop("expected_identifier_a", name);
8970. 16195 }
8971. 16221 }
8972. 16221
8973. 16221// If we have seen this name before, increment its count.
8974. 16221
8975. 16221 if (typeof property_dict[id] === "number") {
8976. 13488 property_dict[id] += 1;
8977. 13488 return id;
8978. 13488 }
8979. 2733
8980. 2733// If this is the first time seeing this property name, and if there is a
8981. 2733// tenure list, then it must be on the list. Otherwise, it must conform to
8982. 2733// the rules for good property names.
8983. 2733
8984. 2733 if (state.mode_property) {
8985. 1845 if (tenure[id] !== true) {
8986. 1845
8987. 1845// test_cause:
8988. 1845// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
8989. 1845
8990. 1845 warn("unregistered_property_a", name);
8991. 1845 }
8992. 1845 } else if (
8993. 888 !option_dict.nomen &&
8994. 888 name.identifier &&
8995. 888 jslint_rgx_weird_property.test(id)
8996. 888 ) {
8997. 888
8998. 888// test_cause:
8999. 888// ["aa.$", "survey", "weird_property_a", "$", 4]
9000. 888// ["aa._", "survey", "weird_property_a", "_", 4]
9001. 888// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
9002. 888// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
9003. 888// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
9004. 888
9005. 888 warn("weird_property_a", name);
9006. 2733 }
9007. 2733 property_dict[id] = 1;
9008. 2733 return id;
9009. 2733 }
9010. 994
9011. 994// These functions are used to specify the grammar of our language:
9012. 994
9013. 132226 function symbol(id, bp) {
9014. 132226
9015. 132226// Create a symbol if it does not already exist in the language's syntax.
9016. 132226
9017. 132226 let the_symbol = syntax_dict[id];
9018. 117292 if (the_symbol === undefined) {
9019. 117292 the_symbol = empty();
9020. 117292 the_symbol.id = id;
9021. 117292 the_symbol.lbp = bp || 0;
9022. 117292 syntax_dict[id] = the_symbol;
9023. 117292 }
9024. 132226 return the_symbol;
9025. 132226 }
9026. 994
9027. 994 function ternary(id1, id2) {
9028. 994
9029. 994// Create a ternary operator.
9030. 994
9031. 994 const the_symbol = symbol(id1, 30);
9032. 339 the_symbol.led_infix = function parse_ternary_led(left) {
9033. 339 const the_token = token_now;
9034. 339 let second;
9035. 339
9036. 339// PR-514 - Bugfix - Both branches are an AssignmentExpression in the spec, so
9037. 339// parse the second like the third. 'aa ? bb = 0 : cc' used to stop at '='.
9038. 339
9039. 339 second = parse_expression(10);
9040. 339 advance(id2);
9041. 339 token_now.arity = "ternary";
9042. 339 the_token.arity = "ternary";
9043. 339 the_token.expression = [left, second, parse_expression(10)];
9044. 3 if (token_nxt.id !== ")") {
9045. 3
9046. 3// test_cause:
9047. 3// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
9048. 3// ["aa=0?aa=0:0", "parse_ternary_led", "use_open", "?", 5]
9049. 3
9050. 3 warn("use_open", the_token);
9051. 3 }
9052. 339 return the_token;
9053. 339 };
9054. 994 return the_symbol;
9055. 994 }
9056. 994
9057. 994// Now we parse JavaScript.
9058. 994// Begin defining the language.
9059. 994
9060. 994 assignment("%=");
9061. 994 assignment("&&=");
9062. 994 assignment("&=");
9063. 994 assignment("**=");
9064. 994 assignment("*=");
9065. 994 assignment("+=");
9066. 994 assignment("-=");
9067. 994 assignment("/=");
9068. 994 assignment("<<=");
9069. 994 assignment("=");
9070. 994 assignment(">>=");
9071. 994 assignment(">>>=");
9072. 994 assignment("??=");
9073. 994 assignment("^=");
9074. 994 assignment("|=");
9075. 994 assignment("||=");
9076. 994 constant("(number)", "number");
9077. 994 constant("(regexp)", "regexp");
9078. 994 constant("(string)", "string");
9079. 994 constant("Function", "function", constant_Function);
9080. 994 constant("Infinity", "number", Infinity);
9081. 994 constant("NaN", "number", NaN);
9082. 994 constant("arguments", "object", constant_arguments);
9083. 994 constant("eval", "function", constant_eval);
9084. 994 constant("false", "boolean", false);
9085. 994 constant("ignore", "undefined", constant_ignore);
9086. 994 constant("isFinite", "function", constant_isInfinite);
9087. 994 constant("isNaN", "function", constant_isNaN);
9088. 994 constant("null", "null", null);
9089. 994 constant("this", "object", constant_this);
9090. 994 constant("true", "boolean", true);
9091. 994 constant("undefined", "undefined");
9092. 994 infix(100, "!=");
9093. 994 infix(100, "!==");
9094. 994 infix(100, "==");
9095. 994 infix(100, "===");
9096. 994 infix(110, "<");
9097. 994 infix(110, "<=");
9098. 994 infix(110, ">");
9099. 994 infix(110, ">=");
9100. 994 infix(110, "in");
9101. 994 infix(110, "instanceof");
9102. 994
9103. 994// PR-504 - Add ES2015-feature for..of.
9104. 994
9105. 994 infix(110, "of");
9106. 994 infix(120, "<<");
9107. 994 infix(120, ">>");
9108. 994 infix(120, ">>>");
9109. 994 infix(130, "+");
9110. 994 infix(130, "-");
9111. 994 infix(140, "%");
9112. 994 infix(140, "*");
9113. 994 infix(140, "/");
9114. 994 infix(160, "(", infix_lparen);
9115. 994 infix(160, "`", infix_grave);
9116. 994 infix(170, ".", infix_dot);
9117. 994 infix(170, "=>", infix_fart_unwrapped);
9118. 994 infix(170, "?.", infix_dot);
9119. 994 infix(170, "[", infix_lbracket);
9120. 994 infix(35, "??");
9121. 994 infix(40, "||");
9122. 994 infix(50, "&&");
9123. 994 infix(70, "|");
9124. 994 infix(80, "^");
9125. 994 infix(90, "&");
9126. 994 infixr(150, "**");
9127. 994 postassign("++");
9128. 994 postassign("--");
9129. 994 preassign("++");
9130. 994 preassign("--");
9131. 994 prefix("!!");
9132. 994 prefix("!");
9133. 994 prefix("(", prefix_lparen);
9134. 994 prefix("+");
9135. 994 prefix("-");
9136. 994 prefix("/=", prefix_assign_divide);
9137. 994 prefix("=>", prefix_fart);
9138. 994 prefix("[", prefix_lbracket);
9139. 994 prefix("`", prefix_tick);
9140. 994 prefix("async", prefix_async);
9141. 994 prefix("await", prefix_await);
9142. 994 prefix("function", prefix_function);
9143. 994 prefix("new", prefix_new);
9144. 994 prefix("typeof");
9145. 994 prefix("void", prefix_void);
9146. 994 prefix("{", prefix_lbrace);
9147. 994 prefix("~");
9148. 994 stmt(";", stmt_semicolon);
9149. 994 stmt("async", prefix_async);
9150. 994 stmt("await", prefix_await);
9151. 994 stmt("break", stmt_break);
9152. 994 stmt("const", stmt_var);
9153. 994 stmt("continue", stmt_continue);
9154. 994 stmt("debugger", stmt_debugger);
9155. 994 stmt("delete", stmt_delete);
9156. 994 stmt("do", stmt_do);
9157. 994 stmt("export", stmt_export);
9158. 994 stmt("for", stmt_for);
9159. 994 stmt("function", prefix_function);
9160. 994 stmt("if", stmt_if);
9161. 994 stmt("import", stmt_import);
9162. 994 stmt("let", stmt_var);
9163. 994 stmt("return", stmt_return);
9164. 994 stmt("switch", stmt_switch);
9165. 994 stmt("throw", stmt_throw);
9166. 994 stmt("try", stmt_try);
9167. 994 stmt("var", stmt_var);
9168. 994 stmt("while", stmt_while);
9169. 994 stmt("with", stmt_with);
9170. 994 stmt("{", stmt_lbrace);
9171. 994 symbol(")");
9172. 994 symbol("*/");
9173. 994 symbol(",");
9174. 994 symbol(":");
9175. 994 symbol(";");
9176. 994 symbol("]");
9177. 994 symbol("case");
9178. 994 symbol("catch");
9179. 994 symbol("class");
9180. 994 symbol("default");
9181. 994 symbol("else");
9182. 994 symbol("enum");
9183. 994 symbol("finally");
9184. 994 symbol("implements");
9185. 994 symbol("interface");
9186. 994 symbol("package");
9187. 994 symbol("private");
9188. 994 symbol("protected");
9189. 994 symbol("public");
9190. 994 symbol("static");
9191. 994 symbol("super");
9192. 994 symbol("void");
9193. 994 symbol("yield");
9194. 994 symbol("}");
9195. 994 ternary("?", ":");
9196. 994
9197. 994// Init block_stack, function_stack.
9198. 994
9199. 994 block_stack.length = 0;
9200. 994 function_stack.length = 0;
9201. 994 scope_block = scope_block_push(token_global, true);
9202. 994 scope_function = scope_function_push(token_global, false);
9203. 994
9204. 994// Init token_nxt.
9205. 994
9206. 994 advance();
9207. 994
9208. 994// Parsing of JSON is simple:
9209. 994
9210. 26 if (state.mode_json) {
9211. 26 state.token_tree = parse_json();
9212. 26 advance("(end)");
9213. 26 return;
9214. 968 }
9215. 968
9216. 968// Because browsers encourage combining of script files, the first token might
9217. 968// be a semicolon to defend against a missing semicolon in the preceding file.
9218. 968
9219. 968 if (option_dict.browser) {
9220. 10 if (token_nxt.id === ";") {
9221. 10 advance(";");
9222. 10 }
9223. 10
9224. 10// If we are not in a browser, then the file form of strict pragma may be used.
9225. 10
9226. 958 } else if (token_nxt.value === "use strict") {
9227. 958 advance("(string)");
9228. 958 semicolon();
9229. 968 }
9230. 968 state.token_tree = parse_statement_block();
9231. 968 advance("(end)");
9232. 968
9233. 968// Check top-level functions are ordered.
9234. 968
9235. 968// PR-514 - Bugfix - Also check an exported function-declaration, which is
9236. 968// hoisted too, though <stmt_export> resets its arity to 'unary'.
9237. 968
9238. 968// test_cause:
9239. 968// ["
9240. 968// export async function bb(){}export async function aa(){}
9241. 968// ", "check_ordered", "expected_a_b_before_c_d", "aa", 51]
9242. 968// ["
9243. 968// export function bb(){}export default function aa(){}
9244. 968// ", "check_ordered", "expected_a_b_before_c_d", "aa", 47]
9245. 968// ["
9246. 968// export function bb(){}export function aa(){}
9247. 968// ", "check_ordered", "expected_a_b_before_c_d", "aa", 39]
9248. 968
9249. 968 check_ordered(
9250. 968 "function",
9251. 2351 function_list.map(function (the_function) {
9252. 2351 const {
9253. 2351 arity,
9254. 2351 level,
9255. 2351 name
9256. 2351 } = the_function;
9257. 2351 return (
9258. 2351 (
9259. 1123 arity === "statement" ||
9260. 1123 Object.values(export_dict).includes(the_function)
9261. 1237 ) &&
9262. 1237 level === 1 &&
9263. 968 name
9264. 2351 );
9265. 2351 }).filter(function (name) {
9266. 2265 return option_dict.beta && name && name.id;
9267. 2351 })
9268. 968 );
9269. 968
9270. 968// Cleanup block_stack, function_stack.
9271. 968
9272. 968 jslint_assert(
9273. 968 block_stack.length === 1,
9274. 968 `block_stack.length=${block_stack.length}.`
9275. 968 );
9276. 968 jslint_assert(
9277. 968 function_stack.length === 1,
9278. 968 `function_stack.length=${function_stack.length}.`
9279. 968 );
9280. 968 block_stack.length = 0;
9281. 968 function_stack.length = 0;
9282. 968}
9283. 23
9284. 861function jslint_phase4_walk(state) {
9285. 861
9286. 861// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
9287. 861// recursive traversal. Each node may be processed on the way down
9288. 861// (preaction) and on the way up (postaction).
9289. 861
9290. 861 const {
9291. 861 artifact,
9292. 861 block_stack,
9293. 861 function_stack,
9294. 861 global_dict,
9295. 861 is_equal,
9296. 861 is_weird,
9297. 861 option_dict,
9298. 861 scope_block_pop,
9299. 861 scope_block_push,
9300. 861 scope_function_pop,
9301. 861 scope_function_push,
9302. 861 scope_report_push,
9303. 861 syntax_dict,
9304. 861 token_global,
9305. 861 warn
9306. 861 } = state;
9307. 861 let postaction;
9308. 861 let postamble;
9309. 861 let posts = empty();
9310. 861 let preaction;
9311. 861 let preamble;
9312. 861 let pres = empty();
9313. 861
9314. 861// The relational operators.
9315. 861
9316. 861 let relationop = object_assign_from_list(empty(), [
9317. 861 "!=", "!==", "<", "<=", "==", "===", ">", ">="
9318. 861 ], true);
9319. 861 let scope_block; // The current block-scope.
9320. 861 let scope_function; // The current function-scope.
9321. 861
9322. 861// Ambulation of the parse tree.
9323. 861
9324. 1722 function action(when) {
9325. 1722
9326. 1722// Produce a function that will register task functions that will be called as
9327. 1722// the tree is traversed.
9328. 1722
9329. 31857 return function (arity, id, task) {
9330. 31857 let a_set = when[arity];
9331. 31857 let i_set;
9332. 31857
9333. 31857// If this arity has no registrations yet, then create a set object to hold
9334. 31857// them.
9335. 31857
9336. 10332 if (a_set === undefined) {
9337. 10332 a_set = empty();
9338. 10332 when[arity] = a_set;
9339. 10332 }
9340. 31857
9341. 31857// If this id has no registrations yet, then create a set array to hold them.
9342. 31857
9343. 31857 i_set = a_set[id];
9344. 30996 if (i_set === undefined) {
9345. 30996 i_set = [];
9346. 30996 a_set[id] = i_set;
9347. 30996 }
9348. 31857
9349. 31857// Register the task with the arity and the id.
9350. 31857
9351. 31857 i_set.push(task);
9352. 31857 };
9353. 1722 }
9354. 861
9355. 1722 function amble(when) {
9356. 1722
9357. 1722// Produce a function that will act on the tasks registered by an action
9358. 1722// function while walking the tree.
9359. 1722
9360. 237648 return function (the_token) {
9361. 237648
9362. 237648// Given a task set that was built by an action function, run all
9363. 237648// relevant tasks on the token.
9364. 237648
9365. 237648 let a_set = when[the_token.arity];
9366. 237648 let i_set;
9367. 237648
9368. 237648// If there are tasks associated with the token's arity...
9369. 237648
9370. 164428 if (a_set !== undefined) {
9371. 164428
9372. 164428// If there are tasks associated with the token's id...
9373. 164428
9374. 164428 i_set = a_set[the_token.id];
9375. 164428 if (i_set !== undefined) {
9376. 164428 i_set.forEach(function (task) {
9377. 164428 task(the_token);
9378. 164428 });
9379. 164428 }
9380. 164428
9381. 164428// If there are tasks for all ids.
9382. 164428
9383. 164428 i_set = a_set["(all)"];
9384. 164428 if (i_set !== undefined) {
9385. 164428 i_set.forEach(function (task) {
9386. 164428 task(the_token);
9387. 164428 });
9388. 164428 }
9389. 164428 }
9390. 237648 };
9391. 1722 }
9392. 861
9393. 3768 function check_assignable(name, the_variable) {
9394. 3768
9395. 3768// PR-514 - This function will warn bad_assignment_a when <name> has a readonly
9396. 3768// binding, such as a const, an import, a catch variable or a function name,
9397. 3768// and return false for that or for no binding.
9398. 3768
9399. 104 if (!the_variable) {
9400. 104
9401. 104// PR-514 - An undeclared <name> returns false with no warning, since
9402. 104// <name_lookup> already warned undeclared_a on this token, and <warn> keeps
9403. 104// only the first warning of a token.
9404. 104
9405. 104// test_cause:
9406. 104// ["aa+=0", "check_assignable", "(live)", "", 0]
9407. 104// ["aa=0", "check_assignable", "(live)", "", 0]
9408. 104
9409. 104 return false;
9410. 3664 }
9411. 3664 if (the_variable.readonly) {
9412. 6
9413. 6// test_cause:
9414. 6// ["const aa=0;++aa", "check_assignable", "bad_assignment_a", "aa", 14]
9415. 6// ["const aa=0;aa++", "check_assignable", "bad_assignment_a", "aa", 12]
9416. 6// ["const aa=0;aa+=0", "check_assignable", "bad_assignment_a", "aa", 12]
9417. 6// ["const aa=0;aa=0", "check_assignable", "bad_assignment_a", "aa", 12]
9418. 6// ["
9419. 6// function aa(){}
9420. 6// aa=0
9421. 6// ", "check_assignable", "bad_assignment_a", "aa", 1]
9422. 6
9423. 6 warn("bad_assignment_a", name);
9424. 6 return false;
9425. 3658 }
9426. 3658 return true;
9427. 3658 }
9428. 861
9429. 35524 function name_lookup(thing) {
9430. 35524
9431. 35524// This function will look up <thing>.id from the current scope outward, and
9432. 35524// return its variable, parameter, label or global, or undefined if undeclared.
9433. 35524
9434. 35524 const id = thing.id;
9435. 35524 const ii_function = block_stack.indexOf(scope_function);
9436. 35524 let the_label;
9437. 35524 let the_variable;
9438. 35524
9439. 35524// PR-510 - deadcode-confirmed - Both callers pass a token already known to be
9440. 35524// a variable. <pre_v_var> is registered as a preaction on arity "variable",
9441. 35524// and <post_a_assignment> walks an assignment token's <name_list>, which only
9442. 35524// <name_declare> calls with role "variable" fill. That role sets <arity>.
9443. 35524//
9444. 35524// deadcode-revive - <name_declare> pushes into the list it is given before
9445. 35524// it checks the role, so a list built under another role holds names with no
9446. 35524// <arity>. <the_function>.name_list collects "parameter" names that way. Pass
9447. 35524// one here and the lookup below resolves the wrong name, with no error.
9448. 35524//
9449. 35524// if (thing.arity !== "variable") {
9450. 35524// return;
9451. 35524// }
9452. 35524
9453. 35524 jslint_assert(
9454. 35524 !(thing.arity !== "variable"),
9455. 35524 `Expected !(thing.arity !== "variable").`
9456. 35524 );
9457. 35524
9458. 35524// Look up the variable, from current-scope, moving up the scope-chain.
9459. 35524
9460. 121328 block_stack.some(function (scope_block, ii) {
9461. 121328 the_variable = scope_block.context[id];
9462. 121328
9463. 121328// PR-514 - Bugfix - Skip a label, so a same-named variable, function or global
9464. 121328// in an outer scope is still found.
9465. 121328
9466. 34906 if (the_variable?.role === "label") {
9467. 3
9468. 3// test_cause:
9469. 3// ["aa:while(0){aa}", "name_lookup", "(live)", "", 0]
9470. 3// ["function aa(){aa:while(aa){break aa;}}", "name_lookup", "(live)", "", 0]
9471. 3
9472. 3 the_label = the_variable;
9473. 3 the_variable = undefined;
9474. 3 }
9475. 34903 if (the_variable && ii > ii_function) {
9476. 15123
9477. 15123// PR-516 - Bugfix - Mark closure only if found outside the current function,
9478. 15123// not merely outside the current scope_block, and record it for the report.
9479. 15123
9480. 15123 the_variable.closure = true;
9481. 15123 scope_report_push(scope_function, the_variable);
9482. 15123 }
9483. 121328 return the_variable;
9484. 121328 });
9485. 621 if (!the_variable && global_dict[id] === undefined) {
9486. 223 if (the_label) {
9487. 223
9488. 223// test_cause:
9489. 223// ["aa:while(0){aa}", "name_lookup", "label_a", "aa", 13]
9490. 223
9491. 223 warn("label_a", thing);
9492. 223 return the_label;
9493. 223 }
9494. 223
9495. 223// test_cause:
9496. 223// ["(function aa(){})aa", "name_lookup", "undeclared_a", "aa", 18]
9497. 223// ["aa", "name_lookup", "undeclared_a", "aa", 1]
9498. 223// ["class aa{}", "name_lookup", "undeclared_a", "aa", 7]
9499. 223// ["
9500. 223// function aa(){function bb(){}}
9501. 223// bb
9502. 223// ", "name_lookup", "undeclared_a", "bb", 1]
9503. 223// ["
9504. 223// function aa(){var bb}
9505. 223// bb
9506. 223// ", "name_lookup", "undeclared_a", "bb", 1]
9507. 223// ["if(0){const aa=0}aa", "name_lookup", "undeclared_a", "aa", 18]
9508. 223// ["if(0){function aa(){}}aa", "name_lookup", "undeclared_a", "aa", 23]
9509. 223// ["if(0){let aa}aa", "name_lookup", "undeclared_a", "aa", 14]
9510. 223// ["try{}catch(aa){}aa", "name_lookup", "undeclared_a", "aa", 17]
9511. 223// ["try{}finally{aa}", "name_lookup", "undeclared_a", "aa", 14]
9512. 223
9513. 223 warn("undeclared_a", thing);
9514. 223 return;
9515. 35301 }
9516. 35301
9517. 35301// If it isn't in any of those either, perhaps it is a predefined global.
9518. 35301// If so, add it to the global context.
9519. 35301
9520. 35301 if (!the_variable) {
9521. 398 the_variable = {
9522. 398
9523. 398// 3.glo.2 - Mark 'alive', the global-variable, immediately.
9524. 398
9525. 398 alive: true,
9526. 398
9527. 398// 3.glo.3 - Mark 'assigned', the global-variable, immediately.
9528. 398
9529. 398 assigned: true,
9530. 398 id,
9531. 398
9532. 398// 3.glo.4 - Mark 'readonly', the global-variable, immediately.
9533. 398
9534. 398 readonly: true,
9535. 398 role: "variable",
9536. 398 scope_declared: token_global
9537. 398 };
9538. 398
9539. 398// 3.glo.1 - Mark 'declared', the global-variable, immediately.
9540. 398
9541. 398 token_global.context[id] = the_variable;
9542. 398 scope_report_push(scope_function, the_variable);
9543. 35301 }
9544. 35301 if (!the_variable.alive) {
9545. 29
9546. 29// PR-514 - Deviation kept 2026-09-29. A function reading a 'let' declared
9547. 29// below it warns, though valid when called later. This matches ESLint
9548. 29// no-use-before-define.
9549. 29
9550. 29// test_cause:
9551. 29// ["(aa=aa)=>0", "name_lookup", "temporal_dead_zone_a", "aa", 5]
9552. 29// ["
9553. 29// (function aa(){aa();var aa})
9554. 29// ", "name_lookup", "temporal_dead_zone_a", "aa", 16]
9555. 29// ["for(const [aa] of aa){}", "name_lookup", "temporal_dead_zone_a", "aa", 19]
9556. 29// ["let [aa]=aa", "name_lookup", "temporal_dead_zone_a", "aa", 10]
9557. 29// ["let aa=()=>aa", "name_lookup", "temporal_dead_zone_a", "aa", 12]
9558. 29// ["let aa=aa", "name_lookup", "temporal_dead_zone_a", "aa", 8]
9559. 29// ["let aa=bb;let bb", "name_lookup", "temporal_dead_zone_a", "bb", 8]
9560. 29
9561. 29 warn("temporal_dead_zone_a", thing);
9562. 35301 }
9563. 35301 return the_variable;
9564. 35301 }
9565. 861
9566. 5333 function post_a_assignment(thing) {
9567. 5333
9568. 5333// Assignment using = sets the assigned property of a variable, and so do '??='
9569. 5333// and '||=' below. A = token keeps that variable, or the variables of a
9570. 5333// destructuring, in its name_list property.
9571. 5333
9572. 5333 const lvalue = thing.expression[0];
9573. 5333 let right;
9574. 772 if (thing.id !== "=") {
9575. 772 if (
9576. 772 lvalue.arity === "variable" &&
9577. 772 check_assignable(lvalue, lvalue.variable) &&
9578. 772 (thing.id === "??=" || thing.id === "||=")
9579. 772 ) {
9580. 772
9581. 772// PR-514 - Bugfix - '??=' and '||=' assign an unassigned variable, since its
9582. 772// 'undefined' is nullish and falsy, so it counts as assigned. '&&=' does not
9583. 772// assign it, and still warns unassigned_var_a.
9584. 772
9585. 772// test_cause:
9586. 772// ["let aa;aa??=0", "post_a_assignment", "(live)", "", 0]
9587. 772// ["let aa;aa||=0", "post_a_assignment", "(live)", "", 0]
9588. 772
9589. 772 lvalue.variable.assigned = true;
9590. 772 }
9591. 772 right = syntax_dict[thing.expression[1].id];
9592. 772 if (
9593. 772 right &&
9594. 772 (
9595. 772 right.id === "function" ||
9596. 772 right.id === "=>" ||
9597. 772 (
9598. 772 right.constant &&
9599. 772 right.id !== "(number)" &&
9600. 772 (right.id !== "(string)" || thing.id !== "+=")
9601. 772 )
9602. 772 )
9603. 772 ) {
9604. 772
9605. 772// test_cause:
9606. 772// ["aa-=undefined", "post_a_assignment", "unexpected_a", "undefined", 5]
9607. 772
9608. 772 warn("unexpected_a", thing.expression[1]);
9609. 772 }
9610. 772 return;
9611. 4561 }
9612. 4561 if (thing.name_list) {
9613. 3099 for (const name of thing.name_list) {
9614. 3099 const the_variable = name_lookup(name);
9615. 3099 if (check_assignable(name, the_variable)) {
9616. 3099
9617. 3099// 3.var.3 - Mark 'assigned', the variable, after assignment.
9618. 3099
9619. 3099 the_variable.assigned = true;
9620. 3099 }
9621. 3099 }
9622. 3060 return;
9623. 3060 }
9624. 1501 if (lvalue.id === "." && thing.expression[1].id === "undefined") {
9625. 2
9626. 2// test_cause:
9627. 2// ["
9628. 2// let aa; aa.aa=undefined;
9629. 2// ", "post_a_assignment", "expected_a_b", "undefined", 9]
9630. 2// ["
9631. 2// let aa;aa.aa=undefined;
9632. 2// ", "post_a_assignment", "expected_a_b", "undefined", 8]
9633. 2
9634. 2 warn("expected_a_b", lvalue.expression, "delete", "undefined");
9635. 2 return;
9636. 2 }
9637. 5333 }
9638. 861
9639. 682 function post_a_pluseq(thing) {
9640. 682 const right = thing.expression[1];
9641. 340 if (right.constant) {
9642. 340 if (
9643. 340 right.value === "" ||
9644. 340 (right.id === "(number)" && right.value === "0") ||
9645. 340 right.id === "(boolean)" ||
9646. 340 right.id === "null" ||
9647. 340 right.id === "undefined" ||
9648. 340 Number.isNaN(right.value)
9649. 340 ) {
9650. 340
9651. 340// test_cause:
9652. 340// ["let aa=0;aa+=0;", "post_a_pluseq", "unexpected_a", "0", 14]
9653. 340
9654. 340 warn("unexpected_a", right);
9655. 340 }
9656. 340 }
9657. 682 }
9658. 861
9659. 1532 function post_b_and(thing) {
9660. 1532 if (
9661. 1527 is_weird(thing.expression[0]) ||
9662. 1527 is_equal(thing.expression[0], thing.expression[1]) ||
9663. 1512 thing.expression[0].constant === true ||
9664. 1510 thing.expression[1].constant === true
9665. 22 ) {
9666. 22
9667. 22// test_cause:
9668. 22// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
9669. 22// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
9670. 22// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
9671. 22// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
9672. 22// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
9673. 22// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
9674. 22// ["aa=bb``&&bb``", "post_b_and", "weird_condition_a", "&&", 8]
9675. 22// ["
9676. 22// aa=function aa(){}&&function aa(){}
9677. 22// ", "post_b_and", "weird_condition_a", "&&", 19]
9678. 22// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
9679. 22
9680. 22 warn("weird_condition_a", thing);
9681. 22 }
9682. 1532 }
9683. 861
9684. 35748 function post_b_binary(thing) {
9685. 35748 let right;
9686. 4312 if (relationop[thing.id]) {
9687. 4312 if (
9688. 4312 is_weird(thing.expression[0]) ||
9689. 4312 is_weird(thing.expression[1]) ||
9690. 4312 is_equal(thing.expression[0], thing.expression[1]) ||
9691. 4312 (
9692. 4312 thing.expression[0].constant === true &&
9693. 4312 thing.expression[1].constant === true
9694. 4312 )
9695. 4312 ) {
9696. 4312
9697. 4312// test_cause:
9698. 4312// ["if(0===0){0}", "post_b_binary", "weird_relation_a", "===", 5]
9699. 4312
9700. 4312 warn("weird_relation_a", thing);
9701. 4312 }
9702. 4312 }
9703. 35748 switch (thing.id) {
9704. 11507 case "(":
9705. 11531 case "=>":
9706. 35748
9707. 35748// Tagged-megastring is infix at same binding-power as "(" and is a call,
9708. 35748// not binary-operator.
9709. 35748
9710. 11541 case "`":
9711. 11541 break;
9712. 2159 case "+":
9713. 2159 if (!option_dict.convert) {
9714. 2159 if (thing.expression[0].value === "") {
9715. 2159
9716. 2159// test_cause:
9717. 2159// ["let aa=\"\"+0;", "post_b_binary", "expected_a_b", "\"\" +", 10]
9718. 2159
9719. 2159 warn("expected_a_b", thing, "String(...)", "\"\" +");
9720. 2159 } else if (thing.expression[1].value === "") {
9721. 2159
9722. 2159// test_cause:
9723. 2159// ["let aa=0+\"\";", "post_b_binary", "expected_a_b", "+ \"\"", 9]
9724. 2159
9725. 2159 warn("expected_a_b", thing, "String(...)", "+ \"\"");
9726. 2159 }
9727. 2159 }
9728. 2159 break;
9729. 12903 case ".":
9730. 12961 case "?.":
9731. 12961 if (thing.expression.id === "RegExp") {
9732. 12961
9733. 12961// PR-499 - Relax warning for ES2025-feature RegExp.escape().
9734. 12961
9735. 12961 if (!thing.name || thing.name.id !== "escape") {
9736. 12961
9737. 12961// test_cause:
9738. 12961// ["aa=RegExp.aa", "post_b_binary", "weird_expression_a", ".", 10]
9739. 12961
9740. 12961 warn("weird_expression_a", thing);
9741. 12961 }
9742. 12961 }
9743. 12961 break;
9744. 1539 case "[":
9745. 1539 if (thing.expression[0].id === "window") {
9746. 1539
9747. 1539// test_cause:
9748. 1539// ["aa=window[0]", "post_b_binary", "weird_expression_a", "window[...]", 10]
9749. 1539
9750. 1539 warn("weird_expression_a", thing, "window[...]");
9751. 1539 }
9752. 1539 if (thing.expression[0].id === "self") {
9753. 1539
9754. 1539// test_cause:
9755. 1539// ["aa=self[0]", "post_b_binary", "weird_expression_a", "self[...]", 8]
9756. 1539
9757. 1539 warn("weird_expression_a", thing, "self[...]");
9758. 1539 }
9759. 1539 break;
9760. 7548 default:
9761. 7548 right = thing.expression[1];
9762. 7548 if (
9763. 7548 (thing.id === "+" || thing.id === "-") &&
9764. 7548 right.id === thing.id &&
9765. 7548 right.arity === "unary" &&
9766. 7548 !right.wrapped
9767. 7548 ) {
9768. 7548
9769. 7548// test_cause:
9770. 7548// ["0- -0", "post_b_binary", "wrap_subexpression_a_b", "-", 4]
9771. 7548
9772. 7548 warn("wrap_subexpression_a_b", right, right.id, thing.id);
9773. 7548 }
9774. 7548
9775. 7548 if (
9776. 7548 thing.id === "**" &&
9777. 7548 thing.expression[0].arity === "unary" &&
9778. 7548 !thing.expression[0].wrapped &&
9779. 7548
9780. 7548// PR-517 - deadcode-confirmed - A 'void' here never showed its warning, since
9781. 7548// <prefix_void> has already warned on the same token, and <warn> drops a
9782. 7548// second warning on one token.
9783. 7548//
9784. 7548// deadcode-revive - Let a token carry more than one warning, and list "void".
9785. 7548
9786. 7548 [
9787. 7548 "!", "!!", "+", "-", "await", "typeof", "~"
9788. 7548 ].includes(thing.expression[0].id)
9789. 7548 ) {
9790. 7548
9791. 7548// PR-514 - Bugfix - A unary operator before '**' is a SyntaxError, since the
9792. 7548// spec's ExponentiationExpression takes an UpdateExpression on its left. So
9793. 7548// '-aa ** 2' needs parens, while '[aa] ** 2' and '++aa ** 2' do not.
9794. 7548
9795. 7548// test_cause:
9796. 7548// ["
9797. 7548// /*jslint bitwise*/
9798. 7548// let aa;aa=~aa**aa;
9799. 7548// ", "post_b_binary", "wrap_subexpression_a_b", "**", 11]
9800. 7548// ["
9801. 7548// /*jslint convert*/
9802. 7548// let aa;aa=!!aa**aa;
9803. 7548// ", "post_b_binary", "wrap_subexpression_a_b", "**", 11]
9804. 7548// ["
9805. 7548// /*jslint convert*/
9806. 7548// let aa;aa=+aa**aa;
9807. 7548// ", "post_b_binary", "wrap_subexpression_a_b", "**", 11]
9808. 7548// ["aa=-0**0", "post_b_binary", "wrap_subexpression_a_b", "**", 4]
9809. 7548// ["
9810. 7548// async function aa(){return await aa**0;}
9811. 7548// ", "post_b_binary", "wrap_subexpression_a_b", "**", 28]
9812. 7548// ["let aa;aa=!aa**aa;", "post_b_binary", "wrap_subexpression_a_b", "**", 11]
9813. 7548
9814. 7548 warn(
9815. 7548 "wrap_subexpression_a_b",
9816. 7548 thing.expression[0],
9817. 7548 thing.expression[0].id,
9818. 7548 "**"
9819. 7548 );
9820. 7548 }
9821. 7548
9822. 7548 if (thing.id === "??") {
9823. 7548
9824. 7548// PR-514 - Bugfix - An unwrapped '&&' or '||' operand of '??' is a SyntaxError,
9825. 7548// since the spec's CoalesceExpression takes a BitwiseORExpression on each side.
9826. 7548// '??' binds loosest of the three, so only a '??' node can hold one.
9827. 7548
9828. 7548 thing.expression.forEach(function (thang) {
9829. 7548 if (
9830. 7548 (thang.id === "&&" || thang.id === "||") &&
9831. 7548 !thang.wrapped
9832. 7548 ) {
9833. 7548
9834. 7548// test_cause:
9835. 7548// ["aa&&bb??cc", "post_b_binary", "wrap_subexpression_a_b", "??", 3]
9836. 7548// ["aa??bb||cc", "post_b_binary", "wrap_subexpression_a_b", "??", 7]
9837. 7548
9838. 7548 warn("wrap_subexpression_a_b", thang, thang.id, "??");
9839. 7548 }
9840. 7548 });
9841. 7548 }
9842. 7548 if (
9843. 7548 thing.expression[0].constant === true &&
9844. 7548 right.constant === true
9845. 7548 ) {
9846. 7548 thing.constant = true;
9847. 7548 }
9848. 35748 }
9849. 35748 }
9850. 861
9851. 1539 function post_b_lbracket(thing) {
9852. 1 if (thing.expression[0].id === "RegExp") {
9853. 1
9854. 1// test_cause:
9855. 1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
9856. 1
9857. 1 warn("weird_expression_a", thing);
9858. 1 }
9859. 1 if (is_weird(thing.expression[1])) {
9860. 1
9861. 1// test_cause:
9862. 1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
9863. 1
9864. 1 warn("weird_expression_a", thing.expression[1]);
9865. 1 }
9866. 1539 }
9867. 861
9868. 11507 function post_b_lparen(thing) {
9869. 11507 let arg;
9870. 11507 let array;
9871. 11507 let cack;
9872. 11507 let left = thing.expression[0];
9873. 11507 let new_date;
9874. 11507 let paren;
9875. 11507 let the_new;
9876. 168 if (left.id === "new") {
9877. 168 the_new = left;
9878. 168 left = left.expression;
9879. 168 }
9880. 39 if (left.id === "function") {
9881. 39 if (!thing.wrapped) {
9882. 39
9883. 39// test_cause:
9884. 39// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
9885. 39
9886. 39 warn("wrap_immediate", thing);
9887. 39 }
9888. 11468 } else if (left.identifier) {
9889. 11468 if (the_new !== undefined) {
9890. 11468
9891. 11468// PR-517 - deadcode-confirmed - A branch for 'new Function' never showed its
9892. 11468// warning. Without option eval, <constant_Function> has already warned on the
9893. 11468// same 'Function' token, and <warn> drops a second warning on one token.
9894. 11468//
9895. 11468// deadcode-revive - Let a token carry more than one warning.
9896. 11468//
9897. 11468// } else if (left.id === "Function") {
9898. 11468// if (!option_dict.eval) {
9899. 11468// warn("unexpected_a", left, "new Function");
9900. 11468// }
9901. 11468
9902. 11468 if (
9903. 11468 left.id[0] > "Z" ||
9904. 11468 left.id === "BigInt" ||
9905. 11468 left.id === "Boolean" ||
9906. 11468 left.id === "Number" ||
9907. 11468 left.id === "String" ||
9908. 11468 left.id === "Symbol"
9909. 11468 ) {
9910. 11468
9911. 11468// test_cause:
9912. 11468// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
9913. 11468// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
9914. 11468// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
9915. 11468// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
9916. 11468// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
9917. 11468// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
9918. 11468
9919. 11468 warn("unexpected_a", the_new);
9920. 11468 } else if (left.id === "Array") {
9921. 11468 arg = thing.expression;
9922. 11468 if (arg.length !== 2 || arg[1].id === "(string)") {
9923. 11468
9924. 11468// test_cause:
9925. 11468// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
9926. 11468// ["new Array(0,\"bb\")", "post_b_lparen", "expected_a_b", "new Array", 5]
9927. 11468
9928. 11468 warn("expected_a_b", left, "[]", "new Array");
9929. 11468 }
9930. 11468 } else if (left.id === "Object") {
9931. 11468
9932. 11468// test_cause:
9933. 11468// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
9934. 11468
9935. 11468 warn(
9936. 11468 "expected_a_b",
9937. 11468 left,
9938. 11468 "Object.create(null)",
9939. 11468 "new Object"
9940. 11468 );
9941. 11468 }
9942. 11468 } else {
9943. 11468 if (
9944. 11468 left.id[0] >= "A" &&
9945. 11468 left.id[0] <= "Z" &&
9946. 11468 left.id !== "BigInt" &&
9947. 11468 left.id !== "Boolean" &&
9948. 11468 left.id !== "Number" &&
9949. 11468 left.id !== "String" &&
9950. 11468 left.id !== "Symbol"
9951. 11468 ) {
9952. 11468
9953. 11468// test_cause:
9954. 11468// ["let Aa;let bb=Aa();", "post_b_lparen", "expected_a_before_b", "Aa", 15]
9955. 11468
9956. 11468 warn("expected_a_before_b", left, "new", artifact(left));
9957. 11468 }
9958. 11468 }
9959. 11468 } else if (left.id === ".") {
9960. 11468 cack = the_new !== undefined;
9961. 11468 if (left.expression.id === "Date" && left.name.id === "UTC") {
9962. 11468
9963. 11468// test_cause:
9964. 11468// ["new Date.UTC()", "post_b_lparen", "(live)", "", 0]
9965. 11468
9966. 11468 cack = !cack;
9967. 11468 }
9968. 11468 if (jslint_rgx_cap.test(left.name.id) !== cack) {
9969. 11468 if (the_new !== undefined) {
9970. 11468
9971. 11468// test_cause:
9972. 11468// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
9973. 11468
9974. 11468 warn("unexpected_a", the_new);
9975. 11468 } else {
9976. 11468
9977. 11468// test_cause:
9978. 11468// ["let Aa;let bb=Aa.Aa();", "post_b_lparen", "expected_a_before_b", "Aa", 15]
9979. 11468
9980. 11468 warn(
9981. 11468 "expected_a_before_b",
9982. 11468 left.expression,
9983. 11468 "new",
9984. 11468 left.name.id
9985. 11468 );
9986. 11468 }
9987. 11468 }
9988. 11468 if (left.name.id === "getTime") {
9989. 11468 paren = left.expression;
9990. 11468 if (paren.id === "(") {
9991. 11468 array = paren.expression;
9992. 11468 if (array.length === 1) {
9993. 11468 new_date = array[0];
9994. 11468 if (
9995. 11468 new_date.id === "new" &&
9996. 11468 new_date.expression.id === "Date"
9997. 11468 ) {
9998. 11468
9999. 11468// test_cause:
10000. 11468// ["
10001. 11468// new Date().getTime()
10002. 11468// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
10003. 11468
10004. 11468 warn(
10005. 11468 "expected_a_b",
10006. 11468 new_date,
10007. 11468 "Date.now()",
10008. 11468 "new Date().getTime()"
10009. 11468 );
10010. 11468 }
10011. 11468 }
10012. 11468 }
10013. 11468 }
10014. 11468 }
10015. 11507 }
10016. 861
10017. 1049 function post_b_or(thing) {
10018. 1049 if (
10019. 1049 is_weird(thing.expression[0]) ||
10020. 1047 is_equal(thing.expression[0], thing.expression[1]) ||
10021. 1047 thing.expression[0].constant === true
10022. 3 ) {
10023. 3
10024. 3// test_cause:
10025. 3// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
10026. 3
10027. 3 warn("weird_condition_a", thing);
10028. 3 }
10029. 1049 }
10030. 861
10031. 10 function post_p_update(thing) {
10032. 10 if (thing.expression.arity === "variable") {
10033. 10 check_assignable(thing.expression, thing.expression.variable);
10034. 10 }
10035. 10 }
10036. 861
10037. 99 function post_s_export_toplevel(the_thing) {
10038. 99
10039. 99// Some features must be at the most outermost level.
10040. 99
10041. 1 if (scope_block !== token_global) {
10042. 1
10043. 1// test_cause:
10044. 1// ["
10045. 1// if(0){import aa from "aa";}
10046. 1// ", "post_s_export_toplevel", "misplaced_a", "import", 7]
10047. 1
10048. 1 warn("misplaced_a", the_thing);
10049. 1 }
10050. 99 }
10051. 861
10052. 90 function post_s_for(thing) {
10053. 18 if (thing.for_semicolon) {
10054. 18
10055. 18// Recurse walk_statement.
10056. 18
10057. 18 walk_statement(thing.for_semicolon[2]);
10058. 18 }
10059. 90 scope_block = scope_block_pop();
10060. 90 }
10061. 861
10062. 2351 function post_s_function(thing) {
10063. 2351 delete scope_function.async;
10064. 2351 delete scope_function.finally;
10065. 2351 delete scope_function.loop;
10066. 2351 delete scope_function.switch;
10067. 2351 delete scope_function.try;
10068. 6 if (thing.wrapped) {
10069. 6
10070. 6// test_cause:
10071. 6// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
10072. 6
10073. 6 warn("unexpected_parens", thing);
10074. 6 }
10075. 2351 scope_block = scope_block_pop();
10076. 2351 scope_block = scope_block_pop();
10077. 2351 scope_function = scope_function_pop();
10078. 2351 }
10079. 861
10080. 58 function post_s_import(the_thing) {
10081. 58
10082. 58// 1.imp.2 - Mark 'alive', the import-name, after import-statement.
10083. 58
10084. 58 post_s_var(the_thing);
10085. 58 post_s_export_toplevel(the_thing);
10086. 58 }
10087. 861
10088. 69 function post_s_try(thing) {
10089. 60 if (thing.catch !== undefined) {
10090. 60
10091. 60// PR-504 - Add hidden scope_block for:
10092. 60// - catch-variable
10093. 60
10094. 60 scope_block = scope_block_push(thing.catch, false);
10095. 60
10096. 60// Recurse walk_statement.
10097. 60
10098. 60 walk_statement(thing.catch.block);
10099. 60
10100. 60// Restore previous catch-scope after catch-block.
10101. 60
10102. 60 scope_block = scope_block_pop();
10103. 60 }
10104. 69
10105. 69// PR-514 - Bugfix - Walk the finally-block after the catch-block, as parsed.
10106. 69// Else a 'var' from the catch-block warned temporal_dead_zone_a in it.
10107. 69
10108. 69// Recurse walk_statement.
10109. 69
10110. 69 walk_statement(thing.finally);
10111. 69 }
10112. 861
10113. 5064 function post_s_var(thing) {
10114. 6137 thing.name_list.forEach(function (name) {
10115. 1598 if (name.expression) {
10116. 1598
10117. 1598// test_cause:
10118. 1598// ["function aa([aa=aa]){}", "name_lookup", "temporal_dead_zone_a", "aa", 17]
10119. 1598// ["function aa({aa=aa}){}", "name_lookup", "temporal_dead_zone_a", "aa", 17]
10120. 1598// ["let aa=0", "post_s_var", "(live)", "", 0]
10121. 1598// ["let[aa=bb]=0;let bb", "name_lookup", "temporal_dead_zone_a", "bb", 8]
10122. 1598// ["let{aa=bb}=0;let bb", "name_lookup", "temporal_dead_zone_a", "bb", 8]
10123. 1598
10124. 1598 walk_expression(name.expression);
10125. 1598 }
10126. 6137
10127. 6137// PR-502 - Fix long-running regression where 'let x = x;'
10128. 6137// doesn't warn about temporal-dead-zone.
10129. 6137
10130. 6137// 3.var.2 - Mark 'alive', the variable, after variable-declaration.
10131. 6137
10132. 6137 name.alive = true;
10133. 6137 });
10134. 5064 }
10135. 861
10136. 339 function post_t_ternary(thing) {
10137. 339 if (
10138. 339 is_weird(thing.expression[0]) ||
10139. 330 thing.expression[0].constant === true ||
10140. 330 is_equal(thing.expression[1], thing.expression[2])
10141. 11 ) {
10142. 11
10143. 11// test_cause:
10144. 11// ["let aa=(aa?`${0}`:`${0}`)", "post_t_ternary", "unexpected_a", "?", 11]
10145. 11// ["let aa=(aa?`0`:`0`)", "post_t_ternary", "unexpected_a", "?", 11]
10146. 11
10147. 11 warn("unexpected_a", thing);
10148. 328 } else if (is_equal(thing.expression[0], thing.expression[1])) {
10149. 328
10150. 328// test_cause:
10151. 328// ["let aa;let bb=(aa?aa:0);", "post_t_ternary", "expected_a_b", "?", 18]
10152. 328
10153. 328 warn("expected_a_b", thing, "||", "?");
10154. 328 } else if (is_equal(thing.expression[0], thing.expression[2])) {
10155. 328
10156. 328// test_cause:
10157. 328// ["let aa;let bb=(aa?0:aa);", "post_t_ternary", "expected_a_b", "?", 18]
10158. 328
10159. 328 warn("expected_a_b", thing, "&&", "?");
10160. 328 } else if (
10161. 328 thing.expression[1].id === "true" &&
10162. 328 thing.expression[2].id === "false"
10163. 328 ) {
10164. 328
10165. 328// test_cause:
10166. 328// ["let aa;let bb=(aa?true:false);", "post_t_ternary", "expected_a_b", "?", 18]
10167. 328
10168. 328 warn("expected_a_b", thing, "Boolean(...)", "?");
10169. 328 } else if (
10170. 328 thing.expression[1].id === "false" &&
10171. 328 thing.expression[2].id === "true"
10172. 328 ) {
10173. 328
10174. 328// test_cause:
10175. 328// ["let aa;let bb=(aa?false:true);", "post_t_ternary", "expected_a_b", "?", 18]
10176. 328
10177. 328 warn("expected_a_b", thing, "!", "?");
10178. 328 } else if (
10179. 328 thing.expression[0].wrapped !== true &&
10180. 328 (
10181. 328 thing.expression[0].id === "||" ||
10182. 328 thing.expression[0].id === "&&"
10183. 328 )
10184. 328 ) {
10185. 328
10186. 328// test_cause:
10187. 328// ["(aa&&!aa?0:1)", "post_t_ternary", "wrap_subexpression_a_b", "?", 4]
10188. 328
10189. 328 warn(
10190. 328 "wrap_subexpression_a_b",
10191. 328 thing.expression[0],
10192. 328 thing.expression[0].id,
10193. 328 "?"
10194. 328 );
10195. 328 }
10196. 339 }
10197. 861
10198. 10 function post_u_plus(thing) {
10199. 10 const right = thing.expression;
10200. 7 if (!option_dict.convert) {
10201. 7
10202. 7// test_cause:
10203. 7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
10204. 7
10205. 7 warn("expected_a_b", thing, "Number(...)", "+");
10206. 7 }
10207. 2 if (right.id === "(" && right.expression[0].id === "new") {
10208. 2
10209. 2// test_cause:
10210. 2// ["
10211. 2// /*jslint convert*/let aa=+new Date();
10212. 2// ", "post_u_plus", "unexpected_a_before_b", "new", 26]
10213. 2
10214. 2 warn("unexpected_a_before_b", thing, "+", "new");
10215. 8 } else if (
10216. 8 right.constant ||
10217. 8 right.id === "{" ||
10218. 8 (right.id === "[" && right.arity !== "binary")
10219. 8 ) {
10220. 8
10221. 8// test_cause:
10222. 8// ["/*jslint convert*/aa=+{};", "post_u_plus", "unexpected_a", "+", 22]
10223. 8
10224. 8 warn("unexpected_a", thing, "+");
10225. 8 }
10226. 10 }
10227. 861
10228. 4958 function post_u_unary(thing) {
10229. 904 if (thing.id === "`") {
10230. 904 if (thing.expression.every(function (thing) {
10231. 904 return thing.constant;
10232. 904 })) {
10233. 904 thing.constant = true;
10234. 904 }
10235. 4054 } else if (thing.id === "!") {
10236. 4054 if (thing.expression.constant === true) {
10237. 4054
10238. 4054// test_cause:
10239. 4054// ["let aa=!0;", "post_u_unary", "unexpected_a", "!", 8]
10240. 4054
10241. 4054 warn("unexpected_a", thing);
10242. 4054 }
10243. 4054 } else if (thing.id === "!!") {
10244. 4054 if (!option_dict.convert) {
10245. 4054
10246. 4054// test_cause:
10247. 4054// ["let aa= !!0;", "post_u_unary", "expected_a_b", "!!", 9]
10248. 4054// ["let aa=!!0;", "post_u_unary", "expected_a_b", "!!", 8]
10249. 4054
10250. 4054 warn("expected_a_b", thing, "Boolean(...)", "!!");
10251. 4054 }
10252. 4054 } else if (
10253. 4054 thing.id !== "[" &&
10254. 4054 thing.id !== "{" &&
10255. 4054 thing.id !== "function" &&
10256. 4054 thing.id !== "new"
10257. 4054 ) {
10258. 4054 if (thing.expression.constant === true) {
10259. 4054 thing.constant = true;
10260. 4054 }
10261. 4054 }
10262. 4958 }
10263. 861
10264. 41085 function pre_a_bitwise(thing) {
10265. 41085
10266. 41085// These are the bitwise operators.
10267. 41085
10268. 41078 switch (!option_dict.bitwise && thing.id) {
10269. 3 case "&":
10270. 4 case "&=":
10271. 6 case "<<":
10272. 7 case "<<=":
10273. 9 case ">>":
10274. 10 case ">>=":
10275. 12 case ">>>":
10276. 13 case ">>>=":
10277. 15 case "^":
10278. 16 case "^=":
10279. 18 case "|":
10280. 19 case "|=":
10281. 22 case "~":
10282. 22
10283. 22// test_cause:
10284. 22// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
10285. 22// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
10286. 22// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
10287. 22// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
10288. 22// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
10289. 22// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
10290. 22// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
10291. 22// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
10292. 22// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
10293. 22// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
10294. 22// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
10295. 22// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
10296. 22// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
10297. 22
10298. 22 warn("unexpected_a", thing);
10299. 22 break;
10300. 41085 }
10301. 41085 if (
10302. 29578 thing.id !== "(" &&
10303. 29578 thing.id !== "&&" &&
10304. 28046 thing.id !== "||" &&
10305. 26997 thing.id !== "=" &&
10306. 22436
10307. 22436// PR-514 - Bugfix - Skip the 'of' or 'in' node of a for-loop-head, so
10308. 22436// 'for (aa of bb < cc)' does not warn on its own 'of', like the const form.
10309. 22436
10310. 22436 thing.for_init !== true &&
10311. 22424 Array.isArray(thing.expression) &&
10312. 9435 thing.expression.length === 2 &&
10313. 9427 (
10314. 9427 relationop[thing.expression[0].id] === true ||
10315. 9427 relationop[thing.expression[1].id] === true
10316. 9427 )
10317. 1 ) {
10318. 1
10319. 1// test_cause:
10320. 1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
10321. 1
10322. 1 warn("unexpected_a", thing);
10323. 1 }
10324. 41085 }
10325. 861
10326. 35748 function pre_b_binary(thing) {
10327. 35748 let left;
10328. 35748 let right;
10329. 35748 let value;
10330. 4312 if (relationop[thing.id] === true) {
10331. 4312 left = thing.expression[0];
10332. 4312 right = thing.expression[1];
10333. 4312 if (left.id === "NaN" || right.id === "NaN") {
10334. 4312
10335. 4312// test_cause:
10336. 4312// ["NaN===NaN", "pre_b_binary", "number_isNaN", "===", 4]
10337. 4312
10338. 4312 warn("number_isNaN", thing);
10339. 4312 } else if (left.id === "typeof") {
10340. 4312 if (right.id !== "(string)") {
10341. 4312 if (right.id !== "typeof") {
10342. 4312
10343. 4312// test_cause:
10344. 4312// ["typeof 0===0", "pre_b_binary", "expected_string_a", "0", 12]
10345. 4312// ["typeof 0===0;", "pre_b_binary", "expected_string_a", "0", 12]
10346. 4312
10347. 4312 warn("expected_string_a", right);
10348. 4312 }
10349. 4312 } else {
10350. 4312 value = right.value;
10351. 4312 if (value === "null" || value === "undefined") {
10352. 4312
10353. 4312// test_cause:
10354. 4312// ["
10355. 4312// typeof aa==="undefined"
10356. 4312// ", "pre_b_binary", "unexpected_typeof_a", "undefined", 13]
10357. 4312
10358. 4312 warn("unexpected_typeof_a", right, value);
10359. 4312 } else if (
10360. 4312 value !== "bigint" &&
10361. 4312 value !== "boolean" &&
10362. 4312 value !== "function" &&
10363. 4312 value !== "number" &&
10364. 4312 value !== "object" &&
10365. 4312 value !== "string" &&
10366. 4312 value !== "symbol"
10367. 4312 ) {
10368. 4312
10369. 4312// test_cause:
10370. 4312// ["typeof 0===\"aa\"", "pre_b_binary", "expected_type_string_a", "aa", 12]
10371. 4312
10372. 4312 warn("expected_type_string_a", right, value);
10373. 4312 }
10374. 4312 }
10375. 4312 }
10376. 4312 }
10377. 35748 }
10378. 861
10379. 1 function pre_b_eqeq(thing) {
10380. 1
10381. 1// test_cause:
10382. 1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
10383. 1
10384. 1 warn("expected_a_b", thing, "===", "==");
10385. 1 }
10386. 861
10387. 6 function pre_b_in(thing) {
10388. 1 if (!thing.for_init) {
10389. 1
10390. 1// test_cause:
10391. 1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
10392. 1
10393. 1 warn("infix_in", thing);
10394. 1 }
10395. 6 }
10396. 861
10397. 1 function pre_b_instanceof(thing) {
10398. 1
10399. 1// test_cause:
10400. 1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
10401. 1
10402. 1 warn("unexpected_a", thing);
10403. 1 }
10404. 861
10405. 1 function pre_b_noteq(thing) {
10406. 1
10407. 1// test_cause:
10408. 1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
10409. 1
10410. 1 warn("expected_a_b", thing, "!==", "!=");
10411. 1 }
10412. 861
10413. 8 function pre_b_of(thing) {
10414. 1 if (!thing.for_init) {
10415. 1
10416. 1// test_cause:
10417. 1// ["0 of 0", "pre_b_of", "unexpected_a", "of", 3]
10418. 1
10419. 1 warn("unexpected_a", thing);
10420. 1 }
10421. 8 }
10422. 861
10423. 1049 function pre_b_or(thing) {
10424. 2098 thing.expression.forEach(function (thang) {
10425. 182 if (thang.id === "&&" && !thang.wrapped) {
10426. 1
10427. 1// test_cause:
10428. 1// ["0&&0||0", "pre_b_or", "wrap_subexpression_a_b", "||", 2]
10429. 1
10430. 1 warn("wrap_subexpression_a_b", thang, "&&", "||");
10431. 1 }
10432. 2098 });
10433. 1049 }
10434. 861
10435. 90 function pre_s_for(thing) {
10436. 90
10437. 90// PR-504 - Add hidden scope_block for:
10438. 90// - for-variable
10439. 90
10440. 90 scope_block = scope_block_push(thing, false);
10441. 18 if (thing.for_semicolon) {
10442. 18 walk_statement(thing.for_semicolon[0]);
10443. 18 walk_expression(thing.for_semicolon[1]);
10444. 18 }
10445. 72 if (thing.for_of) {
10446. 72 switch (thing.for_of.id) {
10447. 72 case "const":
10448. 72 case "let":
10449. 72 case "var":
10450. 72
10451. 72// PR-514 - Bugfix - Walk the iterable of destructured 'for (const [aa] of bb)',
10452. 72// which <stmt_var> keeps in <expression>, and <post_s_var> does not walk. Walk
10453. 72// it before <post_s_var> marks the names alive, to catch temporal_dead_zone_a.
10454. 72
10455. 72 walk_expression(thing.for_of.expression);
10456. 72 post_s_var(thing.for_of);
10457. 72 break;
10458. 72 default:
10459. 72 walk_expression(thing.for_of);
10460. 72 }
10461. 72 }
10462. 90 }
10463. 861
10464. 2351 function pre_s_function(thing) {
10465. 2351
10466. 2351// test_cause:
10467. 2351// ["()=>0", "pre_s_function", "(live)", "", 0]
10468. 2351// ["(function (){}())", "pre_s_function", "(live)", "", 0]
10469. 2351// ["function aa(){}", "pre_s_function", "(live)", "", 0]
10470. 2351
10471. 1228 if (thing.arity === "statement" && !scope_block.function_body) {
10472. 2
10473. 2// test_cause:
10474. 2// ["if(0){function aa(){}}", "pre_s_function", "unexpected_a", "function", 7]
10475. 2
10476. 2 warn("unexpected_a", thing);
10477. 2 }
10478. 2351
10479. 2351// PR-504 - Add hidden scope_block for:
10480. 2351// - function-parameter
10481. 2351
10482. 2351 scope_block = scope_block_push(thing.scope_name, false);
10483. 2351 scope_block = scope_block_push(thing, false);
10484. 2351 scope_function = scope_function_push(thing, false);
10485. 12 if (thing.extra === "get") {
10486. 12 if (thing.parameter_count !== 0) {
10487. 12
10488. 12// test_cause:
10489. 12// ["
10490. 12// /*jslint getset*/
10491. 12// aa={get aa(aa){}}
10492. 12// ", "pre_s_function", "bad_get", "function", 9]
10493. 12
10494. 12 warn("bad_get", thing);
10495. 12 }
10496. 2339 } else if (thing.extra === "set") {
10497. 2339
10498. 2339// PR-514 - Bugfix - A setter's one parameter cannot be a rest-parameter, a
10499. 2339// SyntaxError. Read <parameter_rest>, since <name_list> flattens destructuring
10500. 2339// and cannot tell a rest-parameter from a valid rest-element inside one.
10501. 2339
10502. 2339 if (thing.parameter_count !== 1 || thing.parameter_rest) {
10503. 2339
10504. 2339// test_cause:
10505. 2339// ["
10506. 2339// /*jslint getset*/
10507. 2339// aa={set aa(){}}
10508. 2339// ", "pre_s_function", "bad_set", "function", 9]
10509. 2339// ["
10510. 2339// /*jslint getset*/
10511. 2339// aa={set aa(...aa){}}
10512. 2339// ", "pre_s_function", "bad_set", "function", 9]
10513. 2339
10514. 2339 warn("bad_set", thing);
10515. 2339 }
10516. 2339 }
10517. 2351
10518. 2351// 4.par.2 - Mark 'alive', the function-parameter, after destructuring.
10519. 2351
10520. 2351 post_s_var(thing);
10521. 2351 }
10522. 861
10523. 32425 function pre_v_var(thing) {
10524. 32425 const the_variable = name_lookup(thing);
10525. 32299 if (the_variable !== undefined) {
10526. 32299 thing.variable = the_variable;
10527. 32299 the_variable.used = true;
10528. 32299 }
10529. 32425 }
10530. 861
10531. 183092 function walk_expression(thing) {
10532. 117173 if (thing) {
10533. 117173 if (Array.isArray(thing)) {
10534. 117173
10535. 117173// test_cause:
10536. 117173// ["(function(){}())", "walk_expression", "(live)", "", 0]
10537. 117173// ["0&&0", "walk_expression", "(live)", "", 0]
10538. 117173
10539. 117173 thing.forEach(walk_expression);
10540. 117173 } else {
10541. 117173 preamble(thing);
10542. 117173 walk_expression(thing.expression);
10543. 117173
10544. 117173// PR-414 - Bugfix - Fix fart-body not being walked.
10545. 117173
10546. 117173 if (thing.id === "function" || thing.id === "=>") {
10547. 117173
10548. 117173// test_cause:
10549. 117173// ["aa=()=>0", "walk_expression", "(live)", "", 0]
10550. 117173// ["aa=function(){}", "walk_expression", "(live)", "", 0]
10551. 117173
10552. 117173// Recurse walk_statement.
10553. 117173
10554. 117173 walk_statement(thing.block);
10555. 117173 }
10556. 117173 if (
10557. 117173 thing.arity === "preassign" || thing.arity === "postassign"
10558. 117173 ) {
10559. 117173
10560. 117173// test_cause:
10561. 117173// ["if(++aa){}", "walk_expression", "unexpected_a", "++", 4]
10562. 117173// ["if(--aa){}", "walk_expression", "unexpected_a", "--", 4]
10563. 117173// ["if(aa++){}", "walk_expression", "unexpected_a", "++", 6]
10564. 117173// ["if(aa--){}", "walk_expression", "unexpected_a", "--", 6]
10565. 117173
10566. 117173 warn("unexpected_a", thing);
10567. 117173 } else if (
10568. 117173 thing.arity === "statement" ||
10569. 117173 thing.arity === "assignment"
10570. 117173 ) {
10571. 117173
10572. 117173// test_cause:
10573. 117173// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
10574. 117173
10575. 117173 warn("unexpected_statement_a", thing);
10576. 117173 }
10577. 117173
10578. 117173// test_cause:
10579. 117173// ["aa=0", "walk_expression", "(live)", "", 0]
10580. 117173
10581. 117173 postamble(thing);
10582. 117173 }
10583. 117173 }
10584. 183092 }
10585. 861
10586. 88137 function walk_statement(thing) {
10587. 48175 if (!thing) {
10588. 48175 return;
10589. 48175 }
10590. 39962 if (Array.isArray(thing)) {
10591. 9060
10592. 9060// test_cause:
10593. 9060// ["+[]", "walk_statement", "(live)", "", 0]
10594. 9060
10595. 9060// Recurse walk_statement.
10596. 9060
10597. 9060 thing.forEach(walk_statement);
10598. 9060 return;
10599. 30902 }
10600. 30902
10601. 30902// PR-504 - Add hidden scope_block for:
10602. 30902// - label-name
10603. 30902
10604. 30902 if (thing.label_name) {
10605. 18 scope_block = scope_block_push(thing.label_name, false);
10606. 30902 }
10607. 30902 if (thing.scope_block) {
10608. 6456 scope_block = scope_block_push(thing, false);
10609. 30902 }
10610. 30902 preamble(thing);
10611. 30902 walk_expression(thing.expression);
10612. 30902 if (thing.arity === "binary") {
10613. 5628 if (thing.id !== "(") {
10614. 5628
10615. 5628// test_cause:
10616. 5628// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
10617. 5628
10618. 5628 warn("unexpected_expression_a", thing);
10619. 5628 }
10620. 25274 } else if (
10621. 25274 thing.arity !== "statement" &&
10622. 25274 thing.arity !== "assignment" &&
10623. 25274 thing.id !== "import"
10624. 25274 ) {
10625. 25274
10626. 25274// test_cause:
10627. 25274// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
10628. 25274// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
10629. 25274// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
10630. 25274// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
10631. 25274// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
10632. 25274
10633. 25274 warn("unexpected_expression_a", thing);
10634. 30902 }
10635. 30902
10636. 30902// Recurse walk_statement.
10637. 30902
10638. 30902 walk_statement(thing.block);
10639. 30902 walk_statement(thing.else);
10640. 30902 postamble(thing);
10641. 30902 if (thing.scope_block) {
10642. 6456 scope_block = scope_block_pop();
10643. 30902 }
10644. 30902 if (thing.label_name) {
10645. 18 scope_block = scope_block_pop();
10646. 18 }
10647. 88137 }
10648. 861
10649. 861 postaction = action(posts);
10650. 861 postamble = amble(posts);
10651. 861 preaction = action(pres);
10652. 861 preamble = amble(pres);
10653. 861 postaction("assignment", "(all)", post_a_assignment);
10654. 861 postaction("assignment", "+=", post_a_pluseq);
10655. 861 postaction("binary", "&&", post_b_and);
10656. 861 postaction("binary", "(", post_b_lparen);
10657. 861 postaction("binary", "(all)", post_b_binary);
10658. 861 postaction("binary", "=>", post_s_function);
10659. 861 postaction("binary", "[", post_b_lbracket);
10660. 861 postaction("binary", "||", post_b_or);
10661. 861 postaction("postassign", "(all)", post_p_update);
10662. 861 postaction("preassign", "(all)", post_p_update);
10663. 861 postaction("statement", "const", post_s_var);
10664. 861 postaction("statement", "export", post_s_export_toplevel);
10665. 861 postaction("statement", "for", post_s_for);
10666. 861 postaction("statement", "function", post_s_function);
10667. 861 postaction("statement", "import", post_s_import);
10668. 861 postaction("statement", "let", post_s_var);
10669. 861 postaction("statement", "try", post_s_try);
10670. 861 postaction("statement", "var", post_s_var);
10671. 861 postaction("ternary", "(all)", post_t_ternary);
10672. 861 postaction("unary", "(all)", post_u_unary);
10673. 861 postaction("unary", "+", post_u_plus);
10674. 861 postaction("unary", "function", post_s_function);
10675. 861 preaction("assignment", "(all)", pre_a_bitwise);
10676. 861 preaction("binary", "!=", pre_b_noteq);
10677. 861 preaction("binary", "(all)", pre_a_bitwise);
10678. 861 preaction("binary", "(all)", pre_b_binary);
10679. 861 preaction("binary", "==", pre_b_eqeq);
10680. 861 preaction("binary", "=>", pre_s_function);
10681. 861 preaction("binary", "in", pre_b_in);
10682. 861 preaction("binary", "instanceof", pre_b_instanceof);
10683. 861 preaction("binary", "of", pre_b_of);
10684. 861 preaction("binary", "||", pre_b_or);
10685. 861 preaction("statement", "for", pre_s_for);
10686. 861 preaction("statement", "function", pre_s_function);
10687. 861 preaction("unary", "function", pre_s_function);
10688. 861 preaction("unary", "~", pre_a_bitwise);
10689. 861 preaction("variable", "(all)", pre_v_var);
10690. 861
10691. 861// Init block_stack, function_stack.
10692. 861
10693. 861 block_stack.length = 0;
10694. 861 function_stack.length = 0;
10695. 861 scope_block = scope_block_push(token_global, false);
10696. 861 scope_function = scope_function_push(token_global, false);
10697. 861
10698. 861// Walk the token_tree.
10699. 861
10700. 861 walk_statement(state.token_tree);
10701. 861
10702. 861// Cleanup block_stack, function_stack.
10703. 861
10704. 861 jslint_assert(
10705. 861 block_stack.length === 1,
10706. 861 `block_stack.length=${block_stack.length}.`
10707. 861 );
10708. 861 jslint_assert(
10709. 861 function_stack.length === 1,
10710. 861 `function_stack.length=${function_stack.length}.`
10711. 861 );
10712. 861 block_stack.length = 0;
10713. 861 function_stack.length = 0;
10714. 861}
10715. 23
10716. 306function jslint_phase5_whitage(state) {
10717. 306
10718. 306// PHASE 5. Check whitespace between tokens in <token_list>.
10719. 306
10720. 306 const {
10721. 306 artifact,
10722. 306 block_list,
10723. 306 option_dict,
10724. 306 token_global,
10725. 306 token_list,
10726. 306 warn
10727. 306 } = state;
10728. 306 const indent_stack = [];
10729. 306 let closer = "(end)";
10730. 306 let dot_depth = 0;
10731. 306 let indentage;
10732. 306 let left = token_global;
10733. 306 let margin = 0;
10734. 306 let mode_indent = (
10735. 306
10736. 306// PR-330 - Allow 2-space indent.
10737. 306
10738. 306 option_dict.indent2
10739. 4 ? 2
10740. 302 : 4
10741. 306 );
10742. 306 let nr_comments_skipped = 0;
10743. 306 let open = true;
10744. 306 let opening = true;
10745. 306 let right;
10746. 306
10747. 306// This is the set of infix operators that require a space on each side.
10748. 306
10749. 306 let spaceop = object_assign_from_list(empty(), [
10750. 306 "!=", "!==",
10751. 306 "%", "%=",
10752. 306 "&", "&&", "&&=", "&=",
10753. 306 "*", "**", "**=", "*=",
10754. 306 "+=",
10755. 306 "-=",
10756. 306 "/", "/=",
10757. 306 "<", "<<", "<<=", "<=",
10758. 306 "=", "==", "===", "=>",
10759. 306 ">", ">=", ">>", ">>=", ">>>", ">>>=",
10760. 306 "??", "??=",
10761. 306 "^", "^=",
10762. 306 "|", "|=", "||", "||="
10763. 306 ], true);
10764. 306
10765. 37811 function at_margin(fit) {
10766. 37811 const at = margin + fit;
10767. 45 if (right.from !== at) {
10768. 45 return expected_at(at);
10769. 45 }
10770. 37811 }
10771. 306
10772. 8880 function delve(the_block) {
10773. 7517 Object.keys(the_block.context).forEach(function (id) {
10774. 7517 const name = the_block.context[id];
10775. 7483 if (id !== "ignore" && name.scope_declared === the_block) {
10776. 7483
10777. 7483// test_cause:
10778. 7483// ["function aa(aa) {return aa;}", "delve", "(live)", "", 0]
10779. 7483
10780. 7483 if (!name.used) {
10781. 7483
10782. 7483// test_cause:
10783. 7483// ["function aa(){bb:while(aa){aa();}}", "delve", "unused_a", "bb", 15]
10784. 7483// ["function aa(bb){return;}", "delve", "unused_a", "bb", 13]
10785. 7483// ["let aa;", "delve", "unused_a", "aa", 5]
10786. 7483// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
10787. 7483
10788. 7483 warn("unused_a", name);
10789. 7483 } else if (!name.assigned) {
10790. 7483
10791. 7483// test_cause:
10792. 7483// ["let aa;aa&&=0;aa();", "delve", "unassigned_var_a", "aa", 5]
10793. 7483// ["let aa;aa();", "delve", "unassigned_var_a", "aa", 5]
10794. 7483
10795. 7483 warn("unassigned_var_a", name);
10796. 7483 }
10797. 7483 }
10798. 7517 });
10799. 8880 }
10800. 306
10801. 54 function expected_at(at) {
10802. 54
10803. 54// PR-510 - deadcode-confirmed - <right> is always assigned first. The whitage
10804. 54// walk starts in one place, the <token_list>.forEach whose first statement is
10805. 54// <right> = <the_token>, and every route to <expected_at> runs inside it.
10806. 54//
10807. 54// deadcode-revive - Call <expected_at>, or anything that reaches it, from
10808. 54// outside that forEach. A pre-pass or post-pass indent check would do it, and
10809. 54// <right> would hold whatever the last token left there, or undefined.
10810. 54//
10811. 54// if (right === undefined) {
10812. 54// right = token_nxt;
10813. 54// }
10814. 54
10815. 54 jslint_assert(
10816. 54 !(right === undefined),
10817. 54 `Expected !(right === undefined).`
10818. 54 );
10819. 54
10820. 54// test_cause:
10821. 54// ["
10822. 54// let aa;
10823. 54// aa();
10824. 54// ", "expected_at", "expected_a_at_b_c", "aa", 3]
10825. 54
10826. 54 warn(
10827. 54 "expected_a_at_b_c",
10828. 54 right,
10829. 54 artifact(right),
10830. 54 at,
10831. 54 right.from
10832. 54 );
10833. 54 }
10834. 306
10835. 107590 function no_space_only() {
10836. 107590 if (
10837. 107588 left.id !== "(global)" &&
10838. 107588 left.nr + 1 === right.nr &&
10839. 107587 (
10840. 107587 left.line !== right.line ||
10841. 107587 left.thru !== right.from
10842. 107587 )
10843. 27 ) {
10844. 27
10845. 27// test_cause:
10846. 27// ["String( 0);", "no_space_only", "unexpected_space_a_b", "0", 9]
10847. 27
10848. 27 warn(
10849. 27 "unexpected_space_a_b",
10850. 27 right,
10851. 27 artifact(left),
10852. 27 artifact(right)
10853. 27 );
10854. 27 }
10855. 107590 }
10856. 306
10857. 50566 function one_space() {
10858. 48427 if (left.line === right.line || !open) {
10859. 48427 if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
10860. 48427
10861. 48427// test_cause:
10862. 48427// ["String(0,0);", "one_space", "expected_space_a_b", "0", 10]
10863. 48427
10864. 48427 warn(
10865. 48427 "expected_space_a_b",
10866. 48427 right,
10867. 48427 artifact(left),
10868. 48427 artifact(right)
10869. 48427 );
10870. 48427 }
10871. 48427 return;
10872. 48427 }
10873. 2139 if (right.from !== margin) {
10874. 6
10875. 6// test_cause:
10876. 6// ["
10877. 6// let aa={
10878. 6// aa:
10879. 6// 0};
10880. 6// ", "one_space", "(live)", "", 0]
10881. 6
10882. 6 expected_at(margin);
10883. 6 }
10884. 50566 }
10885. 306
10886. 10217 function one_space_only() {
10887. 10216 if (left.line !== right.line || left.thru + 1 !== right.from) {
10888. 28 warn("expected_space_a_b", right, artifact(left), artifact(right));
10889. 28 }
10890. 10217 }
10891. 306
10892. 29637 function whitage_closer() {
10893. 29637
10894. 29637// test_cause:
10895. 29637// ["String(0);", "whitage_closer", "(live)", "", 0]
10896. 29637// ["let aa=[0];", "whitage_closer", "(live)", "", 0]
10897. 29637// ["let aa=`${0}`;", "whitage_closer", "(live)", "", 0]
10898. 29637// ["let aa={String};", "whitage_closer", "(live)", "", 0]
10899. 29637
10900. 29637// If right is a closer, then pop the previous state.
10901. 29637
10902. 29637 indentage = indent_stack.pop();
10903. 29637 closer = indentage.closer;
10904. 29637 margin = indentage.margin;
10905. 29637 open = indentage.open;
10906. 29637 opening = indentage.opening;
10907. 29637
10908. 29637// Commit 3903449a - Cleanup indent for multiline-method-chaining.
10909. 29637
10910. 108 if (dot_depth > 1) {
10911. 108 dot_depth -= 1;
10912. 108 }
10913. 9884 if (opening && right.id !== ";") {
10914. 9884
10915. 9884// test_cause:
10916. 9884// ["
10917. 9884// String(
10918. 9884// 0);
10919. 9884// ", "whitage_closer", "(live)", "", 0]
10920. 9884
10921. 9884 at_margin(
10922. 9884 indentage.dot_depth === 1
10923. 9884
10924. 9884// Commit 3903449a - Cleanup indent for multiline-method-chaining.
10925. 9884
10926. 9884 ? mode_indent
10927. 9884 : 0
10928. 9884 );
10929. 9884 return;
10930. 19753 }
10931. 19753
10932. 19753// test_cause:
10933. 19753// ["String( );", "whitage_closer", "(live)", "", 0]
10934. 19753
10935. 19753 no_space_only();
10936. 19753 }
10937. 306
10938. 213888 function whitage_default() {
10939. 213888
10940. 213888// The open and close pairs.
10941. 213888
10942. 213888 switch (left.id) {
10943. 824 case "${":
10944. 19875 case "(":
10945. 22331 case "[":
10946. 29637 case "{":
10947. 29637 whitage_opener();
10948. 29637 return;
10949. 184251 }
10950. 184251 if (right.id === closer) {
10951. 26993 whitage_closer();
10952. 26993 return;
10953. 157258 }
10954. 157258
10955. 157258// Left is not an opener, and right is not a closer.
10956. 157258// The nature of left and right will determine the space between them.
10957. 157258
10958. 157258 if (right.statement === true) {
10959. 15792 if (left.id === "else") {
10960. 15792
10961. 15792// test_cause:
10962. 15792// ["
10963. 15792// let aa=0;if(aa){aa();}else if(aa){aa();}
10964. 15792// ", "whitage_default", "(live)", "", 0]
10965. 15792
10966. 15792 one_space_only();
10967. 15792 return;
10968. 15792 }
10969. 15792
10970. 15792// test_cause:
10971. 15792// ["let aa;", "whitage_default", "(live)", "", 0]
10972. 15792
10973. 15792 at_margin(0);
10974. 15792 open = false;
10975. 15792 return;
10976. 141466 }
10977. 141466
10978. 141466// If left is ',' or ';' or right is a statement, then if open,
10979. 141466// right must go at the margin, or if closed, a space between.
10980. 141466
10981. 141466 if (right.switch) {
10982. 1780 at_margin(-mode_indent);
10983. 1780 return;
10984. 139686 }
10985. 139686 if (right.role === "label") {
10986. 3 if (right.from !== 0) {
10987. 3
10988. 3// test_cause:
10989. 3// ["
10990. 3// function aa(){
10991. 3// aa();bb:while(aa){aa();}}
10992. 3// ", "whitage_default", "(live)", "", 0]
10993. 3
10994. 3 expected_at(0);
10995. 3 }
10996. 3 return;
10997. 139683 }
10998. 139683 if (left.id === ",") {
10999. 10664 if (
11000. 10664 !open ||
11001. 10664 (closer === "]" && left.line === right.line)
11002. 10664 ) {
11003. 10664
11004. 10664// test_cause:
11005. 10664// ["String(0,0);", "whitage_default", "(live)", "", 0]
11006. 10664
11007. 10664 one_space();
11008. 10664 return;
11009. 10664 }
11010. 10664
11011. 10664// test_cause:
11012. 10664// ["
11013. 10664// String(
11014. 10664// 0,0);
11015. 10664// ", "whitage_default", "(live)", "", 0]
11016. 10664
11017. 10664 return;
11018. 129019 }
11019. 129019
11020. 129019// If right is a ternary operator, line it up on the margin.
11021. 129019
11022. 129019 if (right.arity === "ternary") {
11023. 622 if (open) {
11024. 622
11025. 622// test_cause:
11026. 622// ["
11027. 622// String(
11028. 622// String?0:1);
11029. 622// ", "whitage_default", "(live)", "", 0]
11030. 622
11031. 622 at_margin(0);
11032. 622 return;
11033. 622 }
11034. 622
11035. 622// test_cause:
11036. 622// ["String(String?0:1);", "whitage_default", "use_open", "?", 14]
11037. 622
11038. 622 warn("use_open", right);
11039. 622 return;
11040. 128397 }
11041. 128397 if (
11042. 128397 left.id === "." ||
11043. 115903 left.id === "?." ||
11044. 115837 left.id === "..." ||
11045. 115718 right.id === "," ||
11046. 105055 right.id === ";" ||
11047. 88262 right.id === ":" ||
11048. 84810 (
11049. 84810 right.arity === "binary" &&
11050. 84810 (right.id === "(" || right.id === "[")
11051. 84810 ) ||
11052. 72217 (
11053. 72217 right.arity === "function" &&
11054. 72217 left.id !== "function"
11055. 72217 )
11056. 57363 ) {
11057. 57363
11058. 57363// test_cause:
11059. 57363// ["String(...String);", "whitage_default", "(live)", "", 0]
11060. 57363
11061. 57363 no_space_only();
11062. 57363 return;
11063. 71034 }
11064. 71034 if (right.id === "." || right.id === "?.") {
11065. 12560 if (left.line === right.line) {
11066. 12560
11067. 12560// test_cause:
11068. 12560// ["String?.aa();", "whitage_default", "(live)", "", 0]
11069. 12560
11070. 12560 no_space_only();
11071. 12560 return;
11072. 12560 }
11073. 12560 at_margin(mode_indent);
11074. 12560 return;
11075. 58474 }
11076. 58474 if (left.id === ";") {
11077. 10 if (open) {
11078. 10
11079. 10// test_cause:
11080. 10// ["
11081. 10// function aa() {
11082. 10// for (
11083. 10// let bb = 0;
11084. 10// bb < 0;
11085. 10// bb += 1
11086. 10// ) {
11087. 10// aa(bb);
11088. 10// }
11089. 10// }
11090. 10// ", "whitage_default", "(live)", "", 0]
11091. 10
11092. 10 at_margin(0);
11093. 10 }
11094. 10 return;
11095. 58464 }
11096. 58464 if (left.getset === true) {
11097. 9
11098. 9// PR-514 - Bugfix - On one line, <one_space> still lets a comment sit between
11099. 9// the 'get' or 'set' and its name.
11100. 9
11101. 9 if (left.line === right.line) {
11102. 9
11103. 9// test_cause:
11104. 9// ["
11105. 9// /*jslint getset*/
11106. 9// String({get aa() {
11107. 9// return;
11108. 9// }});
11109. 9// ", "whitage_default", "(live)", "", 0]
11110. 9
11111. 9 one_space();
11112. 9 } else {
11113. 9
11114. 9// test_cause:
11115. 9// ["
11116. 9// /*jslint getset*/
11117. 9// String({
11118. 9// get
11119. 9// aa() {
11120. 9// return;
11121. 9// }
11122. 9// });
11123. 9// ", "one_space_only", "expected_space_a_b", "aa", 5]
11124. 9
11125. 9 one_space_only();
11126. 9 }
11127. 9 return;
11128. 58455 }
11129. 58455 if (
11130. 58455 left.arity === "ternary" ||
11131. 57833 left.id === "case" ||
11132. 55916 left.id === "catch" ||
11133. 55849 left.id === "else" ||
11134. 55629 left.id === "finally" ||
11135. 55580 left.id === "while" ||
11136. 55369 left.id === "await" ||
11137. 55023 right.id === "catch" ||
11138. 54967 right.id === "else" ||
11139. 54539 right.id === "finally" ||
11140. 54531 (right.id === "while" && !right.statement) ||
11141. 54529 (left.id === ")" && right.id === "{")
11142. 9998 ) {
11143. 9998
11144. 9998// test_cause:
11145. 9998// ["if(String()){String();}", "whitage_default", "(live)", "", 0]
11146. 9998
11147. 9998 one_space_only();
11148. 9998 return;
11149. 48457 }
11150. 48457 if (
11151. 48457
11152. 48457// There is a space between left and right.
11153. 48457
11154. 48457 spaceop[left.id] === true ||
11155. 35055 spaceop[right.id] === true ||
11156. 21715 (
11157. 21715 left.arity === "binary" &&
11158. 21715 (left.id === "+" || left.id === "-")
11159. 21715 ) ||
11160. 19169 (
11161. 19169 right.arity === "binary" &&
11162. 19169 (right.id === "+" || right.id === "-")
11163. 19169 ) ||
11164. 16623 left.id === "function" ||
11165. 14475 left.id === ":" ||
11166. 13100 left.id === "async" ||
11167. 12960 (
11168. 12960 (
11169. 12960 left.identifier ||
11170. 12960 left.id === "(string)" ||
11171. 12960 left.id === "(number)"
11172. 12960 ) &&
11173. 12960 (
11174. 12960 right.identifier ||
11175. 12960 right.id === "(string)" ||
11176. 12960 right.id === "(number)"
11177. 12960 )
11178. 12960 ) ||
11179. 8377 (left.arity === "statement" && right.id !== ";")
11180. 44165 ) {
11181. 44165
11182. 44165// test_cause:
11183. 44165// ["let aa=0;", "whitage_default", "(live)", "", 0]
11184. 44165
11185. 44165 one_space();
11186. 44165 return;
11187. 44165 }
11188. 4292 if (left.arity === "unary" && left.id !== "`") {
11189. 882 no_space_only();
11190. 882 return;
11191. 882 }
11192. 213888 }
11193. 306
11194. 29637 function whitage_opener() {
11195. 29637
11196. 29637// test_cause:
11197. 29637// ["String();", "whitage_opener", "(live)", "", 0]
11198. 29637// ["String(0);", "whitage_opener", "(live)", "", 0]
11199. 29637// ["let aa=[0];", "whitage_opener", "(live)", "", 0]
11200. 29637// ["let aa=[];", "whitage_opener", "(live)", "", 0]
11201. 29637// ["let aa=`${0}`;", "whitage_opener", "(live)", "", 0]
11202. 29637// ["let aa={String};", "whitage_opener", "(live)", "", 0]
11203. 29637// ["let aa={};", "whitage_opener", "(live)", "", 0]
11204. 29637
11205. 29586 opening = left.open || (left.line !== right.line);
11206. 29637 indentage = {
11207. 29637 closer,
11208. 29637 dot_depth,
11209. 29637 margin,
11210. 29637 open,
11211. 29637 opening
11212. 29637 };
11213. 29637 indent_stack.push(indentage);
11214. 29637
11215. 29637// Commit 3903449a - Cleanup indent for multiline-method-chaining.
11216. 29637
11217. 138 if (dot_depth >= 1) {
11218. 138 dot_depth += 1;
11219. 138 if (dot_depth === 2) {
11220. 138 margin += mode_indent;
11221. 138 }
11222. 138 }
11223. 29637 switch (left.id) {
11224. 824 case "${":
11225. 824 closer = "}";
11226. 824 break;
11227. 19051 case "(":
11228. 19051 closer = ")";
11229. 19051 break;
11230. 2456 case "[":
11231. 2456 closer = "]";
11232. 2456 break;
11233. 7306 case "{":
11234. 7306 closer = "}";
11235. 7306 break;
11236. 29637 }
11237. 2644 if (right.id === closer) {
11238. 2644 whitage_closer();
11239. 2644 return;
11240. 26993 }
11241. 26993 if (opening) {
11242. 9878
11243. 9878// test_cause:
11244. 9878// ["
11245. 9878// String(
11246. 9878// 0);
11247. 9878// ", "whitage_opener", "(live)", "", 0]
11248. 9878
11249. 9878 open = true;
11250. 9878 margin += mode_indent;
11251. 9878 if (right.role === "label") {
11252. 9878 if (right.from !== 0) {
11253. 9878
11254. 9878// test_cause:
11255. 9878// ["
11256. 9878// function aa(){
11257. 9878// bb:while(aa){aa();}}
11258. 9878// ", "whitage_opener", "(live)", "", 0]
11259. 9878
11260. 9878 expected_at(0);
11261. 9878 }
11262. 9878 } else if (right.switch) {
11263. 9878 at_margin(-mode_indent);
11264. 9878 } else {
11265. 9878 at_margin(0);
11266. 9878 }
11267. 9878 return;
11268. 17115 }
11269. 17115 if (
11270. 17115 (right.statement || right.role === "label") &&
11271. 68 (!right.for_init || left.line !== right.line)
11272. 23 ) {
11273. 23
11274. 23// test_cause:
11275. 23// ["
11276. 23// function aa(){bb:while(aa){aa();}}
11277. 23// ", "whitage_opener", "expected_line_break_a_b", "aa", 28]
11278. 23
11279. 23 warn(
11280. 23 "expected_line_break_a_b",
11281. 23 right,
11282. 23 artifact(left),
11283. 23 artifact(right)
11284. 23 );
11285. 17115 }
11286. 17115 open = false;
11287. 17115
11288. 17115// test_cause:
11289. 17115// ["String(0);", "whitage_opener", "(live)", "", 0]
11290. 17115
11291. 17115 no_space_only();
11292. 17115 }
11293. 306
11294. 306// unassigned_or_unused();
11295. 306// Delve into the functions looking for variables that were not assigned or
11296. 306// used. If the file imports or exports, then its global object is also delved.
11297. 306
11298. 306// PR-502 - Tighten warning of unused variables to be always on.
11299. 306
11300. 306 block_list.forEach(delve);
11301. 3 if (option_dict.white) {
11302. 3 return;
11303. 303 }
11304. 303
11305. 303// whitage();
11306. 303// Go through the token list, looking at usage of whitespace.
11307. 303
11308. 229826 token_list.forEach(function (the_token) {
11309. 229826 right = the_token;
11310. 214198 if (right.id === "(comment)" || right.id === "(end)") {
11311. 15938 nr_comments_skipped += 1;
11312. 15938 return;
11313. 213888 }
11314. 213888
11315. 213888// If left is an opener and right is not the closer, then push the previous
11316. 213888// state. If the token following the opener is on the next line, then this is
11317. 213888// an open form. If the tokens are on the same line, then it is a closed form.
11318. 213888// Open form is more readable, with each item (statement, argument, parameter,
11319. 213888// etc) starting on its own line. Closed form is more compact. Statement blocks
11320. 213888// are always in open form.
11321. 213888
11322. 213888 whitage_default();
11323. 213888
11324. 213888// Commit 3903449a - Cleanup indent for multiline-method-chaining.
11325. 213888
11326. 213888 if (left.line !== right.line) {
11327. 44373
11328. 44373// PR-511 - Binary operators at end-of-line - A tagged-megastring is a binary
11329. 44373// '`' whose right side is the megastring. Moving its backtick up would put the
11330. 44373// line break INSIDE the megastring and change its value, so it is excluded.
11331. 44373
11332. 44373 if (
11333. 44373 option_dict.beta &&
11334. 44373 right.arity === "binary" &&
11335. 44373 right.id !== "." &&
11336. 44373 right.id !== "?." &&
11337. 44373 right.id !== "`"
11338. 44373 ) {
11339. 44373
11340. 44373// test_cause:
11341. 44373// ["
11342. 44373// let aa = [
11343. 44373// 0
11344. 44373// + 0
11345. 44373// ];
11346. 44373// ", "jslint_phase5_whitage", "expected_a_at_end", "+", 5]
11347. 44373
11348. 44373 warn(
11349. 44373 "expected_a_at_end",
11350. 44373 right,
11351. 44373 artifact(right),
11352. 44373 undefined,
11353. 44373 left.line,
11354. 44373 left.thru
11355. 44373 );
11356. 44373 }
11357. 44373 dot_depth = 0;
11358. 44373 switch (right.id) {
11359. 44373 case ".":
11360. 44373 case "?.":
11361. 44373 dot_depth = dot_depth || 1;
11362. 44373 break;
11363. 44373 }
11364. 213888 }
11365. 213888 nr_comments_skipped = 0;
11366. 213888 delete left.alive;
11367. 213888 delete left.assigned;
11368. 213888 delete left.open;
11369. 213888 delete left.used;
11370. 213888 left = right;
11371. 213888 });
11372. 303 jslint_assert(
11373. 303 indent_stack.length === 0,
11374. 303 `indent_stack.length=${indent_stack.length}.`
11375. 303 );
11376. 303}
11377. 23
11378. 23// PR-509 - Add function jslint_phase6_autofix.
11379. 23
11380. 67function jslint_phase6_autofix(state) {
11381. 67
11382. 67// PHASE 6. Autofix whitespace-warnings in <source>, and return the result.
11383. 67
11384. 62 const line_crlf = jslint_rgx_crlf.exec(state.source)?.[0] || "\n";
11385. 445 const line_list = state.line_list.map(function ({
11386. 445 line_source
11387. 445 }) {
11388. 445 return line_source;
11389. 445 });
11390. 67 const warning_list = state.warning_list;
11391. 25 if (warning_list.length === 0) {
11392. 25 return;
11393. 42 }
11394. 70 for (const {code} of warning_list) {
11395. 70 if (!jslint_autofix_warning_list.includes(code)) {
11396. 70 return;
11397. 70 }
11398. 70 }
11399. 35
11400. 35// Apply each fix RIGHT-TO-LEFT within a line, so an earlier fix cannot
11401. 35// invalidate a later fix's column, and BOTTOM-UP across lines, so every line
11402. 35// a splice shifts has already been visited.
11403. 35
11404. 36 warning_list.slice().sort(function (aa, bb) {
11405. 35 return bb.line - aa.line || bb.column - aa.column;
11406. 63 }).forEach(function ({
11407. 63 a,
11408. 63 b,
11409. 63 c,
11410. 63 code,
11411. 63 column,
11412. 63 d,
11413. 63 line
11414. 63 }) {
11415. 63 const ii = column - 1;
11416. 63 const line_source = line_list[line];
11417. 63 let indentage_at;
11418. 63 let jj = ii;
11419. 63 switch (code) {
11420. 35 case "expected_a_at_b_c":
11421. 35
11422. 35// expected_a_at_b_c is indentation. <at_margin>, <one_space> and the two label
11423. 35// callers of <expected_at> warn a token that belongs at a margin, and a label
11424. 35// can be mid-line, such as 'cc:' after 'bb();'. Unless a warning such as
11425. 35// weird_loop blocks autofix, that reaches here, so keep the mid-line branch.
11426. 35
11427. 35 indentage_at = line_source.length - line_source.trimStart().length;
11428. 35
11429. 35// A MID-LINE token cannot be re-indented, but it does not need to be skipped,
11430. 35// since all four callers warn a token that belongs AT a margin and therefore
11431. 35// on its OWN line. So split first and indent the remainder, which also lands a
11432. 35// trailing closer correctly, and is what line-breaks a single-line ternary.
11433. 35
11434. 35 if (ii !== indentage_at) {
11435. 35 line_list.splice(
11436. 35 line,
11437. 35 1,
11438. 35 line_source.slice(0, ii).replace((/ +$/), ""),
11439. 35 " ".repeat(b) + line_source.slice(ii)
11440. 35 );
11441. 35 return;
11442. 35 }
11443. 35 line_list[line] = " ".repeat(b) + line_source.trimStart();
11444. 35 return;
11445. 35 case "expected_a_at_end":
11446. 35
11447. 35// PR-511 - Move the line-leading operator to just after its left operand, which
11448. 35// ends at line <c>, column <d>, and before any trailing comment there.
11449. 35
11450. 35 line_list[c] = (
11451. 35 line_list[c].slice(0, d) + " " + a +
11452. 35 line_list[c].slice(d).replace((/ +$/), "")
11453. 35 );
11454. 35 line_list[line] = (
11455. 35 line_source.slice(0, ii) +
11456. 35 line_source.slice(ii + a.length).replace((/^ /), "")
11457. 35 );
11458. 35
11459. 35// An operator left ALONE on its line would leave a blank line behind.
11460. 35
11461. 35 if (line_list[line].trim() === "") {
11462. 35 line_list.splice(line, 1);
11463. 35 }
11464. 35 return;
11465. 35 case "expected_line_break_a_b":
11466. 35
11467. 35// Split the line at the token. The new line lands unindented and the
11468. 35// expected_a_at_b_c pass re-indents it on the NEXT recursion - that division
11469. 35// of labour is why autofix iterates rather than trying to be complete in one
11470. 35// pass.
11471. 35
11472. 35 line_list.splice(
11473. 35 line,
11474. 35 1,
11475. 35 line_source.slice(0, ii).replace((/ +$/), ""),
11476. 35 line_source.slice(ii)
11477. 35 );
11478. 35 return;
11479. 35 }
11480. 35
11481. 35// Walk back over the whitespace-run immediately before the token.
11482. 35
11483. 35 while (
11484. 38 jj > 0 &&
11485. 38 (line_source[jj - 1] === " " || line_source[jj - 1] === "\t")
11486. 35 ) {
11487. 35 jj -= 1;
11488. 35 }
11489. 35
11490. 35// A run reaching column 0 is INDENTATION or a line-join, not a gap between
11491. 35// two tokens on one line. Leave it to a future jslint_autofix_warning_list
11492. 35// entry.
11493. 35
11494. 35 if (jj === 0) {
11495. 35 return;
11496. 35 }
11497. 35 line_list[line] = (
11498. 35 line_source.slice(0, jj) +
11499. 35 (
11500. 35 code === "expected_space_a_b"
11501. 35 ? " "
11502. 35 : ""
11503. 63 ) +
11504. 63 line_source.slice(ii)
11505. 63 );
11506. 63 });
11507. 35
11508. 35// PR-511 - jslint-autofix - Append <line_crlf> to end of code, if missing.
11509. 35
11510. 35 if (line_list[line_list.length - 1] !== "") {
11511. 2 line_list.push("");
11512. 35 }
11513. 35 return line_list.slice(jslint_fudge).join(line_crlf);
11514. 35}
11515. 23
11516. 13function jslint_report({
11517. 13 autofix,
11518. 13 exports,
11519. 13 froms,
11520. 13 functions,
11521. 13 global,
11522. 13 json,
11523. 13 module,
11524. 13 property,
11525. 13 stop,
11526. 13 warnings
11527. 13}) {
11528. 13
11529. 13// This function will create human-readable, html-report
11530. 13// for warnings, properties, and functions from jslint-result-object.
11531. 13//
11532. 13// Example usage:
11533. 13// let result = jslint("console.log('hello world')");
11534. 13// let html = jslint_report(result);
11535. 13
11536. 4 const autofix_blocked = autofix && warnings.some(function ({
11537. 4 code
11538. 4 }) {
11539. 4 return !jslint_autofix_warning_list.includes(code);
11540. 4 });
11541. 13 let html = "";
11542. 13 let length_80 = 1111;
11543. 13
11544. 368 function address(line = 1, column = 1) {
11545. 368
11546. 368// This function will create HTML address element from <line> and <column>
11547. 368
11548. 368 return `<address>${Number(line)}: ${Number(column)}</address>`;
11549. 368
11550. 368 }
11551. 13
11552. 2516 function detail(title, list) {
11553. 2516 return (
11554. 2516 (Array.isArray(list) && list.length > 0)
11555. 859 ? (
11556. 859
11557. 859// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
11558. 859// <dt> and <dd> groups, <script>, <template> or <div> elements.
11559. 859
11560. 859 "<dl>" +
11561. 859 "<dt>" + htmlEscape(title) + "</dt>" +
11562. 859 "<dd>" + list.map(htmlEscape).join(", ") + "</dd>" +
11563. 859 "</dl>"
11564. 859 )
11565. 1657 : ""
11566. 2516 );
11567. 2516 }
11568. 13
11569. 13 html += String(`
11570. 13<style class="JSLINT_REPORT_STYLE">
11571. 13/* jslint utility2:true */
11572. 13/*csslint box-model: false, ids:false */
11573. 13/*csslint ignore:start*/
11574. 13@font-face {
11575. 13 font-display: swap;
11576. 13 font-family: "Daley";
11577. 13 src: url(
11578. 13"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
11579. 13AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
11580. 137NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
11581. 13KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
11582. 13llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
11583. 137sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
11584. 13X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
11585. 13fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
11586. 13/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
11587. 13gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
11588. 13l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
11589. 13r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
11590. 13J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
11591. 13weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
11592. 13/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
11593. 13EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
11594. 13DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
11595. 13qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
11596. 13r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
11597. 13Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
11598. 13jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
11599. 13cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
11600. 13c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
11601. 13Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
11602. 13fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
11603. 13OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
11604. 13Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
11605. 13mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
11606. 13UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
11607. 136cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
11608. 130EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
11609. 139KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
11610. 13ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
11611. 13bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
11612. 13ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
11613. 13lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
11614. 13mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
11615. 137HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
11616. 13prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
11617. 13OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
11618. 13pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
11619. 13H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
11620. 13knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
11621. 13aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
11622. 13ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
11623. 13JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
11624. 136iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
11625. 13FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
11626. 13nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
11627. 13edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
11628. 13T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
11629. 13OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
11630. 13nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
11631. 13c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
11632. 13ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
11633. 131JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
11634. 135o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
11635. 13b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
11636. 13/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
11637. 13DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
11638. 13tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
11639. 13C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
11640. 13wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
11641. 13vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
11642. 13vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
11643. 13tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
11644. 13/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
11645. 13wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
11646. 13qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
11647. 1329CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
11648. 13eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
11649. 13s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
11650. 13xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
11651. 13c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
11652. 130WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
11653. 13+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
11654. 13mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
11655. 13U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
11656. 13kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
11657. 13Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
11658. 13AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
11659. 13O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
11660. 13R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
11661. 13wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
11662. 133gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
11663. 13r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
11664. 13+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
11665. 13icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
11666. 137frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
11667. 13j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
11668. 13wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
11669. 13I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
11670. 13ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
11671. 133FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
11672. 13OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
11673. 13Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
11674. 13xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
11675. 13dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
11676. 13aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
11677. 13sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
11678. 133v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
11679. 139GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
11680. 1359iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
11681. 13O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
11682. 13Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
11683. 13aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
11684. 13nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
11685. 13CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
11686. 137ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
11687. 13PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
11688. 13X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
11689. 13dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
11690. 13ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
11691. 13gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
11692. 13ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
11693. 13nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
11694. 13+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
11695. 13lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
11696. 13OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
11697. 13v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
11698. 139uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
11699. 13b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
11700. 13RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
11701. 1359yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
11702. 138U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
11703. 1356J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
11704. 13K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
11705. 13+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
11706. 13La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
11707. 13K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
11708. 135Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
11709. 13WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
11710. 13+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
11711. 13SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
11712. 13pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
11713. 131R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
11714. 13/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
11715. 13 ) format("woff2");
11716. 13}
11717. 13.JSLINT_,
11718. 13.JSLINT_ address,
11719. 13.JSLINT_ button,
11720. 13.JSLINT_ cite,
11721. 13.JSLINT_ dd,
11722. 13.JSLINT_ dfn,
11723. 13.JSLINT_ dl,
11724. 13.JSLINT_ dt,
11725. 13.JSLINT_ fieldset,
11726. 13.JSLINT_ fieldset > div,
11727. 13.JSLINT_ input,
11728. 13.JSLINT_ label,
11729. 13.JSLINT_ legend,
11730. 13.JSLINT_ ol,
11731. 13.JSLINT_ samp,
11732. 13.JSLINT_ style,
11733. 13.JSLINT_ textarea,
11734. 13.JSLINT_ ul {
11735. 13 border: 0;
11736. 13 box-sizing: border-box;
11737. 13 margin: 0;
11738. 13 padding: 0;
11739. 13}
11740. 13/* disable text inflation algorithm used on some smartphones and tablets */
11741. 13.JSLINT_ {
11742. 13 -ms-text-size-adjust: none;
11743. 13 -webkit-text-size-adjust: none;
11744. 13 text-size-adjust: none;
11745. 13}
11746. 13.JSLINT_REPORT_ div {
11747. 13 box-sizing: border-box;
11748. 13}
11749. 13/*csslint ignore:end*/
11750. 13
11751. 13/* css - jslint_report - font */
11752. 13.JSLINT_,
11753. 13.JSLINT_ fieldset legend,
11754. 13.JSLINT_ .center {
11755. 13 font-family: daley, sans-serif;
11756. 13 font-size: 14px;
11757. 13}
11758. 13.JSLINT_ fieldset textarea,
11759. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
11760. 13.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
11761. 13 font-size: 12px;
11762. 13}
11763. 13.JSLINT_ fieldset textarea,
11764. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
11765. 13 font-family: monospace;
11766. 13}
11767. 13.JSLINT_ fieldset > div {
11768. 13 font-family: sans-serif;
11769. 13}
11770. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
11771. 13 font-style: normal;
11772. 13 font-weight: bold;
11773. 13}
11774. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
11775. 13 font-style: italic;
11776. 13}
11777. 13.JSLINT_ #JSLINT_REPORT_TITLE {
11778. 13 font-size: 32px;
11779. 13}
11780. 13
11781. 13/* css - jslint_report - general */
11782. 13.JSLINT_ {
11783. 13 background: antiquewhite;
11784. 13}
11785. 13.JSLINT_ fieldset {
11786. 13 background: gainsboro;
11787. 13 clear: both;
11788. 13 margin: 16px 40px;
11789. 13 width: auto;
11790. 13}
11791. 13.JSLINT_ fieldset address {
11792. 13 float: right;
11793. 13}
11794. 13.JSLINT_ fieldset legend,
11795. 13.JSLINT_ .center {
11796. 13 text-align: center;
11797. 13}
11798. 13.JSLINT_ fieldset legend {
11799. 13 background: darkslategray;
11800. 13 color: white;
11801. 13 padding: 4px 0;
11802. 13 width: 100%;
11803. 13}
11804. 13.JSLINT_ fieldset textarea {
11805. 13 padding: 4px;
11806. 13 resize: none;
11807. 13 white-space: pre;
11808. 13 width: 100%;
11809. 13}
11810. 13.JSLINT_ fieldset textarea::selection {
11811. 13 background: wheat;
11812. 13}
11813. 13.JSLINT_ fieldset > div {
11814. 13 padding: 16px;
11815. 13 width: 100%;
11816. 13 word-wrap: break-word;
11817. 13}
11818. 13.JSLINT_ #JSLINT_REPORT_AUTOFIX > div {
11819. 13 background: honeydew;
11820. 13}
11821. 13.JSLINT_ #JSLINT_REPORT_AUTOFIX.blocked > div {
11822. 13 background: pink;
11823. 13}
11824. 13.JSLINT_ #JSLINT_REPORT_AUTOFIX.blocked > legend {
11825. 13 background: #b44;
11826. 13}
11827. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
11828. 13 background: cornsilk;
11829. 13 padding: 8px 16px;
11830. 13}
11831. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
11832. 13 line-height: 20px;
11833. 13 padding-left: 120px;
11834. 13}
11835. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
11836. 13 display: block;
11837. 13 line-height: 20px;
11838. 13}
11839. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
11840. 13 position: relative
11841. 13}
11842. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
11843. 13 line-height: 20px;
11844. 13 position: absolute;
11845. 13 text-align: right;
11846. 13 width: 100px;
11847. 13}
11848. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
11849. 13 background: white;
11850. 13}
11851. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
11852. 13 /* yellow */
11853. 13 background: #ffffe0;
11854. 13 margin-left: 16px;
11855. 13}
11856. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
11857. 13 /* green */
11858. 13 background: #e0ffe0;
11859. 13 margin-left: 32px;
11860. 13}
11861. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
11862. 13 /* blue */
11863. 13 background: #D0D0ff;
11864. 13 margin-left: 48px;
11865. 13}
11866. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
11867. 13 /* purple */
11868. 13 background: #ffe0ff;
11869. 13 margin-left: 64px;
11870. 13}
11871. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
11872. 13 /* red */
11873. 13 background: #ffe0e0;
11874. 13 margin-left: 80px;
11875. 13}
11876. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
11877. 13 /* orange */
11878. 13 background: #ffe390;
11879. 13 margin-left: 96px;
11880. 13}
11881. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
11882. 13 /* gray */
11883. 13 background: #e0e0e0;
11884. 13 margin-left: 112px;
11885. 13}
11886. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
11887. 13 margin-left: 128px;
11888. 13}
11889. 13.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
11890. 13 margin-left: 144px;
11891. 13}
11892. 13.JSLINT_ #JSLINT_REPORT_PROPERTIES {
11893. 13 background: transparent;
11894. 13}
11895. 13.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
11896. 13 background: honeydew;
11897. 13 height: 100px;
11898. 13}
11899. 13.JSLINT_ #JSLINT_REPORT_TITLE {
11900. 13 color: darkslategray;
11901. 13 padding-top: 16px;
11902. 13}
11903. 13.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
11904. 13 display: block;
11905. 13 margin: 16px 0 4px 0;
11906. 13 overflow-x: hidden;
11907. 13 white-space: pre-line;
11908. 13}
11909. 13.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
11910. 13 margin-top: 0;
11911. 13}
11912. 13.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
11913. 13 background: lavenderblush;
11914. 13 display: block;
11915. 13 padding: 4px;
11916. 13 white-space: pre-wrap;
11917. 13}
11918. 13.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
11919. 13 background: pink;
11920. 13 max-height: 400px;
11921. 13 overflow-y: auto;
11922. 13}
11923. 13.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
11924. 13/* Google Lighthouse Accessibility - Background and foreground colors do not */
11925. 13/* have a sufficient contrast ratio. */
11926. 13 /* background: indianred; */
11927. 13 background: #b44;
11928. 13}
11929. 13</style>
11930. 13 `).trim() + "\n";
11931. 13
11932. 13// Produce the Title.
11933. 13
11934. 13 html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
11935. 13 html += "JSLint Report\n";
11936. 13 html += "</div>\n";
11937. 13
11938. 13// PR-509 - Produce the Autofix Report.
11939. 13
11940. 13 html += String(`
11941. 13<fieldset
11942. 13 class="
11943. 13 ${(
11944. 13 autofix_blocked
11945. 1 ? "blocked"
11946. 12 : ""
11947. 13 )}
11948. 13 "
11949. 13 id="JSLINT_REPORT_AUTOFIX"
11950. 13>
11951. 13<legend>Report: Autofix</legend>
11952. 13<div class="center">
11953. 13 ${(
11954. 13 autofix_blocked
11955. 1 ? "Autofix blocked. Fix non-whitespace warnings below."
11956. 12 : autofix
11957. 12 ? "Autofix successful."
11958. 12 : ""
11959. 13 )}
11960. 13</div>
11961. 13</fieldset>
11962. 13 `).trim() + "\n";
11963. 13
11964. 13// Produce the HTML Error Report.
11965. 13// <cite>
11966. 13// <address>LINE_NUMBER</address>
11967. 13// MESSAGE
11968. 13// </cite>
11969. 13// <samp>EVIDENCE</samp>
11970. 13
11971. 13 html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
11972. 13 html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
11973. 13 html += "<div>\n";
11974. 1 if (stop) {
11975. 1 html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
11976. 1 }
11977. 12 warnings.forEach(function ({
11978. 12 column,
11979. 12 line,
11980. 12 line_source,
11981. 12 message,
11982. 12 stack_trace = ""
11983. 12 }, ii) {
11984. 12 html += (
11985. 12 "<cite>" +
11986. 12 address(line, column) +
11987. 12 htmlEscape((ii + 1) + ". " + message) +
11988. 12 "</cite>" +
11989. 12 "<samp>" +
11990. 12 htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace) +
11991. 12 "</samp>\n"
11992. 12 );
11993. 12 });
11994. 5 if (warnings.length === 0) {
11995. 5 html += "<div class=\"center\">There are no warnings.</div>\n";
11996. 5 }
11997. 13 html += "</div>\n";
11998. 13 html += "</fieldset>\n";
11999. 13
12000. 13// Produce the /*property*/ directive.
12001. 13
12002. 13 html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
12003. 13 html += (
12004. 13 "<legend>Report: Properties (" +
12005. 13 Object.keys(property).length +
12006. 13 ")</legend>\n"
12007. 13 );
12008. 13 html += "<label>\n";
12009. 13 html += "<textarea readonly>";
12010. 13 html += "/*property";
12011. 340 Object.keys(property).sort().forEach(function (key, ii) {
12012. 337 if (ii !== 0) {
12013. 337 html += ",";
12014. 337 length_80 += 2;
12015. 337 }
12016. 51 if (length_80 + key.length >= 80) {
12017. 51 length_80 = 4;
12018. 51 html += "\n ";
12019. 51 }
12020. 340 html += " " + key;
12021. 340 length_80 += key.length;
12022. 340 });
12023. 13 html += "\n*/\n";
12024. 13 html += "</textarea>\n";
12025. 13 html += "</label>\n";
12026. 13 html += "</fieldset>\n";
12027. 13
12028. 13// Produce the HTML Function Report.
12029. 13// <div class=LEVEL>
12030. 13// <address>LINE_NUMBER</address>
12031. 13// <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
12032. 13// <dl>
12033. 13// <dt>DETAIL</dt>
12034. 13// <dd>NAMES</dd>
12035. 13// </dl>
12036. 13// </div>
12037. 13
12038. 13// A function's name is a best guess, since most functions have none in the
12039. 13// source. A wrong name in an odd case goes in the CHANGELOG Todo list, and is
12040. 13// fixed only when a user reports it [owner 2026-10-22].
12041. 13
12042. 13 html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
12043. 13 html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
12044. 13 html += "<div>\n";
12045. 2 if (json) {
12046. 2
12047. 2// Bugfix - Fix website crashing when linting pure json-object.
12048. 2// return (
12049. 2
12050. 2 html += (
12051. 2 warnings.length === 0
12052. 2 ? "<div class=\"center\">JSON: good.</div>\n"
12053. 2 : "<div class=\"center\">JSON: bad.</div>\n"
12054. 2 );
12055. 11 } else if (functions.length === 0) {
12056. 11 html += "<div class=\"center\">There are no functions.</div>\n";
12057. 11 }
12058. 13 exports = Object.keys(exports).sort();
12059. 13 froms.sort();
12060. 13 global = Object.keys(global.context).sort();
12061. 13 module = (
12062. 13 module
12063. 1 ? "module"
12064. 12 : "global"
12065. 13 );
12066. 8 if (global.length + froms.length + exports.length > 0) {
12067. 8 if (functions.length === 0) {
12068. 8 html += "<br>\n";
12069. 8 }
12070. 8 html += "<div class=\"level level0\">\n";
12071. 8 html += detail(module, global);
12072. 8 html += detail("import from", froms);
12073. 8 html += detail("export", exports);
12074. 8 html += "</div>\n";
12075. 8 }
12076. 356 functions.forEach(function (the_function) {
12077. 356 const {
12078. 356 context_report = [],
12079. 356 from,
12080. 356 id,
12081. 356 level,
12082. 356 line,
12083. 356 name,
12084. 356 name_list = [],
12085. 356 signature
12086. 356 } = the_function;
12087. 356
12088. 356// PR-516 - Bugfix - <context_report> mixes own and outer names, so each row
12089. 356// checks <scope_function>.
12090. 356
12091. 2136 function id_list(filter) {
12092. 2136 return Array.from(
12093. 3785 new Set(context_report.filter(filter).map(function ({id}) {
12094. 3785 return id;
12095. 3785 }))
12096. 2136 ).sort();
12097. 2136 }
12098. 23562 function is_own({scope_function}) {
12099. 23562 return scope_function === the_function;
12100. 23562 }
12101. 356 html += (
12102. 356 "<div class=\"level level" + htmlEscape(level) + "\">" +
12103. 356 address(line, from + 1) +
12104. 356 "<dfn>" +
12105. 356 (
12106. 356 id === "=>"
12107. 1 ? (
12108. 1 "\u00ab" + htmlEscape(name) + "\u00bb" +
12109. 1 htmlEscape(signature) +
12110. 1 " =>"
12111. 1 )
12112. 355 : (
12113. 355 typeof name === "string"
12114. 355 ? "\u00ab" + htmlEscape(name) + "\u00bb"
12115. 355 : htmlEscape(name.id)
12116. 355 ) + htmlEscape(signature)
12117. 356 ) +
12118. 356 "</dfn>"
12119. 356 );
12120. 486 html += detail("parameter", name_list.map(function ({id}) {
12121. 486 return id;
12122. 486 }).sort());
12123. 3927 html += detail("variable", id_list(function (entry) {
12124. 1132 return is_own(entry) && entry.role === "variable";
12125. 3927 }));
12126. 3927 html += detail("exception", id_list(function (entry) {
12127. 1132 return is_own(entry) && entry.role === "exception";
12128. 3927 }));
12129. 3927 html += detail("closure", id_list(function (entry) {
12130. 1132 return is_own(entry) && entry.closure === true;
12131. 3927 }));
12132. 3927 html += detail("outer", id_list(function (entry) {
12133. 2795 return !is_own(entry) && entry.scope_declared.id !== "(global)";
12134. 3927 }));
12135. 3927 html += detail(module, id_list(function (entry) {
12136. 2795 return !is_own(entry) && entry.scope_declared.id === "(global)";
12137. 3927 }));
12138. 3927 html += detail("label", id_list(function (entry) {
12139. 1132 return is_own(entry) && entry.role === "label";
12140. 3927 }));
12141. 356 html += "</div>\n";
12142. 356 });
12143. 13 html += "</div>\n";
12144. 13 html += "</fieldset>\n";
12145. 13 return html;
12146. 13}
12147. 23
12148. 37async function jstestDescribe(description, testFunction) {
12149. 37
12150. 37// This function will collect test-group <testFunction>'s tests with given
12151. 37// <description>, then run them.
12152. 37
12153. 37 const describe = {
12154. 37 description,
12155. 37 itList: []
12156. 37 };
12157. 37 const describePrv = jstestDescribePrv;
12158. 37 const recordBody = {
12159. 37 describe: description,
12160. 37 description: "jstestDescribe body",
12161. 37 status: "pending"
12162. 37 };
12163. 37 let message;
12164. 37 let result;
12165. 37
12166. 37// Record <description> before the first await, since describes then
12167. 37// interleave and finish out of source order.
12168. 37
12169. 37 jstestDescribePrv = description;
12170. 37
12171. 37// Init jstestTimeStart.
12172. 37
12173. 22 if (jstestTimeStart === undefined) {
12174. 22 jstestTimeStart = Date.now();
12175. 22 process.on("exit", jstestOnExit);
12176. 22 }
12177. 37
12178. 37// PR-517 - Collect, then run. Record the body as pending, then await it, so a
12179. 37// body that hangs before registering fails at exit instead of passing with 0
12180. 37// tests. A body that throws crashes node as an unhandled rejection, leaving its
12181. 37// record pending. A body that settles clean leaves no record.
12182. 37
12183. 37 jstestItRecordList.push(recordBody);
12184. 32 await jstestDescribeRun(describe, testFunction);
12185. 32 jstestItRecordList.splice(jstestItRecordList.indexOf(recordBody), 1);
12186. 32
12187. 32// PR-517 - Assert this describe's tests, then this describe, are in ASCII
12188. 32// order. Record them before any test runs, so a hung sibling cannot hide them
12189. 32// at exit.
12190. 32
12191. 32 result = [
12192. 363 jstestOrderCheck(describe.itList.map(function (record) {
12193. 363 return record.description;
12194. 363 })),
12195. 32 jstestOrderCheck([describePrv, description])
12196. 32 ].filter(Boolean).map(function ([err, descriptionIt]) {
12197. 32 return {
12198. 32 describe: description,
12199. 32 description: descriptionIt,
12200. 32 err,
12201. 32 status: "failed"
12202. 32 };
12203. 32 });
12204. 32 jstestItRecordList.push(...result);
12205. 363 await Promise.all(describe.itList.map(async function (record) {
12206. 363 try {
12207. 360 await record.testFunction();
12208. 360 record.status = "passed";
12209. 360 } catch (err) {
12210. 360 record.err = err;
12211. 360 record.status = "failed";
12212. 360 }
12213. 363 }));
12214. 32 result.unshift(...describe.itList);
12215. 32
12216. 32// PR-517 - Print nothing for a describe whose tests the filter all skipped.
12217. 32
12218. 32 if (result.length === 0) {
12219. 32 return;
12220. 32 }
12221. 32
12222. 32// Print test results.
12223. 32
12224. 32 message = (
12225. 32 "\n " + (Date.now() - jstestTimeStart) + "ms" +
12226. 32 " - test describe - " + description + "\n" +
12227. 358 result.map(function (record) {
12228. 358 jstestItCount += 1;
12229. 358 record.printed = true;
12230. 358 return (
12231. 358 (record.status === "passed")
12232. 92 ? (
12233. 92 " \u001b[32m\u2714 " + jstestItCount + ". test it - " +
12234. 92 record.description + "\u001b[39m"
12235. 92 )
12236. 266 : (
12237. 266 " \u001b[31m\u2718 " + jstestItCount + ". test it - " +
12238. 266 record.description + "\n" +
12239. 266 jstestErrText(record.err) + "\u001b[39m"
12240. 266 )
12241. 358 );
12242. 358 }).join("\n")
12243. 32 );
12244. 32 console.error(message);
12245. 32}
12246. 23
12247. 37function jstestDescribeRun(describe, testFunction) {
12248. 37
12249. 37// This function will run describe-body <testFunction> with <describe> current
12250. 37// only while it runs synchronously, so <jstestIt> registers there alone and a
12251. 37// call after an await throws.
12252. 37
12253. 37 jstestDescribeNow = describe;
12254. 37 try {
12255. 37 return testFunction();
12256. 37 } finally {
12257. 37 jstestDescribeNow = undefined;
12258. 37 }
12259. 37}
12260. 23
12261. 269function jstestErrText(err) {
12262. 269
12263. 269// This function will return the stack of thrown <err>, else <err> as text,
12264. 269// for any thrown value, even a falsy one or a null-prototype object.
12265. 269
12266. 269 try {
12267. 267 return String(err?.stack ?? err);
12268. 1 } catch (ignore) {
12269. 1 return "[" + typeof err + "]";
12270. 1 }
12271. 269}
12272. 23
12273. 374function jstestIt(description, testFunction) {
12274. 374
12275. 374// This function will register test-case <testFunction> with given
12276. 374// <description> in the current describe, to run once its body settles.
12277. 374
12278. 374 const describe = jstestDescribeNow;
12279. 374
12280. 374// PR-517 - Throw outside a describe body's synchronous part, since no describe
12281. 374// would run or report this test.
12282. 374
12283. 372 if (!describe) {
12284. 372 throw new Error(
12285. 372 "jstestIt " + JSON.stringify(description) +
12286. 372 " called outside a jstestDescribe body, or after an await"
12287. 372 );
12288. 372 }
12289. 372
12290. 372// PR-517 - Run only a test whose "<describe> - <it>" matches opt-in regexp
12291. 372// npm_config_mode_test_filter, e.g.
12292. 372// npm_config_mode_test_filter="jstestXxx.*pending" npm test
12293. 372// Prefer the env form, since npm 11 warns the flag form --mode-test-filter
12294. 372// stops working in npm 12.
12295. 372
12296. 372 if (!new RegExp(
12297. 372 process.env.npm_config_mode_test_filter || ""
12298. 372 ).test(describe.description + " - " + description)) {
12299. 372 return;
12300. 372 }
12301. 372
12302. 372// PR-517 - Record this test as pending from registration, so <jstestReport>
12303. 372// sees it even if its describe never runs or prints.
12304. 372
12305. 372 jstestItRecordList.push({
12306. 372 describe: describe.description,
12307. 372 description,
12308. 372 status: "pending",
12309. 372 testFunction
12310. 372 });
12311. 372 describe.itList.push(jstestItRecordList.at(-1));
12312. 372}
12313. 23
12314. 22function jstestOnExit(exitCode) {
12315. 22
12316. 22// This function will on process-exit, print test-report
12317. 22// and exit with non-zero exit-code if any test failed.
12318. 22
12319. 22 const report = jstestReport();
12320. 22 console.error(report.text);
12321. 16 process.exitCode = exitCode || report.exitCode;
12322. 22}
12323. 23
12324. 64function jstestOrderCheck(descriptionList) {
12325. 64
12326. 64// This function will return a failed result for the first description in
12327. 64// <descriptionList> not strictly after the one before it, else undefined.
12328. 64// Strict means a duplicate fails too, as two identical lines cannot be told
12329. 64// apart in the output. Not '>=', which is false for a non-string name such as
12330. 64// undefined, so that name would pass.
12331. 64
12332. 426 for (const [ii, description] of descriptionList.entries()) {
12333. 426 if (ii > 0 && !(descriptionList[ii - 1] < description)) {
12334. 426 const now = JSON.stringify(description);
12335. 426 const prv = JSON.stringify(descriptionList[ii - 1]);
12336. 426 return [
12337. 426 new Error(
12338. 426 (prv === now)
12339. 426 ? `duplicate description ${now}`
12340. 426 : `${now} must sort before ${prv}`
12341. 426 ),
12342. 426 "jstestDescribe and jstestIt descriptions in ASCII order"
12343. 426 ];
12344. 426 }
12345. 426 }
12346. 59}
12347. 23
12348. 22function jstestReport() {
12349. 22
12350. 22// This function will return the test-report <text> and its <exitCode>,
12351. 22// without side effects.
12352. 22
12353. 22// PR-517 - List and count as failed each test its describe never printed: one
12354. 22// still pending, e.g. hung until a watchdog ended the process, or one that
12355. 22// failed while a sibling hung, whose error was otherwise never shown.
12356. 22
12357. 376 const countFailed = jstestItRecordList.filter(function ({status}) {
12358. 376 return status !== "passed";
12359. 376 }).length;
12360. 22 const filter = process.env.npm_config_mode_test_filter;
12361. 22
12362. 22// PR-517 - Fail a run whose filter matched no test, so a stray or mistyped
12363. 22// filter, e.g. a mode_test_filter line left in .npmrc, cannot skip the whole
12364. 22// suite and pass green.
12365. 22
12366. 4 const filterEmpty = Boolean(filter) && jstestItRecordList.length === 0;
12367. 376 const unprintedList = jstestItRecordList.filter(function (record) {
12368. 18 return !record.printed && record.status !== "passed";
12369. 376 });
12370. 22 return {
12371. 22
12372. 22// PR-517 - Exit 1, not the failed count, since an exit status keeps only 8
12373. 22// bits, so 256 failed tests would exit 0.
12374. 22
12375. 4 exitCode: Number(countFailed > 0 || filterEmpty),
12376. 22 text: (
12377. 4 (countFailed > 0 || filterEmpty)
12378. 19 ? "\n\u001b[31m"
12379. 3 : "\n\u001b[32m"
12380. 22 ) +
12381. 17 unprintedList.map(function ({
12382. 17 describe,
12383. 17 description,
12384. 17 err,
12385. 17 status
12386. 17 }, ii) {
12387. 17 return (
12388. 17 " \u2718 " + (jstestItCount + ii + 1) + ". test it " + status +
12389. 17 " - " + describe + " - " + description + "\n" + (
12390. 17 (status === "pending")
12391. 14 ? ""
12392. 3 : jstestErrText(err) + "\n"
12393. 17 )
12394. 17 );
12395. 17 }).join("") + (
12396. 22 filterEmpty
12397. 1 ? (
12398. 1 " \u2718 no test matched npm_config_mode_test_filter=" +
12399. 1 JSON.stringify(filter) + "\n"
12400. 1 )
12401. 21 : ""
12402. 22 ) +
12403. 22 " tests total - " + jstestItRecordList.length + "\n" +
12404. 22 " tests failed - " + countFailed + "\n" +
12405. 22 "\n" +
12406. 22 " time finished - " +
12407. 22 Number(Date.now() - jstestTimeStart).toLocaleString() +
12408. 22 " ms\n" +
12409. 22 "\u001b[39m"
12410. 22 };
12411. 22}
12412. 23
12413. 185async function moduleFsInit() {
12414. 185
12415. 185// This function will import nodejs builtin-modules if they have not yet been
12416. 185// imported.
12417. 185
12418. 185// State 3 - Modules already imported.
12419. 185
12420. 160 if (moduleFs !== undefined) {
12421. 160 return;
12422. 160 }
12423. 25
12424. 25// State 2 - Wait while modules are importing.
12425. 25
12426. 25 if (moduleFsInitResolveList !== undefined) {
12427. 2 return new Promise(function (resolve) {
12428. 2 moduleFsInitResolveList.push(resolve);
12429. 2 });
12430. 23 }
12431. 23
12432. 23// State 1 - Start importing modules.
12433. 23
12434. 23 moduleFsInitResolveList = [];
12435. 23 [
12436. 23 moduleChildProcess,
12437. 23 moduleFs,
12438. 23 modulePath,
12439. 23 moduleUrl
12440. 23 ] = await Promise.all([
12441. 23 import("child_process"),
12442. 23 import("fs"),
12443. 23 import("path"),
12444. 23 import("url")
12445. 23 ]);
12446. 23 while (moduleFsInitResolveList.length > 0) {
12447. 2 moduleFsInitResolveList.shift()();
12448. 2 }
12449. 185}
12450. 23
12451. 3314function noop(value) {
12452. 3314
12453. 3314// This function will do nothing except return <value>.
12454. 3314
12455. 3314 return value;
12456. 3314}
12457. 23
12458. 13201function objectDeepCopyWithKeysSorted(obj) {
12459. 13201
12460. 13201// This function will recursively deep-copy <obj> with keys sorted.
12461. 13201
12462. 13201 let sorted;
12463. 9187 if (typeof obj !== "object" || !obj) {
12464. 9187 return obj;
12465. 9187 }
12466. 4014
12467. 4014// Recursively deep-copy list with child-keys sorted.
12468. 4014
12469. 4014 if (Array.isArray(obj)) {
12470. 1428 return obj.map(objectDeepCopyWithKeysSorted);
12471. 2586 }
12472. 2586
12473. 2586// Recursively deep-copy obj with keys sorted.
12474. 2586
12475. 2586 sorted = Object.create(null);
12476. 7457 Object.keys(obj).sort().forEach(function (key) {
12477. 7457 sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
12478. 7457 });
12479. 2586 return sorted;
12480. 2586}
12481. 23
12482. 2217function object_assign_from_list(dict, list, value) {
12483. 2217
12484. 2217// Assign each property-name from <list> to <dict>.
12485. 2217
12486. 18516 list.forEach(function (key) {
12487. 18516 dict[key] = value;
12488. 18516 });
12489. 2217 return dict;
12490. 2217}
12491. 23
12492. 98function v8CoverageListMerge(processCovs) {
12493. 98
12494. 98// This function is derived from MIT Licensed v8-coverage at
12495. 98// https://github.com/demurgos/v8-coverage/tree/master/ts
12496. 98// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
12497. 98//
12498. 98// Merges a list of v8 process coverages.
12499. 98// The result is normalized.
12500. 98// The input values may be mutated, it is not safe to use them after passing
12501. 98// them to this function.
12502. 98// The computation is synchronous.
12503. 98// @param processCovs Process coverages to merge.
12504. 98// @return Merged process coverage.
12505. 98
12506. 98 let resultMerged = []; // List of merged scripts from processCovs.
12507. 98 let urlToScriptDict = new Map(); // Map scriptCov.url to scriptCovs.
12508. 98
12509. 1198 function compareRangeList(aa, bb) {
12510. 1198
12511. 1198// Compares two range coverages.
12512. 1198// The ranges are first ordered by ascending `startOffset` and then by
12513. 1198// descending `endOffset`.
12514. 1198// This corresponds to a pre-order tree traversal.
12515. 1198
12516. 1168 if (aa.startOffset !== bb.startOffset) {
12517. 1168 return aa.startOffset - bb.startOffset;
12518. 1168 }
12519. 30 return bb.endOffset - aa.endOffset;
12520. 30 }
12521. 98
12522. 1708 function dictKeyValueAppend(dict, key, value) {
12523. 1708
12524. 1708// This function will append <value> to list <dict>[<key>].
12525. 1708
12526. 1708 let list = dict.get(key);
12527. 1166 if (list === undefined) {
12528. 1166 list = [];
12529. 1166 dict.set(key, list);
12530. 1166 }
12531. 1708 list.push(value);
12532. 1708 }
12533. 98
12534. 384 function mergeTreeList(parentTrees) {
12535. 384
12536. 384// This function will return RangeTree object with <parentTrees> merged into
12537. 384// property-children.
12538. 384// @precondition Same `start` and `end` for all the parentTrees
12539. 384
12540. 119 if (parentTrees.length <= 1) {
12541. 119 return parentTrees[0];
12542. 265 }
12543. 265
12544. 265// new RangeTree().
12545. 265
12546. 265 return {
12547. 265
12548. 265// Merge parentTrees into property-children.
12549. 265
12550. 265 children: mergeTreeListToChildren(parentTrees),
12551. 669 delta: parentTrees.reduce(function (aa, bb) {
12552. 669 return aa + bb.delta;
12553. 669 }, 0),
12554. 265 end: parentTrees[0].end,
12555. 265 start: parentTrees[0].start
12556. 265 };
12557. 265 }
12558. 98
12559. 265 function mergeTreeListToChildren(parentTrees) {
12560. 265
12561. 265// This function will return <resultChildren> with <parentTrees> merged.
12562. 265
12563. 265 let openRange;
12564. 265 let parentToChildDict = new Map(); // Map parent to child.
12565. 265 let queueList;
12566. 265 let queueListIi = 0;
12567. 265 let queueOffset;
12568. 265 let queueTrees;
12569. 265 let resultChildren = [];
12570. 265 let startToTreeDict = new Map(); // Map tree.start to tree.
12571. 639 function nextXxx() {
12572. 639
12573. 639// Increment nextOffset, nextTrees.
12574. 639
12575. 639 let [
12576. 639 nextOffset, nextTrees
12577. 300 ] = queueList[queueListIi] || [];
12578. 639 let openRangeEnd;
12579. 583 if (queueTrees === undefined) {
12580. 583 queueListIi += 1;
12581. 583
12582. 583// Increment nextOffset, nextTrees.
12583. 583
12584. 583 } else if (nextOffset === undefined || nextOffset > queueOffset) {
12585. 56 nextOffset = queueOffset;
12586. 56 nextTrees = queueTrees;
12587. 56 queueTrees = undefined;
12588. 56
12589. 56// Concat queueTrees to nextTrees.
12590. 56
12591. 56 } else {
12592. 56 if (nextOffset === queueOffset) {
12593. 56 queueTrees.forEach(function (tree) {
12594. 56 nextTrees.push(tree);
12595. 56 });
12596. 56 queueTrees = undefined;
12597. 56 }
12598. 56 queueListIi += 1;
12599. 56 }
12600. 639
12601. 639// Reached end of queueList.
12602. 639
12603. 265 if (nextOffset === undefined) {
12604. 265 if (openRange !== undefined) {
12605. 265
12606. 265// Append nested-children from parentToChildDict (within openRange) to
12607. 265// resultChildren.
12608. 265
12609. 265 resultAppendNextChild();
12610. 265 }
12611. 265 return true;
12612. 374 }
12613. 374 if (openRange !== undefined && openRange.end <= nextOffset) {
12614. 129
12615. 129// Append nested-children from parentToChildDict (within openRange) to
12616. 129// resultChildren.
12617. 129
12618. 129 resultAppendNextChild();
12619. 129 openRange = undefined;
12620. 374 }
12621. 374 if (openRange === undefined) {
12622. 292 openRangeEnd = nextOffset + 1;
12623. 502 nextTrees.forEach(function ({
12624. 502 parentIi,
12625. 502 tree
12626. 502 }) {
12627. 502 openRangeEnd = Math.max(openRangeEnd, tree.end);
12628. 502
12629. 502// Append children from nextTrees to parentToChildDict.
12630. 502
12631. 502 dictKeyValueAppend(parentToChildDict, parentIi, tree);
12632. 502 });
12633. 292 queueOffset = openRangeEnd;
12634. 292 openRange = {
12635. 292 end: openRangeEnd,
12636. 292 start: nextOffset
12637. 292 };
12638. 292 } else {
12639. 114 nextTrees.forEach(function ({
12640. 114 parentIi,
12641. 114 tree
12642. 114 }) {
12643. 114 let right;
12644. 82 if (tree.end > openRange.end) {
12645. 82 right = treeSplit(tree, openRange.end);
12646. 82 if (queueTrees === undefined) {
12647. 82 queueTrees = [];
12648. 82 }
12649. 82
12650. 82// new RangeTreeWithParent().
12651. 82
12652. 82 queueTrees.push({
12653. 82 parentIi,
12654. 82 tree: right
12655. 82 });
12656. 82 }
12657. 114
12658. 114// Append children from nextTrees to parentToChildDict.
12659. 114
12660. 114 dictKeyValueAppend(parentToChildDict, parentIi, tree);
12661. 114 });
12662. 82 }
12663. 639 }
12664. 292 function resultAppendNextChild() {
12665. 292
12666. 292// This function will append next child to <resultChildren>.
12667. 292
12668. 292 let treesMatching = [];
12669. 589 parentToChildDict.forEach(function (nested) {
12670. 589 if (
12671. 563 nested.length === 1 &&
12672. 563 nested[0].start === openRange.start &&
12673. 480 nested[0].end === openRange.end
12674. 468 ) {
12675. 468 treesMatching.push(nested[0]);
12676. 468 } else {
12677. 121
12678. 121// new rangeTreeCreate().
12679. 121
12680. 121 treesMatching.push({
12681. 121 children: nested,
12682. 121 delta: 0,
12683. 121 end: openRange.end,
12684. 121 start: openRange.start
12685. 121 });
12686. 121 }
12687. 589 });
12688. 292 parentToChildDict.clear();
12689. 292
12690. 292// Recurse mergeTreeList.
12691. 292
12692. 292 resultChildren.push(mergeTreeList(treesMatching));
12693. 292 }
12694. 75 function treeSplit(tree, offset) {
12695. 75
12696. 75// This function will split <tree> along <offset> and return the right-side.
12697. 75// @precondition `tree.start < offset && offset < tree.end`
12698. 75// @return RangeTree Right part
12699. 75
12700. 75 let child;
12701. 75 let ii = 0;
12702. 75 let leftChildLen = tree.children.length;
12703. 75 let mid;
12704. 75 let resultTree;
12705. 75 let rightChildren;
12706. 75
12707. 75// TODO(perf): Binary search (check overhead) //jslint-ignore-line
12708. 75
12709. 19 while (ii < tree.children.length) {
12710. 19 child = tree.children[ii];
12711. 19 if (child.start < offset && offset < child.end) {
12712. 19
12713. 19// Recurse treeSplit.
12714. 19
12715. 19 mid = treeSplit(child, offset);
12716. 19 leftChildLen = ii + 1;
12717. 19 break;
12718. 19 }
12719. 19 if (child.start >= offset) {
12720. 19 leftChildLen = ii;
12721. 19 break;
12722. 19 }
12723. 19 ii += 1;
12724. 19 }
12725. 75 rightChildren = tree.children.splice(
12726. 75 leftChildLen,
12727. 75 tree.children.length - leftChildLen
12728. 75 );
12729. 4 if (mid !== undefined) {
12730. 4 rightChildren.unshift(mid);
12731. 4 }
12732. 75
12733. 75// new rangeTreeCreate().
12734. 75
12735. 75 resultTree = {
12736. 75 children: rightChildren,
12737. 75 delta: tree.delta,
12738. 75 end: tree.end,
12739. 75 start: offset
12740. 75 };
12741. 75 tree.end = offset;
12742. 75 return resultTree;
12743. 75 }
12744. 265
12745. 265// Init startToTreeDict.
12746. 265
12747. 669 parentTrees.forEach(function (parentTree, parentIi) {
12748. 545 parentTree.children.forEach(function (child) {
12749. 545
12750. 545// Append child with child.start to startToTreeDict.
12751. 545
12752. 545 dictKeyValueAppend(startToTreeDict, child.start, {
12753. 545 parentIi,
12754. 545 tree: child
12755. 545 });
12756. 545 });
12757. 669 });
12758. 265
12759. 265// init queueList.
12760. 265
12761. 335 queueList = Array.from(startToTreeDict).map(function ([
12762. 335 startOffset, trees
12763. 335 ]) {
12764. 335
12765. 335// new StartEvent().
12766. 335
12767. 335 return [
12768. 335 startOffset, trees
12769. 335 ];
12770. 222 }).sort(function (aa, bb) {
12771. 222 return aa[0] - bb[0];
12772. 222 });
12773. 639 while (true) {
12774. 639 if (nextXxx()) {
12775. 639 break;
12776. 639 }
12777. 639 }
12778. 265 return resultChildren;
12779. 265 }
12780. 98
12781. 696 function sortFunc(funcCov) {
12782. 696
12783. 696// This function will normalize-and-sort <funcCov>.ranges.
12784. 696// Sorts the ranges (pre-order sort).
12785. 696// TODO: Tree-based normalization of the ranges. //jslint-ignore-line
12786. 696// @param funcCov Function coverage to normalize.
12787. 696
12788. 696 funcCov.ranges = treeToRanges(treeFromSortedRanges(
12789. 696 funcCov.ranges.sort(compareRangeList)
12790. 696 ));
12791. 696 return funcCov;
12792. 696 }
12793. 98
12794. 130 function sortScript(scriptCov) {
12795. 130
12796. 130// This function will normalize-and-sort <scriptCov>.functions.
12797. 130
12798. 130// Normalize-and-sort functions[xxx].ranges.
12799. 130
12800. 695 scriptCov.functions.forEach(function (funcCov) {
12801. 695 sortFunc(funcCov);
12802. 695 });
12803. 130
12804. 130// Sort functions by root range (pre-order sort).
12805. 130
12806. 583 scriptCov.functions.sort(function (aa, bb) {
12807. 583 return compareRangeList(aa.ranges[0], bb.ranges[0]);
12808. 583 });
12809. 130 return scriptCov;
12810. 130 }
12811. 98
12812. 895 function treeFromSortedRanges(ranges) {
12813. 895
12814. 895// @precondition `ranges` are well-formed and pre-order sorted
12815. 895
12816. 895 let root;
12817. 895 let stack = []; // Stack of parent trees and parent counts.
12818. 1869 ranges.forEach(function (range) {
12819. 1869
12820. 1869// new rangeTreeCreate().
12821. 1869
12822. 1869 let node = {
12823. 1869 children: [],
12824. 1869 delta: range.count,
12825. 1869 end: range.endOffset,
12826. 1869 start: range.startOffset
12827. 1869 };
12828. 1869 let parent;
12829. 1869 let parentCount;
12830. 895 if (root === undefined) {
12831. 895 root = node;
12832. 895 stack.push([
12833. 895 node, range.count
12834. 895 ]);
12835. 895 return;
12836. 974 }
12837. 1574 while (true) {
12838. 1574 [
12839. 1574 parent, parentCount
12840. 1574 ] = stack[stack.length - 1];
12841. 1574
12842. 1574// assert: `top !== undefined` (the ranges are sorted)
12843. 1574
12844. 1574 if (range.startOffset < parent.end) {
12845. 1574 break;
12846. 1574 }
12847. 1574 stack.pop();
12848. 1574 }
12849. 974 node.delta -= parentCount;
12850. 974 parent.children.push(node);
12851. 974 stack.push([
12852. 974 node, range.count
12853. 974 ]);
12854. 974 });
12855. 895 return root;
12856. 895 }
12857. 98
12858. 788 function treeToRanges(tree) {
12859. 788
12860. 788// Get the range coverages corresponding to the tree.
12861. 788// The ranges are pre-order sorted.
12862. 788
12863. 788 let count;
12864. 788 let cur;
12865. 788 let ii;
12866. 788 let parentCount;
12867. 788 let ranges = [];
12868. 788 let stack = [ // Stack of parent trees and counts.
12869. 788 [
12870. 788 tree, 0
12871. 788 ]
12872. 788 ];
12873. 1643 function normalizeRange(tree) {
12874. 1643
12875. 1643// @internal
12876. 1643
12877. 1643 let children = [];
12878. 1643 let curEnd;
12879. 1643 let head;
12880. 1643 let tail = [];
12881. 855 function endChain() {
12882. 18 if (tail.length !== 0) {
12883. 18 head.end = tail[tail.length - 1].end;
12884. 18 tail.forEach(function (tailTree) {
12885. 18 tailTree.children.forEach(function (subChild) {
12886. 18 subChild.delta += tailTree.delta - head.delta;
12887. 18 head.children.push(subChild);
12888. 18 });
12889. 18 });
12890. 18 tail.length = 0;
12891. 18 }
12892. 855
12893. 855// Recurse normalizeRange.
12894. 855
12895. 855 normalizeRange(head);
12896. 855 children.push(head);
12897. 855 }
12898. 873 tree.children.forEach(function (child) {
12899. 435 if (head === undefined) {
12900. 435 head = child;
12901. 438 } else if (
12902. 438 child.delta === head.delta && child.start === curEnd
12903. 438 ) {
12904. 438 tail.push(child);
12905. 438 } else {
12906. 438 endChain();
12907. 438 head = child;
12908. 438 }
12909. 873 curEnd = child.end;
12910. 873 });
12911. 435 if (head !== undefined) {
12912. 435 endChain();
12913. 435 }
12914. 240 if (children.length === 1) {
12915. 240 if (
12916. 240 children[0].start === tree.start &&
12917. 240 children[0].end === tree.end
12918. 240 ) {
12919. 240 tree.delta += children[0].delta;
12920. 240 tree.children = children[0].children;
12921. 240
12922. 240// `.lazyCount` is zero for both (both are after normalization)
12923. 240
12924. 240 return;
12925. 240 }
12926. 1637 }
12927. 1637 tree.children = children;
12928. 1637 }
12929. 788 normalizeRange(tree);
12930. 1637 while (stack.length > 0) {
12931. 1637 [
12932. 1637 cur, parentCount
12933. 1637 ] = stack.pop();
12934. 1637 count = parentCount + cur.delta;
12935. 1637 ranges.push({
12936. 1637 count,
12937. 1637 endOffset: cur.end,
12938. 1637 startOffset: cur.start
12939. 1637 });
12940. 1637 ii = cur.children.length - 1;
12941. 1637 while (ii >= 0) {
12942. 1637 stack.push([
12943. 1637 cur.children[ii], count
12944. 1637 ]);
12945. 1637 ii -= 1;
12946. 1637 }
12947. 1637 }
12948. 788 return ranges;
12949. 788 }
12950. 98
12951. 1 if (processCovs.length === 0) {
12952. 1 return {
12953. 1 result: []
12954. 1 };
12955. 97 }
12956. 97
12957. 97// Init urlToScriptDict.
12958. 97
12959. 235 processCovs.forEach(function ({
12960. 235 result
12961. 235 }) {
12962. 270 result.forEach(function (scriptCov) {
12963. 270 dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
12964. 270 });
12965. 235 });
12966. 130 urlToScriptDict.forEach(function (scriptCovs) {
12967. 130
12968. 130// assert: `scriptCovs.length > 0`
12969. 130
12970. 130// function mergeScriptList(scriptCovs) {
12971. 130// Merges a list of matching script coverages.
12972. 130// Scripts are matching if they have the same `url`.
12973. 130// The result is normalized.
12974. 130// The input values may be mutated, it is not safe to use them after passing
12975. 130// them to this function.
12976. 130// The computation is synchronous.
12977. 130// @param scriptCovs Process coverages to merge.
12978. 130// @return Merged script coverage, or `undefined` if the input list was empty.
12979. 130
12980. 130 let functions = [];
12981. 130
12982. 130// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
12983. 130
12984. 130 let rangeToFuncDict = new Map();
12985. 130
12986. 130// PR-510 - deadcode-confirmed - Every list in <urlToScriptDict> has at least
12987. 130// one element. <dictKeyValueAppend> is the only writer and pushes in the same
12988. 130// call that creates the list, and nothing pops, splices or filters it.
12989. 130//
12990. 130// deadcode-revive - Restore <mergeScriptList>, whose signature is commented
12991. 130// out above. It took its list from any caller, which is why it needed this
12992. 130// guard. Adding a second writer or a filter to the map would also do it.
12993. 130//
12994. 130// if (scriptCovs.length === 0) {
12995. 130// return undefined;
12996. 130// }
12997. 130
12998. 97 if (scriptCovs.length === 1) {
12999. 97 resultMerged.push(sortScript(scriptCovs[0]));
13000. 97 return;
13001. 97 }
13002. 97
13003. 97// Init rangeToFuncDict.
13004. 97// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
13005. 97
13006. 226 scriptCovs.forEach(function ({
13007. 226 functions
13008. 226 }) {
13009. 277 functions.forEach(function (funcCov) {
13010. 277 dictKeyValueAppend(
13011. 277 rangeToFuncDict,
13012. 277
13013. 277// This string can be used to match function with same root range.
13014. 277// The string is derived from the start and end offsets of the root range of
13015. 277// the function.
13016. 277// This assumes that `ranges` is non-empty (true for valid function coverages).
13017. 277
13018. 277 (
13019. 277 funcCov.ranges[0].startOffset +
13020. 277 ";" + funcCov.ranges[0].endOffset
13021. 277 ),
13022. 277 funcCov
13023. 277 );
13024. 277 });
13025. 226 });
13026. 112 rangeToFuncDict.forEach(function (funcCovs) {
13027. 112
13028. 112// assert: `funcCovs.length > 0`
13029. 112
13030. 112// function mergeFuncList(funcCovs) {
13031. 112// Merges a list of matching function coverages.
13032. 112// Functions are matching if their root ranges have the same span.
13033. 112// The result is normalized.
13034. 112// The input values may be mutated, it is not safe to use them after passing
13035. 112// them to this function.
13036. 112// The computation is synchronous.
13037. 112// @param funcCovs Function coverages to merge.
13038. 112// @return Merged function coverage, or `undefined` if the input list was empty.
13039. 112
13040. 112 let count = 0;
13041. 112 let isBlockCoverage;
13042. 112 let merged;
13043. 112 let ranges;
13044. 112 let trees = [];
13045. 112
13046. 112// PR-510 - deadcode-confirmed - Same as <scriptCovs> above, but with
13047. 112// <rangeToFuncDict>. <dictKeyValueAppend> is its only writer and pushes as it
13048. 112// creates each list.
13049. 112//
13050. 112// deadcode-revive - Restore <mergeFuncList>, commented out above, or give the
13051. 112// map a second writer.
13052. 112//
13053. 112// if (funcCovs.length === 0) {
13054. 112// return undefined;
13055. 112// }
13056. 112
13057. 97 if (funcCovs.length === 1) {
13058. 97 functions.push(sortFunc(funcCovs[0]));
13059. 97 return;
13060. 111 }
13061. 111
13062. 111// assert: `funcCovs[0].ranges.length > 0`
13063. 111
13064. 276 funcCovs.forEach(function (funcCov) {
13065. 276
13066. 276// assert: `funcCov.ranges.length > 0`
13067. 276// assert: `funcCov.ranges` is sorted
13068. 276
13069. 276 count += (
13070. 276 funcCov.count !== undefined
13071. 111 ? funcCov.count
13072. 274 : funcCov.ranges[0].count
13073. 276 );
13074. 199 if (funcCov.isBlockCoverage) {
13075. 199 trees.push(treeFromSortedRanges(funcCov.ranges));
13076. 199 }
13077. 276 });
13078. 111 if (trees.length > 0) {
13079. 97 isBlockCoverage = true;
13080. 97 ranges = treeToRanges(mergeTreeList(trees));
13081. 97 } else {
13082. 97 isBlockCoverage = false;
13083. 97 ranges = [
13084. 97 {
13085. 97 count,
13086. 97 endOffset: funcCovs[0].ranges[0].endOffset,
13087. 97 startOffset: funcCovs[0].ranges[0].startOffset
13088. 97 }
13089. 97 ];
13090. 111 }
13091. 111 merged = {
13092. 111 functionName: funcCovs[0].functionName,
13093. 111 isBlockCoverage,
13094. 111 ranges
13095. 111 };
13096. 111 if (count !== ranges[0].count) {
13097. 97 merged.count = count;
13098. 111 }
13099. 111
13100. 111// assert: `merged` is normalized
13101. 111
13102. 111 functions.push(merged);
13103. 111 });
13104. 97 resultMerged.push(sortScript({
13105. 97 functions,
13106. 97 scriptId: scriptCovs[0].scriptId,
13107. 97 url: scriptCovs[0].url
13108. 97 }));
13109. 97 });
13110. 97
13111. 97// Sorts the scripts alphabetically by `url`.
13112. 97// Reassigns script ids: the script at index `0` receives `"0"`, the script at
13113. 97// index `1` receives `"1"` etc.
13114. 97
13115. 97 Object.entries(resultMerged.sort(function (aa, bb) {
13116. 97 return (
13117. 97 aa.url > bb.url
13118. 97 ? 1
13119. 97 : -1
13120. 97 );
13121. 130 })).forEach(function ([
13122. 130 scriptId, scriptCov
13123. 130 ]) {
13124. 130 scriptCov.scriptId = scriptId.toString(10);
13125. 130 });
13126. 97 return {
13127. 97 result: resultMerged
13128. 97 };
13129. 97}
13130. 23
13131. 9async function v8CoverageReportCreate({
13132. 9 consoleError,
13133. 9 coverageDir,
13134. 9 processArgv = []
13135. 9}) {
13136. 9
13137. 9// This function will create html-coverage-reports directly from
13138. 9// v8-coverage-files in <coverageDir>.
13139. 9// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
13140. 9// 2. Merge JSON v8-coverage-files in <coverageDir>.
13141. 9// 3. Create html-coverage-reports in <coverageDir>.
13142. 9
13143. 9 let cwd;
13144. 9 let excludeList = [];
13145. 9 let exitCode = 0;
13146. 9 let fileDict;
13147. 9 let includeList = [];
13148. 9 let modeIncludeNodeModules;
13149. 9 let processArgElem;
13150. 9 let promiseList = [];
13151. 9 let v8CoverageObj;
13152. 9
13153. 15 function htmlRender({
13154. 15 fileList,
13155. 15 lineList,
13156. 15 modeIndex,
13157. 15 pathname
13158. 15 }) {
13159. 15 let html;
13160. 15 let padLines;
13161. 15 let padPathname;
13162. 15 let txt;
13163. 15 let txtBorder;
13164. 15 html = "";
13165. 15 html += String(`
13166. 15<!DOCTYPE html>
13167. 15<html lang="en">
13168. 15<head>
13169. 15<title>V8 Coverage Report</title>
13170. 15<style>
13171. 15/* jslint utility2:true */
13172. 15/*csslint ignore:start*/
13173. 15.coverage,
13174. 15.coverage a,
13175. 15.coverage div,
13176. 15.coverage pre,
13177. 15.coverage span,
13178. 15.coverage table,
13179. 15.coverage tbody,
13180. 15.coverage td,
13181. 15.coverage th,
13182. 15.coverage thead,
13183. 15.coverage tr {
13184. 15 box-sizing: border-box;
13185. 15 font-family: monospace;
13186. 15}
13187. 15/*csslint ignore:end*/
13188. 15
13189. 15/* css - coverage_report - general */
13190. 15body {
13191. 15 margin: 0;
13192. 15}
13193. 15.coverage pre {
13194. 15 margin: 5px 0;
13195. 15}
13196. 15.coverage table {
13197. 15 border-collapse: collapse;
13198. 15}
13199. 15.coverage td,
13200. 15.coverage th {
13201. 15 border: 1px solid #777;
13202. 15 line-height: 20px;
13203. 15 margin: 0;
13204. 15 padding: 5px 10px;
13205. 15}
13206. 15.coverage td span {
13207. 15 display: inline-block;
13208. 15 width: 100%;
13209. 15}
13210. 15.coverage .content {
13211. 15 padding: 0 5px;
13212. 15}
13213. 15.coverage .content a {
13214. 15 text-decoration: none;
13215. 15}
13216. 15.coverage .count {
13217. 15 margin: 0 5px;
13218. 15 padding: 0 5px;
13219. 15}
13220. 15.coverage .footer,
13221. 15.coverage .header {
13222. 15 padding: 20px;
13223. 15}
13224. 15.coverage .footer {
13225. 15 text-align: center;
13226. 15}
13227. 15.coverage .percentbar {
13228. 15 height: 12px;
13229. 15 margin: 2px 0;
13230. 15 min-width: 200px;
13231. 15 position: relative;
13232. 15 width: 100%;
13233. 15}
13234. 15.coverage .percentbar div {
13235. 15 height: 100%;
13236. 15 position: absolute;
13237. 15}
13238. 15.coverage .title {
13239. 15 font-size: large;
13240. 15 font-weight: bold;
13241. 15 margin-bottom: 10px;
13242. 15}
13243. 15
13244. 15/* css - coverage_report - color */
13245. 15.coverage td,
13246. 15.coverage th {
13247. 15 background: #fff;
13248. 15}
13249. 15.coverage .count,
13250. 15.coverage .coverageHigh {
13251. 15 background: #9d9;
13252. 15}
13253. 15.coverage .count {
13254. 15 color: #333;
13255. 15}
13256. 15.coverage .coverageIgnore {
13257. 15 background: #ccc;
13258. 15}
13259. 15.coverage .coverageLow,
13260. 15.coverage .ignore {
13261. 15 background: #ccc;
13262. 15}
13263. 15.coverage .uncovered {
13264. 15 background: #ebb;
13265. 15}
13266. 15.coverage .coverageMedium {
13267. 15 background: #fd7;
13268. 15}
13269. 15.coverage .footer,
13270. 15.coverage .header,
13271. 15.coverage .lineno {
13272. 15 background: #ddd;
13273. 15}
13274. 15.coverage .percentbar {
13275. 15 background: #999;
13276. 15}
13277. 15.coverage .percentbar div {
13278. 15 background: #666;
13279. 15}
13280. 15
13281. 15/* css - coverage_report - important */
13282. 15.coverage pre:hover span,
13283. 15.coverage tr:hover td {
13284. 15 background: #7d7;
13285. 15}
13286. 15.coverage pre:hover span.ignore,
13287. 15.coverage tr:hover td.coverageIgnore {
13288. 15 background: #ccc;
13289. 15}
13290. 15.coverage pre:hover span.uncovered,
13291. 15.coverage tr:hover td.coverageLow {
13292. 15 background: #f99;
13293. 15}
13294. 15</style>
13295. 15</head>
13296. 15<body class="coverage">
13297. 15<!-- header start -->
13298. 15<div class="header">
13299. 15<div class="title">V8 Coverage Report</div>
13300. 15<table>
13301. 15<thead>
13302. 15 <tr>
13303. 15 <th>Files covered</th>
13304. 15 <th>Lines</th>
13305. 15 <th>Remaining</th>
13306. 15 </tr>
13307. 15</thead>
13308. 15<tbody>
13309. 15 `).trim() + "\n";
13310. 8 if (modeIndex) {
13311. 8 padLines = String("(ignore) 100.00 %").length;
13312. 8 padPathname = 32;
13313. 8 fileList.unshift({
13314. 8 linesCovered: 0,
13315. 8 linesTotal: 0,
13316. 8 modeCoverageIgnoreFile: "",
13317. 8 pathname: "./"
13318. 8 });
13319. 8 fileList.slice(1).forEach(function ({
13320. 8 linesCovered,
13321. 8 linesTotal,
13322. 8 modeCoverageIgnoreFile,
13323. 8 pathname
13324. 8 }) {
13325. 8 if (!modeCoverageIgnoreFile) {
13326. 8 fileList[0].linesCovered += linesCovered;
13327. 8 fileList[0].linesTotal += linesTotal;
13328. 8 }
13329. 8 padPathname = Math.max(padPathname, pathname.length + 2);
13330. 8 padLines = Math.max(
13331. 8 padLines,
13332. 8 String(linesCovered + " / " + linesTotal).length
13333. 8 );
13334. 8 });
13335. 8 }
13336. 15 txtBorder = (
13337. 15 "+" + "-".repeat(padPathname + 2) + "+" +
13338. 15 "-".repeat(padLines + 2) + "+" +
13339. 15 "-".repeat(padLines + 2) + "+\n"
13340. 15 );
13341. 15 txt = "";
13342. 15 txt += "V8 Coverage Report\n";
13343. 15 txt += txtBorder;
13344. 15 txt += (
13345. 15 "| " + String("Files covered").padEnd(padPathname, " ") + " | " +
13346. 15 String("Lines").padStart(padLines, " ") + " | " +
13347. 15 String("Remaining").padStart(padLines, " ") + " |\n"
13348. 15 );
13349. 15 txt += txtBorder;
13350. 22 fileList.forEach(function ({
13351. 22 linesCovered,
13352. 22 linesTotal,
13353. 22 modeCoverageIgnoreFile,
13354. 22 pathname
13355. 22 }, ii) {
13356. 22 let coverageLevel;
13357. 22 let coveragePct;
13358. 22 let fill;
13359. 22 let str1;
13360. 22 let str2;
13361. 22 let xx1;
13362. 22 let xx2;
13363. 2 coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
13364. 22 coverageLevel = (
13365. 22 modeCoverageIgnoreFile
13366. 2 ? "coverageIgnore"
13367. 20 : coveragePct >= 8000
13368. 20 ? "coverageHigh"
13369. 20 : coveragePct >= 5000
13370. 20 ? "coverageMedium"
13371. 20 : "coverageLow"
13372. 22 );
13373. 22 coveragePct = Number(coveragePct / 100).toFixed(2);
13374. 15 if (modeIndex && ii === 0) {
13375. 8 fill = (
13376. 8
13377. 8// Badge-color rgb-red.
13378. 8
13379. 8 "#" + Math.round(
13380. 8 (100 - Number(coveragePct)) * 2.21
13381. 8 ).toString(16).padStart(2, "0") +
13382. 8
13383. 8// Badge-color rgb-green.
13384. 8
13385. 8 Math.round(
13386. 8 Number(coveragePct) * 2.21
13387. 8 ).toString(16).padStart(2, "0") +
13388. 8
13389. 8// Badge-color rgb-blue.
13390. 8
13391. 8 "00"
13392. 8 );
13393. 8 str1 = "coverage";
13394. 8 str2 = coveragePct + " %";
13395. 8 xx1 = 6 * str1.length + 20;
13396. 8 xx2 = 6 * str2.length + 20;
13397. 8
13398. 8// Fs - write coverage_badge.svg.
13399. 8
13400. 8 promiseList.push(fsWriteFileWithParents((
13401. 8 coverageDir + "coverage_badge.svg"
13402. 8 ), String(`
13403. 8<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
13404. 8<rect fill="#555" height="20" width="${xx1 + xx2}"/>
13405. 8<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
13406. 8<g
13407. 8 fill="#fff"
13408. 8 font-family="verdana, geneva, dejavu sans, sans-serif"
13409. 8 font-size="11"
13410. 8 font-weight="bold"
13411. 8 text-anchor="middle"
13412. 8>
13413. 8<text x="${0.5 * xx1}" y="14">${str1}</text>
13414. 8<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
13415. 8</g>
13416. 8</svg>
13417. 8 `).trim() + "\n"));
13418. 8 pathname = "";
13419. 8 }
13420. 22 txt += (
13421. 22 "| " +
13422. 22 String("./" + pathname).padEnd(padPathname, " ") + " | " +
13423. 22 String(
13424. 22 modeCoverageIgnoreFile + " " + coveragePct + " %"
13425. 22 ).padStart(padLines, " ") + " | " +
13426. 22 " ".repeat(padLines) + " |\n"
13427. 22 );
13428. 22 txt += (
13429. 22 "| " + "*".repeat(
13430. 22 Math.round(0.01 * coveragePct * padPathname)
13431. 22 ).padEnd(padPathname, "_") + " | " +
13432. 22 String(
13433. 22 linesCovered + " / " + linesTotal
13434. 22 ).padStart(padLines, " ") + " | " +
13435. 22 String(
13436. 22 (linesTotal - linesCovered) + " / " + linesTotal
13437. 22 ).padStart(padLines, " ") + " |\n"
13438. 22 );
13439. 22 txt += txtBorder;
13440. 22 pathname = htmlEscape(pathname);
13441. 22
13442. 22// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
13443. 22
13444. 22 html += String(`
13445. 22 <tr>
13446. 22 <td class="${coverageLevel}">
13447. 22 ${(
13448. 22 modeIndex
13449. 15 ? (
13450. 15 "<a href=\"" + (pathname || "index") + ".html\">. / " +
13451. 15 pathname + "</a><br>"
13452. 15 )
13453. 7 : (
13454. 7 "<a href=\"" +
13455. 7 "../".repeat(pathname.split("/").length - 1) +
13456. 7 "index.html\">. / </a>" +
13457. 7 pathname + "<br>"
13458. 7 )
13459. 22 )}
13460. 22 <div class="percentbar">
13461. 22 <div style="width: ${coveragePct}%;"></div>
13462. 22 </div>
13463. 22 </td>
13464. 22 <td style="text-align: right;">
13465. 22 ${modeCoverageIgnoreFile} ${coveragePct} %<br>
13466. 22 ${linesCovered} / ${linesTotal}
13467. 22 </td>
13468. 22 <td style="text-align: right;">
13469. 22 <br>
13470. 22 ${linesTotal - linesCovered} / ${linesTotal}
13471. 22 </td>
13472. 22 </tr>
13473. 22 `).trim() + "\n";
13474. 22 });
13475. 15 html += String(`
13476. 15</tbody>
13477. 15</table>
13478. 15</div>
13479. 15<!-- header end -->
13480. 15 `).trim() + "\n";
13481. 7 if (!modeIndex) {
13482. 7 html += String(`
13483. 7<!-- content start -->
13484. 7<div class="content">
13485. 7 `).trim() + "\n";
13486. 14269 lineList.forEach(function ({
13487. 14269 count,
13488. 14269 holeList,
13489. 14269 ignoreLine,
13490. 14269 line,
13491. 14269 startOffset
13492. 14269 }, ii) {
13493. 14269 let chunk;
13494. 14269 let inHole;
13495. 14269 let lineHtml;
13496. 14269 let lineId;
13497. 14269 lineHtml = "";
13498. 14269 lineId = "line_" + (ii + 1);
13499. 14269 switch (count) {
13500. 32 case -1:
13501. 13077 case 0:
13502. 13077 if (holeList.length === 0) {
13503. 13077 lineHtml += "</span>";
13504. 13077 lineHtml += "<span class=\"uncovered\">";
13505. 13077 lineHtml += htmlEscape(line);
13506. 13077 break;
13507. 13077 }
13508. 13077 line = line.split("").map(function (char) {
13509. 13077 return {
13510. 13077 char,
13511. 13077 isHole: undefined
13512. 13077 };
13513. 13077 });
13514. 13077 holeList.forEach(function ([
13515. 13077 aa, bb
13516. 13077 ]) {
13517. 13077 aa = Math.max(aa - startOffset, 0);
13518. 13077 bb = Math.min(bb - startOffset, line.length);
13519. 13077 while (aa < bb) {
13520. 13077 line[aa].isHole = true;
13521. 13077 aa += 1;
13522. 13077 }
13523. 13077 });
13524. 13077 chunk = "";
13525. 13077 line.forEach(function ({
13526. 13077 char,
13527. 13077 isHole
13528. 13077 }) {
13529. 13077 if (inHole !== isHole) {
13530. 13077 lineHtml += htmlEscape(chunk);
13531. 13077 lineHtml += (
13532. 13077 (isHole && ignoreLine)
13533. 13077 ? "</span><span class=\"ignore\">"
13534. 13077 : isHole
13535. 13077 ? "</span><span class=\"uncovered\">"
13536. 13077
13537. 13077// PR-510 - deadcode-dispelled - The branch below is live: a hole ending before
13538. 13077// end-of-line re-enters with <isHole> undefined, and that bare span closes it.
13539. 13077// Line coverage hides this, the condition above running either way:
13540. 13077//
13541. 13077// function aa(bb) {
13542. 13077// return bb && bb.cc;
13543. 13077// ^^^^^^^^ this hole ends at the ";", not at end-of-line
13544. 13077// }
13545. 13077// aa(0);
13546. 13077
13547. 13077 : "</span><span>"
13548. 13077 );
13549. 13077 chunk = "";
13550. 13077 inHole = isHole;
13551. 13077 }
13552. 13077 chunk += char;
13553. 13077 });
13554. 13077 lineHtml += htmlEscape(chunk);
13555. 13077 break;
13556. 1192 default:
13557. 1192 lineHtml += htmlEscape(line);
13558. 14269 }
13559. 14269 html += String(`
13560. 14269<pre>
13561. 14269<span class="lineno">
13562. 14269<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
13563. 14269</span>
13564. 14269<span class="count
13565. 14269 ${(
13566. 13077 (count <= 0 && ignoreLine)
13567. 7 ? "ignore"
13568. 14266 : count <= 0
13569. 14266 ? "uncovered"
13570. 14266 : ""
13571. 14269 )}"
13572. 14269>
13573. 13045${String(count || "-0").padStart(7, " ")}
13574. 14269</span>
13575. 14269<span>${lineHtml}</span>
13576. 14269</pre>
13577. 14269 `).replace((
13578. 14269 /\n/g
13579. 14269 ), "").trim() + "\n";
13580. 14269 });
13581. 7 html += String(`
13582. 7</div>
13583. 7<!-- content end -->
13584. 7 `).trim() + "\n";
13585. 7 }
13586. 15 html += String(`
13587. 15<div class="footer">
13588. 15 [
13589. 15 This document was created with
13590. 15 <a href="https://github.com/jslint-org/jslint">JSLint</a>
13591. 15 ]
13592. 15</div>
13593. 15</body>
13594. 15</html>
13595. 15 `).trim() + "\n";
13596. 15
13597. 15// Fs - write <file>.html.
13598. 15
13599. 15 promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
13600. 7 if (!modeIndex) {
13601. 7 return;
13602. 8 }
13603. 8
13604. 8// Fs - write coverage_report.txt.
13605. 8
13606. 8 consoleError("\n" + txt);
13607. 8 promiseList.push(fsWriteFileWithParents((
13608. 8 coverageDir + "coverage_report.txt"
13609. 8 ), txt));
13610. 8 }
13611. 9
13612. 9/*
13613. 9function sentinel() {}
13614. 9*/
13615. 9
13616. 9 await moduleFsInit();
13617. 1 consoleError = consoleError || console.error;
13618. 9 cwd = process.cwd().replace((
13619. 9 /\\/g
13620. 9 ), "/") + "/";
13621. 9
13622. 9// Init coverageDir.
13623. 9// Assert coverageDir is subdirectory of cwd.
13624. 9
13625. 9 assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
13626. 9
13627. 9// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
13628. 9// assertOrThrow(
13629. 9// pathnameRelativeCwd(coverageDir),
13630. 9// "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
13631. 9// );
13632. 9
13633. 9 coverageDir = modulePath.resolve(coverageDir).replace((
13634. 9 /\\/g
13635. 9 ), "/") + "/";
13636. 9
13637. 9 processArgv = processArgv.slice();
13638. 10 while (processArgv[0] && processArgv[0][0] === "-") {
13639. 3 processArgElem = processArgv.shift().split("=");
13640. 3 processArgElem[1] = processArgElem.slice(1).join("=");
13641. 3 switch (processArgElem[0]) {
13642. 3
13643. 3// PR-371 - Add cli-option `--exclude=...`.
13644. 3
13645. 3 case "--exclude":
13646. 3 excludeList.push(processArgElem[1]);
13647. 3 break;
13648. 3
13649. 3// PR-371 - Add cli-option `--include=...`
13650. 3
13651. 3 case "--include":
13652. 3 includeList.push(processArgElem[1]);
13653. 3 break;
13654. 3
13655. 3// PR-400
13656. 3// Disable default-coverage of directory `node_modules`,
13657. 3// but allow override with cli-option `--include-node-modules=1`.
13658. 3
13659. 3 case "--include-node-modules":
13660. 3 modeIncludeNodeModules = !(
13661. 3 /0|false|null|undefined/
13662. 3 ).test(processArgElem[1]);
13663. 3 break;
13664. 3 }
13665. 8 }
13666. 8
13667. 8// 1. Spawn node.js program <processArgv> with coverage
13668. 8
13669. 8 if (processArgv.length > 0) {
13670. 7
13671. 7// Remove old coverage-files.
13672. 7
13673. 7 await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
13674. 7 await Promise.all(Array.from(
13675. 7 await moduleFs.promises.readdir(coverageDir)
13676. 13 ).map(async function (file) {
13677. 13 if ((
13678. 13 /^coverage-\d+?-\d+?-\d+?\.json$/
13679. 7 ).test(file)) {
13680. 7 consoleError("rm file " + coverageDir + file);
13681. 7 await moduleFs.promises.unlink(coverageDir + file);
13682. 7 }
13683. 13 }));
13684. 7 exitCode = await new Promise(function (resolve) {
13685. 7 let processArgv0 = processArgv[0];
13686. 7
13687. 7// If win32 environment, then replace program npm with npm.cmd.
13688. 7// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
13689. 7
13690. 7 if (processArgv0 === "npm") {
13691. 7 processArgv0 = process.platform
13692. 7 .replace("win32", "npm.cmd")
13693. 7 .replace(process.platform, "npm");
13694. 7 }
13695. 7 moduleChildProcess.spawn(
13696. 7 processArgv0,
13697. 7 processArgv.slice(1),
13698. 7 {
13699. 7 env: {
13700. 7 ...process.env,
13701. 7 NODE_V8_COVERAGE: coverageDir
13702. 7 },
13703. 7
13704. 7// PR-465
13705. 7// https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
13706. 7// Node.js will now error with EINVAL if a .bat or .cmd file is passed to
13707. 7// child_process.spawn and child_process.spawnSync without the shell option set.
13708. 7
13709. 7 shell: (
13710. 7 processArgv0.endsWith(".bat") ||
13711. 7 processArgv0.endsWith(".cmd")
13712. 7 ),
13713. 7 stdio: ["ignore", 1, 2]
13714. 7 }
13715. 7 ).on("exit", resolve);
13716. 7 });
13717. 7 consoleError(
13718. 7 `v8CoverageReportCreate - program exited with exitCode=${exitCode}`
13719. 7 );
13720. 8 }
13721. 8
13722. 8// 2. Merge JSON v8-coverage-files in <coverageDir>.
13723. 8
13724. 8 consoleError("v8CoverageReportCreate - merging coverage files...");
13725. 8 v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
13726. 21 v8CoverageObj = v8CoverageObj.filter(function (file) {
13727. 21 return (
13728. 21 /^coverage-\d+?-\d+?-\d+?\.json$/
13729. 21 ).test(file);
13730. 21 });
13731. 8 v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
13732. 8 let data;
13733. 8 let pathnameDict = Object.create(null);
13734. 8 data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
13735. 8 data = JSON.parse(data);
13736. 478 data.result.forEach(function (scriptCov) {
13737. 478 let pathname = scriptCov.url;
13738. 478
13739. 478// Filter out internal coverages.
13740. 478
13741. 399 if (!pathname.startsWith("file:///")) {
13742. 399 return;
13743. 399 }
13744. 79
13745. 79// Normalize pathname.
13746. 79
13747. 79 pathname = moduleUrl.fileURLToPath(pathname);
13748. 79 pathname = modulePath.resolve(pathname).replace((
13749. 79 /\\/g
13750. 79 ), "/");
13751. 79
13752. 79// Filter files outside of cwd.
13753. 79
13754. 79 if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
13755. 72 return;
13756. 72 }
13757. 8
13758. 8// Normalize pathname relative to cwd.
13759. 8
13760. 8 pathname = pathname.slice(cwd.length);
13761. 8 scriptCov.url = pathname;
13762. 8 pathnameDict[pathname] = scriptCov;
13763. 8 });
13764. 8
13765. 8// PR-400 - Filter directory `node_modules`.
13766. 8
13767. 8 if (!modeIncludeNodeModules) {
13768. 8 excludeList.push("node_modules/");
13769. 8 }
13770. 8
13771. 8// PR-400 - Filter files by glob-patterns in excludeList, includeList.
13772. 8
13773. 8 data.result = globExclude({
13774. 8 excludeList,
13775. 8 includeList,
13776. 8 pathnameList: Object.keys(pathnameDict)
13777. 8 }).pathnameList.map(function (pathname) {
13778. 8 return pathnameDict[pathname];
13779. 8 });
13780. 8 return data;
13781. 8 }));
13782. 8
13783. 8// Merge v8CoverageObj.
13784. 8
13785. 8 v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
13786. 8
13787. 8// Debug v8CoverageObj.
13788. 8
13789. 8 await fsWriteFileWithParents(
13790. 8 coverageDir + "v8_coverage_merged.json",
13791. 8 JSON.stringify(v8CoverageObj, undefined, 1)
13792. 8 );
13793. 8
13794. 8// 3. Create html-coverage-reports in <coverageDir>.
13795. 8
13796. 8 consoleError("v8CoverageReportCreate - creating html-coverage-report...");
13797. 8 fileDict = Object.create(null);
13798. 8 await Promise.all(v8CoverageObj.result.map(async function ({
13799. 8 functions,
13800. 8 url: pathname
13801. 8 }) {
13802. 8 let ignoreBlock = false;
13803. 8 let lineList;
13804. 8 let linesCovered;
13805. 8 let linesTotal;
13806. 8 let source;
13807. 8 source = await moduleFs.promises.readFile(pathname, "utf8");
13808. 8 lineList = [{}];
13809. 8
13810. 8// PR-513 - One entry per line, "\r\n" being ONE terminator: /^.*$/gm also ends
13811. 8// a line at "\r", so a crlf file got an empty entry after every line.
13812. 8
13813. 8 source.replace((
13814. 8 /(?<![^\n\r])(?!(?<=\r)\n)[^\n\r]*/g
13815. 14269 ), function (line, startOffset) {
13816. 8 if (line === "/*coverage-disable*/") {
13817. 8 ignoreBlock = true;
13818. 8 }
13819. 14269 lineList[lineList.length - 1].endOffset = startOffset - 1;
13820. 14269 lineList.push({
13821. 14269 count: -1,
13822. 14269 endOffset: 0,
13823. 14269 holeList: [],
13824. 14269 ignoreLine: (
13825. 14266 ignoreBlock || line.endsWith("//coverage-ignore-line")
13826. 14269 ),
13827. 14269 line,
13828. 14269 startOffset
13829. 14269 });
13830. 8 if (line === "/*coverage-enable*/") {
13831. 8 ignoreBlock = false;
13832. 8 }
13833. 14269 return "";
13834. 14269 });
13835. 8 lineList.shift();
13836. 8 lineList[lineList.length - 1].endOffset = source.length;
13837. 48 functions.reverse().forEach(function ({
13838. 48 ranges
13839. 48 }) {
13840. 78 ranges.reverse().forEach(function ({
13841. 78 count,
13842. 78 endOffset,
13843. 78 startOffset
13844. 78 }, ii, list) {
13845. 784126 lineList.forEach(function (elem) {
13846. 784126 if (!(
13847. 784126 (
13848. 328082 elem.startOffset <= startOffset &&
13849. 328082 startOffset <= elem.endOffset
13850. 784048 ) || (
13851. 784048 elem.startOffset <= endOffset &&
13852. 784048 endOffset <= elem.endOffset
13853. 784048 ) || (
13854. 783980 startOffset <= elem.startOffset &&
13855. 783980 elem.endOffset <= endOffset
13856. 783980 )
13857. 756198 )) {
13858. 756198 return;
13859. 756198 }
13860. 27928
13861. 27928// Handle tree-root.
13862. 27928
13863. 27928 if (ii + 1 === list.length) {
13864. 27623 if (elem.count === -1) {
13865. 27623 elem.count = count;
13866. 27623 }
13867. 27623 return;
13868. 27623 }
13869. 305
13870. 305// Handle tree-children.
13871. 305
13872. 305 if (elem.count !== 0) {
13873. 199 elem.count = Math.max(count, elem.count);
13874. 305 }
13875. 305 if (count === 0) {
13876. 239 elem.count = 0;
13877. 239 elem.holeList.push([
13878. 239 startOffset, endOffset
13879. 239 ]);
13880. 239 }
13881. 784126 });
13882. 78 });
13883. 48 });
13884. 8 linesTotal = lineList.length;
13885. 8 linesCovered = 0;
13886. 14269 lineList.forEach(function ({
13887. 14269 count,
13888. 14269 ignoreLine
13889. 14269 }) {
13890. 13077 linesCovered += count > 0 || ignoreLine;
13891. 14269 });
13892. 8 await moduleFs.promises.mkdir((
13893. 8 modulePath.dirname(coverageDir + pathname)
13894. 8 ), {
13895. 8 recursive: true
13896. 8 });
13897. 8 fileDict[pathname] = {
13898. 8 lineList,
13899. 8 linesCovered,
13900. 8 linesTotal,
13901. 8 modeCoverageIgnoreFile: (
13902. 8 (
13903. 8 /^\/\*coverage-ignore-file\*\/$/m
13904. 8 ).test(source.slice(0, 65536))
13905. 8 ? "(ignore)"
13906. 8 : ""
13907. 8 ),
13908. 8 pathname
13909. 8 };
13910. 8 htmlRender({
13911. 8 fileList: [
13912. 8 fileDict[pathname]
13913. 8 ],
13914. 8 lineList,
13915. 8 pathname: coverageDir + pathname
13916. 8 });
13917. 8 }));
13918. 8 htmlRender({
13919. 8 fileList: Object.keys(fileDict).sort().map(function (pathname) {
13920. 8 return fileDict[pathname];
13921. 8 }),
13922. 8 modeIndex: true,
13923. 8 pathname: coverageDir + "index"
13924. 8 });
13925. 8 await Promise.all(promiseList);
13926. 8 assertOrThrow(
13927. 8 exitCode === 0,
13928. 8 "v8CoverageReportCreate - nonzero exitCode " + exitCode
13929. 8 );
13930. 8}
13931. 23
13932. 23/*
13933. 23function sentinel() {}
13934. 23*/
13935. 23
13936. 23// Export jslint as cjs/esm.
13937. 23
13938. 23jslint_export = Object.freeze(
13939. 23 Object.assign(
13940. 23 jslint,
13941. 23 {
13942. 23 assertErrorThrownAsync,
13943. 23 assertJsonEqual,
13944. 23 assertOrThrow,
13945. 23 debugInline,
13946. 23 fsWriteFileWithParents,
13947. 23 globExclude,
13948. 23 htmlEscape,
13949. 23 jslint,
13950. 23 jslint_apidoc,
13951. 23 jslint_charset_ascii,
13952. 23 jslint_cli,
13953. 23 jslint_edition,
13954. 23 jslint_phase1_split,
13955. 23 jslint_phase2_lex,
13956. 23 jslint_phase3_parse,
13957. 23 jslint_phase4_walk,
13958. 23 jslint_phase5_whitage,
13959. 23 jslint_phase6_autofix,
13960. 23 jslint_report,
13961. 23 jstestDescribe,
13962. 23 jstestIt,
13963. 23 moduleFsInit,
13964. 23 noop,
13965. 23 objectDeepCopyWithKeysSorted,
13966. 23 v8CoverageListMerge,
13967. 23 v8CoverageReportCreate
13968. 23 }
13969. 23 )
13970. 23);
13971. 23// module.exports = jslint_export; // Export jslint as cjs.
13972. 23export default Object.freeze(jslint_export); // Export jslint as esm.
13973. 23jslint_import_meta_url = import.meta.url;
13974. 23
13975. 23// Run jslint_cli.
13976. 23jslint_cli({});
13977. 23