├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── release-please.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile.js ├── README.md ├── eslint.config.js ├── lib ├── definition.js ├── index.js ├── pattern-visitor.js ├── reference.js ├── referencer.js ├── scope-manager.js ├── scope.js ├── variable.js └── version.js ├── package.json ├── rollup.config.js ├── tests ├── arguments.js ├── catch-scope.js ├── child-visitor-keys.js ├── class-fields.js ├── class-static-blocks.js ├── commonjs.cjs ├── es6-arrow-function-expression.js ├── es6-block-scope.js ├── es6-catch.js ├── es6-class.js ├── es6-default-parameters.js ├── es6-destructuring-assignments.js ├── es6-export.js ├── es6-import.js ├── es6-iteration-scope.js ├── es6-new-target.js ├── es6-object.js ├── es6-rest-args.js ├── es6-super.js ├── es6-switch.js ├── es6-template-literal.js ├── export-star-as-ns-from-source.js ├── fallback.js ├── function-expression-name.js ├── get-declared-variables.js ├── global-increment.js ├── implicit-global-reference.js ├── implied-strict.js ├── label.js ├── nodejs-scope.js ├── object-expression.js ├── optimistic.js ├── references.js ├── typescript.js ├── use-strict.js ├── util │ ├── ecma-version.js │ └── espree.js └── with-scope.js └── tools └── update-version.js /.editorconfig: -------------------------------------------------------------------------------- 1 | ; EditorConfig file: https://EditorConfig.org 2 | ; Install the "EditorConfig" plugin into your editor to use 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | 14 | [package.json] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | lint: 11 | name: Lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Setup Node.js 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: "lts/*" 19 | - name: Install dependencies 20 | run: npm install 21 | - name: Lint files 22 | run: npm run lint 23 | test: 24 | name: Test 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest] 28 | node: [22.x, 21.x, 20.x, 18.x, "18.18.0"] 29 | include: 30 | - os: windows-latest 31 | node: "lts/*" 32 | - os: macOS-latest 33 | node: "lts/*" 34 | runs-on: ${{ matrix.os }} 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: actions/setup-node@v4 38 | with: 39 | node-version: ${{ matrix.node }} 40 | - name: Install dependencies 41 | run: npm install 42 | - name: Run tests 43 | run: npm test 44 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: release-please 6 | jobs: 7 | release-please: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | id-token: write 13 | steps: 14 | - uses: google-github-actions/release-please-action@v3 15 | id: release 16 | with: 17 | release-type: node 18 | package-name: 'eslint-scope' 19 | pull-request-title-pattern: 'chore: release ${version}' 20 | changelog-types: > 21 | [ 22 | { "type": "feat", "section": "Features", "hidden": false }, 23 | { "type": "fix", "section": "Bug Fixes", "hidden": false }, 24 | { "type": "perf", "section": "Performance Improvements", "hidden": false } 25 | ] 26 | - uses: actions/checkout@v4 27 | if: ${{ steps.release.outputs.release_created }} 28 | - uses: actions/setup-node@v4 29 | with: 30 | node-version: lts/* 31 | registry-url: https://registry.npmjs.org 32 | if: ${{ steps.release.outputs.release_created }} 33 | - run: npm install 34 | if: ${{ steps.release.outputs.release_created }} 35 | - run: npm publish --provenance 36 | env: 37 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 38 | if: ${{ steps.release.outputs.release_created }} 39 | - run: 'npx @humanwhocodes/tweet "eslint-scope ${{ steps.release.outputs.tag_name }} has been released: ${{ steps.release.outputs.html_url }}"' 40 | if: ${{ steps.release.outputs.release_created }} 41 | env: 42 | TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }} 43 | TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }} 44 | TWITTER_ACCESS_TOKEN_KEY: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }} 45 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} 46 | - run: 'npx @humanwhocodes/toot "eslint-scope ${{ steps.release.outputs.tag_name }} has been released: ${{ steps.release.outputs.html_url }}"' 47 | if: ${{ steps.release.outputs.release_created }} 48 | env: 49 | MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} 50 | MASTODON_HOST: ${{ secrets.MASTODON_HOST }} 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | coverage/ 3 | build/ 4 | npm-debug.log 5 | .DS_Store 6 | .idea 7 | *.iml 8 | /.vscode 9 | .sublimelinterrc 10 | .eslint-release-info.json 11 | dist 12 | *.tgz 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [8.0.2](https://github.com/eslint/eslint-scope/compare/v8.0.1...v8.0.2) (2024-07-08) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * `Definition#name` in catch patterns should be `Identifier` node ([#127](https://github.com/eslint/eslint-scope/issues/127)) ([8082caa](https://github.com/eslint/eslint-scope/commit/8082caa1a0f9aae0894a0a01fef9efa7a5e509f6)) 9 | 10 | ## [8.0.1](https://github.com/eslint/eslint-scope/compare/v8.0.0...v8.0.1) (2024-03-20) 11 | 12 | 13 | ### Documentation 14 | 15 | * document Security Policy ([#122](https://github.com/eslint/eslint-scope/issues/122)) ([0d03700](https://github.com/eslint/eslint-scope/commit/0d0370035c3fcd3846bcfed25e2de1c90c204cc8)) 16 | 17 | 18 | ### Chores 19 | 20 | * switch to eslint flat config ([#121](https://github.com/eslint/eslint-scope/issues/121)) ([59b1606](https://github.com/eslint/eslint-scope/commit/59b160624bd725a1254024bcbbd28b7529c04c64)) 21 | * upgrade espree@10.0.1, eslint-visitor-keys@4.0.0 ([#119](https://github.com/eslint/eslint-scope/issues/119)) ([6f95a94](https://github.com/eslint/eslint-scope/commit/6f95a9406e321ba519bbf635ebb8b1ae4b8861e7)) 22 | 23 | ## [8.0.0](https://github.com/eslint/eslint-scope/compare/v7.2.2...v8.0.0) (2024-01-04) 24 | 25 | 26 | ### ⚠ BREAKING CHANGES 27 | 28 | * use ESTree `directive` property when searching for `"use strict"` ([#118](https://github.com/eslint/eslint-scope/issues/118)) 29 | * class `extends` is evaluated in the class scope ([#116](https://github.com/eslint/eslint-scope/issues/116)) 30 | * Require Node.js ^18.18.0 || ^20.9.0 || >=21.1.0 ([#115](https://github.com/eslint/eslint-scope/issues/115)) 31 | 32 | ### Features 33 | 34 | * Require Node.js ^18.18.0 || ^20.9.0 || >=21.1.0 ([#115](https://github.com/eslint/eslint-scope/issues/115)) ([ed67857](https://github.com/eslint/eslint-scope/commit/ed678573aca7b00815ecb3c5dc4eee913b53a162)) 35 | * use ESTree `directive` property when searching for `"use strict"` ([#118](https://github.com/eslint/eslint-scope/issues/118)) ([23fe81f](https://github.com/eslint/eslint-scope/commit/23fe81f5861ade17a2f17f9518fdde376dd2395f)) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * class `extends` is evaluated in the class scope ([#116](https://github.com/eslint/eslint-scope/issues/116)) ([42ef7a9](https://github.com/eslint/eslint-scope/commit/42ef7a995771f0700fc6af7eee03bab977f272c6)) 41 | 42 | 43 | ### Documentation 44 | 45 | * Update README with analyze() options ([#111](https://github.com/eslint/eslint-scope/issues/111)) ([2122fdb](https://github.com/eslint/eslint-scope/commit/2122fdb237cc0c115cd2473f383f741b1f055791)), closes [#110](https://github.com/eslint/eslint-scope/issues/110) 46 | 47 | 48 | ### Chores 49 | 50 | * Remove add-to-triage ([#106](https://github.com/eslint/eslint-scope/issues/106)) ([dc75851](https://github.com/eslint/eslint-scope/commit/dc75851b92b47eb37ed617448c0291129db676e1)) 51 | * run tests in Node.js 21 ([#109](https://github.com/eslint/eslint-scope/issues/109)) ([957748e](https://github.com/eslint/eslint-scope/commit/957748e7fb741dd23f521af0c124ce6da0848997)) 52 | * standardize npm script names ([#105](https://github.com/eslint/eslint-scope/issues/105)) ([115ded3](https://github.com/eslint/eslint-scope/commit/115ded3cb6f768a37f0dcb17bb16e2299849e16f)) 53 | 54 | ## [7.2.2](https://github.com/eslint/eslint-scope/compare/v7.2.1...v7.2.2) (2023-07-27) 55 | 56 | 57 | ### Chores 58 | 59 | * Add PRs to triage ([#104](https://github.com/eslint/eslint-scope/issues/104)) ([a4dd888](https://github.com/eslint/eslint-scope/commit/a4dd8884726758ed513210a6b537105a07e8bf70)) 60 | * generate provenance statements when release ([#102](https://github.com/eslint/eslint-scope/issues/102)) ([a27ce6b](https://github.com/eslint/eslint-scope/commit/a27ce6bbf70d7ba5af763a4d1650bfd87eee8136)) 61 | 62 | ## [7.2.1](https://github.com/eslint/eslint-scope/compare/v7.2.0...v7.2.1) (2023-05-31) 63 | 64 | 65 | ### Chores 66 | 67 | * run tests on Node.js v20 ([#97](https://github.com/eslint/eslint-scope/issues/97)) ([675f7de](https://github.com/eslint/eslint-scope/commit/675f7de78c312546441fa9b204734c26376710f7)) 68 | * set up release-please ([#99](https://github.com/eslint/eslint-scope/issues/99)) ([6bc2619](https://github.com/eslint/eslint-scope/commit/6bc2619fff2aa401fe43d3fda60e0c127d2d39a8)) 69 | 70 | v7.2.0 - April 13, 2023 71 | 72 | * [`70c8db1`](https://github.com/eslint/eslint-scope/commit/70c8db16962830f20e27765cd4d1fd0e29b93c08) feat: Add isGlobalReturn method on scopeManager. (#96) (Nicholas C. Zakas) 73 | * [`3dbad80`](https://github.com/eslint/eslint-scope/commit/3dbad80d98e5bb2453423dc3882500a7d76d6259) chore: add triage action (#95) (Milos Djermanovic) 74 | * [`34ffedc`](https://github.com/eslint/eslint-scope/commit/34ffedc9645f3e5bf2111f766931efb0ff33040f) ci: add Node v19 (#94) (Milos Djermanovic) 75 | * [`4c00534`](https://github.com/eslint/eslint-scope/commit/4c005347cd556b4fa97ba0b626decdd0fce95962) ci: update Github actions (#93) (Deepshika S) 76 | * [`6c8ccf2`](https://github.com/eslint/eslint-scope/commit/6c8ccf223952daff78295907316d8d8c1e93cf89) chore: add funding field (#92) (Deepshika S) 77 | * [`a8811b8`](https://github.com/eslint/eslint-scope/commit/a8811b89b93a8b6bb6ac7089d893d5686dabbeb8) build: add node v18 (#91) (唯然) 78 | * [`25abacf`](https://github.com/eslint/eslint-scope/commit/25abacffe690b6141f19d59dc8c0e09599671508) docs: add badges (#89) (Milos Djermanovic) 79 | 80 | v7.1.1 - February 11, 2022 81 | 82 | * [`8938109`](https://github.com/eslint/eslint-scope/commit/89381090cef60d8d47aeba111e04f859e063ae41) chore: upgrade espree@9.3.1 eslint-visitor-keys@3.3.0 (#88) (Milos Djermanovic) 83 | * [`4e1d24c`](https://github.com/eslint/eslint-scope/commit/4e1d24ca4a747c14b37f059543cf08d1e1820b2d) fix: ignore `"use strict"` directives in ES3 (#87) (Milos Djermanovic) 84 | * [`ceb8bdd`](https://github.com/eslint/eslint-scope/commit/ceb8bdd2cc31f67255e37a961096f9e3320abac6) ci: use node v16 (#84) (Nitin Kumar) 85 | * [`62e147b`](https://github.com/eslint/eslint-scope/commit/62e147be60c1eb84a40c1918913755acbc2d3a3d) test: add tests with year-based `ecmaVersion` (#83) (Milos Djermanovic) 86 | 87 | v7.1.0 - November 20, 2021 88 | 89 | * [`d756f1e`](https://github.com/eslint/eslint-scope/commit/d756f1ea974093c3ed7121d17f858254036b9690) feat: Add sourceType:commonjs support (#81) (Nicholas C. Zakas) 90 | 91 | v7.0.0 - November 16, 2021 92 | 93 | * [`22a55c0`](https://github.com/eslint/eslint-scope/commit/22a55c01d1a28fd3ffd45c8818b49e65bd3e5005) feat!: support class static blocks (#80) (Milos Djermanovic) 94 | * [`4aafb61`](https://github.com/eslint/eslint-scope/commit/4aafb616212adc39af37064932da912bdc7d9226) build: upgrade eslint-release to v3.2.0 to support conventional commits (#79) (Milos Djermanovic) 95 | * [`263c762`](https://github.com/eslint/eslint-scope/commit/263c762432c5a3995e30fa814d02b0ed358b0e68) Build: add node v17 (#76) (唯然) 96 | 97 | v6.0.0 - July 23, 2021 98 | 99 | * [`4ee1d80`](https://github.com/eslint/eslint-scope/commit/4ee1d80ce7dab961d9a158bc664d781bb663b570) Fix: Ensure correct version in package (#73) (Nicholas C. Zakas) 100 | * [`82a7e6d`](https://github.com/eslint/eslint-scope/commit/82a7e6d9de8f4fca48e99779e9573dd46adbc18c) Breaking: Switch to ESM (fixes #70) (#71) (Brett Zamir) 101 | * [`0b4a5f1`](https://github.com/eslint/eslint-scope/commit/0b4a5f132fb65520eee31bcd166078656b6e158e) Update: support class fields (refs eslint/eslint#14343) (#69) (Toru Nagashima) 102 | * [`39f8cfc`](https://github.com/eslint/eslint-scope/commit/39f8cfc026d9b9b7c02e07368323350e74698f29) Chore: upgrade estraverse to version 5 (#68) (Rouven Weßling) 103 | * [`ae27ff3`](https://github.com/eslint/eslint-scope/commit/ae27ff3692ab13cf62075b8659f0e17dfa44acd1) Docs: Add range to espree options in README (fixes #66) (#67) (Alan Liang) 104 | 105 | v5.1.1 - September 12, 2020 106 | 107 | * [`9b528d7`](https://github.com/eslint/eslint-scope/commit/9b528d778c381718c12dabfb7f1c0e0dc6b36e49) Upgrade: esrecurse version to ^4.3.0 (#64) (Timofey Kachalov) 108 | * [`f758bbc`](https://github.com/eslint/eslint-scope/commit/f758bbc3d49b9b9ea2289a5d6a6bba8dcf2c4903) Chore: fix definiton -> definition typo in comments (#63) (Kevin Kirsche) 109 | * [`7513734`](https://github.com/eslint/eslint-scope/commit/751373473375b3f2edc4eaf1c8d2763d8435bb72) Chore: move to GitHub Actions (#62) (Kai Cataldo) 110 | 111 | v5.1.0 - June 4, 2020 112 | 113 | * [`d4a3764`](https://github.com/eslint/eslint-scope/commit/d4a376434b16289c1a428d7e304576e997520873) Update: support new export syntax (#56) (Toru Nagashima) 114 | 115 | v5.0.0 - July 20, 2019 116 | 117 | * [`e9fa22e`](https://github.com/eslint/eslint-scope/commit/e9fa22ea412c26cf2761fa98af7e715644bdb464) Upgrade: update dependencies after dropping support for Node <8 (#53) (Kai Cataldo) 118 | * [`ee9f7c1`](https://github.com/eslint/eslint-scope/commit/ee9f7c12721aa195ba7e0e69551f49bfdb479951) Breaking: drop support for Node v6 (#54) (Kai Cataldo) 119 | 120 | v4.0.3 - March 15, 2019 121 | 122 | * [`299df64`](https://github.com/eslint/eslint-scope/commit/299df64bdafb30b4d9372e4b7af0cf51a3818c4a) Fix: arrow function scope strictness (take 2) (#52) (futpib) 123 | 124 | v4.0.2 - March 1, 2019 125 | 126 | * [`c925600`](https://github.com/eslint/eslint-scope/commit/c925600a684ae0f71b96f85339437a43b4d50d99) Revert "Fix: Arrow function scope strictness (fixes #49) (#50)" (#51) (Teddy Katz) 127 | 128 | v4.0.1 - March 1, 2019 129 | 130 | * [`2533966`](https://github.com/eslint/eslint-scope/commit/2533966faf317df5a3847fab937ba462c16808b8) Fix: Arrow function scope strictness (fixes #49) (#50) (futpib) 131 | * [`0cbeea5`](https://github.com/eslint/eslint-scope/commit/0cbeea51dfb66ab88ea34b0e3b4ad5e6cc210f2f) Chore: add supported Node.js versions to CI (#47) (Kai Cataldo) 132 | * [`b423057`](https://github.com/eslint/eslint-scope/commit/b42305760638b8edf4667acf1445e450869bd983) Upgrade: eslint-release@1.0.0 (#46) (Teddy Katz) 133 | 134 | v4.0.0 - June 21, 2018 135 | 136 | 137 | 138 | v4.0.0-rc.0 - June 9, 2018 139 | 140 | * 3b919b8 Build: Adding rc release script to package.json (#38) (Kevin Partington) 141 | * 137732a Chore: avoid creating package-lock.json files (#37) (Teddy Katz) 142 | 143 | v4.0.0-alpha.0 - April 27, 2018 144 | 145 | * 7cc3769 Upgrade: eslint-release ^0.11.1 (#36) (Teddy Katz) 146 | * c9f6967 Breaking: remove TDZScope (refs eslint/eslint#10245) (#35) (Toru Nagashima) 147 | * 982a71f Fix: wrong resolution about default parameters (#33) (Toru Nagashima) 148 | * 57889f1 Docs: Remove extra header line from LICENSE (#32) (Gyandeep Singh) 149 | 150 | v3.7.1 - April 12, 2017 151 | 152 | * ced6262 Fix: restore previous Scope API exports from escope (#31) (Vitor Balocco) 153 | * 5c3d966 Fix: Remove and Modify tests that contain invalid ES6 syntax (#29) (Reyad Attiyat) 154 | 155 | v3.7.0 - March 17, 2017 156 | 157 | * 9e27835 Chore: Add files section to package.json (#24) (Ilya Volodin) 158 | * 3e4d123 Upgrade: eslint-config-eslint to 4.0.0 (#21) (Teddy Katz) 159 | * 38c50fb Chore: Rename src to lib and test to tests (#20) (Corbin Uselton) 160 | * f4cd920 Chore: Remove esprima (#19) (Corbin Uselton) 161 | * f81fad5 Revert "Chore: Remove esprima" (#18) (James Henry) 162 | * 31b0085 Chore: Remove es6-map and es6-weakmap as they are included in node4 (#10) (#13) (Corbin Uselton) 163 | * 12a1ca1 Add Makefile.js and eslint (#15) (Reyad Attiyat) 164 | * 7d23f8e Chore: Remove es6-map and es6-weakmap as they are included in node4 (#10) (Corbin Uselton) 165 | * 019441e Chore: Convert to ES6 that is supported on Node 4, commonjs modules and remove Babel (#14) (Corbin Uselton) 166 | * c647f65 Update: Add check for node.body in referencer (#2) (Corbin Uselton) 167 | * eb5c9db Remove browserify and jsdoc (#12) (Corbin Uselton) 168 | * cf38df0 Chore: Update README.md (#3) (James Henry) 169 | * 8a142ca Chore: Add eslint-release scripts (#6) (James Henry) 170 | * e60d8cb Chore: Remove unused bower.json (#5) (James Henry) 171 | * 049c545 Chore: Fix tests for eslint-scope (#4) (James Henry) 172 | * f026aab Chore: Update package.json for eslint fork (#1) (James Henry) 173 | * a94d281 Chore: Update license with JSF copyright (Nicholas C. Zakas) 174 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Project license: \ 2 | 3 | - You will only Submit Contributions where You have authored 100% of the content. 4 | - You will only Submit Contributions to which You have the necessary rights. This means that if You are employed You have received the necessary permissions from Your employer to make the Contributions. 5 | - Whatever content You Contribute will be provided under the Project License. 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation 2 | Copyright (C) 2012-2013 Yusuke Suzuki (twitter: @Constellation) and other contributors. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Makefile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Build file 3 | * @author nzakas 4 | * @copyright jQuery Foundation and other contributors, https://jquery.org/ 5 | * MIT License 6 | */ 7 | 8 | //------------------------------------------------------------------------------ 9 | // Requirements 10 | //------------------------------------------------------------------------------ 11 | 12 | import path from "path"; 13 | import { fileURLToPath } from "url"; 14 | 15 | import "shelljs/make.js"; 16 | import checker from "npm-license"; 17 | 18 | const dirname = path.dirname(fileURLToPath(import.meta.url)); 19 | 20 | // `shelljs/make.js` global command to unset any `set('-e')` (to exit upon 21 | // first error) 22 | set("+e"); 23 | 24 | //------------------------------------------------------------------------------ 25 | // Settings 26 | //------------------------------------------------------------------------------ 27 | 28 | const OPEN_SOURCE_LICENSES = [ 29 | /MIT/u, /BSD/u, /Apache/u, /ISC/u, /WTF/u, /Public Domain/u 30 | ]; 31 | 32 | //------------------------------------------------------------------------------ 33 | // Data 34 | //------------------------------------------------------------------------------ 35 | 36 | const NODE = "node", 37 | NODE_MODULES = "./node_modules", 38 | 39 | // Utilities - intentional extra space at the end of each string 40 | MOCHA = `${NODE_MODULES}/mocha/bin/_mocha `, 41 | 42 | // If switching back to Istanbul when may be working with ESM 43 | // ISTANBUL = `${NODE} ${NODE_MODULES}/istanbul/lib/cli.js `, 44 | C8 = `${NODE} ${NODE_MODULES}/c8/bin/c8.js`, 45 | 46 | // Files 47 | TEST_FILES = "tests/*.js", 48 | CJS_TEST_FILES = "tests/*.cjs"; 49 | 50 | //------------------------------------------------------------------------------ 51 | // Tasks 52 | //------------------------------------------------------------------------------ 53 | 54 | target.all = function() { 55 | target.test(); 56 | }; 57 | 58 | target.test = function() { 59 | let errors = 0; 60 | let lastReturn = exec(`${NODE} ${MOCHA} -- -R progress -c ${CJS_TEST_FILES}`); 61 | 62 | if (lastReturn.code !== 0) { 63 | errors++; 64 | } 65 | 66 | lastReturn = exec(`${C8} ${MOCHA} -- -R progress -c ${TEST_FILES}`); 67 | 68 | if (lastReturn.code !== 0) { 69 | errors++; 70 | } 71 | 72 | if (errors) { 73 | exit(1); 74 | } 75 | 76 | target.checkLicenses(); 77 | }; 78 | 79 | target.checkLicenses = function() { 80 | 81 | /** 82 | * Returns true if the given dependency's licenses are all permissable for use in OSS 83 | * @param {Object} dependency object containing the name and licenses of the given dependency 84 | * @returns {boolean} is permissable dependency 85 | */ 86 | function isPermissible(dependency) { 87 | const licenses = dependency.licenses; 88 | 89 | if (Array.isArray(licenses)) { 90 | return licenses.some(license => isPermissible({ 91 | name: dependency.name, 92 | licenses: license 93 | })); 94 | } 95 | 96 | return OPEN_SOURCE_LICENSES.some(license => license.test(licenses)); 97 | } 98 | 99 | echo("Validating licenses"); 100 | 101 | checker.init({ 102 | start: dirname 103 | }, deps => { 104 | const impermissible = Object.keys(deps).map(dependency => ({ 105 | name: dependency, 106 | licenses: deps[dependency].licenses 107 | })).filter(dependency => !isPermissible(dependency)); 108 | 109 | if (impermissible.length) { 110 | impermissible.forEach(dependency => { 111 | console.error("%s license for %s is impermissible.", 112 | dependency.licenses, 113 | dependency.name); 114 | }); 115 | exit(1); 116 | } 117 | }); 118 | }; 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://img.shields.io/npm/v/eslint-scope.svg)](https://www.npmjs.com/package/eslint-scope) 2 | [![Downloads](https://img.shields.io/npm/dm/eslint-scope.svg)](https://www.npmjs.com/package/eslint-scope) 3 | [![Build Status](https://github.com/eslint/eslint-scope/workflows/CI/badge.svg)](https://github.com/eslint/eslint-scope/actions) 4 | 5 | # ESLint Scope 6 | 7 | > [!Important] 8 | > Active development for this project has moved to https://github.com/eslint/js. Please open issues/PRs there. 9 | 10 | ESLint Scope is the [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm) scope analyzer used in ESLint. It is a fork of [escope](http://github.com/estools/escope). 11 | 12 | ## Install 13 | 14 | ``` 15 | npm i eslint-scope --save 16 | ``` 17 | 18 | ## 📖 Usage 19 | 20 | To use in an ESM file: 21 | 22 | ```js 23 | import * as eslintScope from 'eslint-scope'; 24 | ``` 25 | 26 | To use in a CommonJS file: 27 | 28 | ```js 29 | const eslintScope = require('eslint-scope'); 30 | ``` 31 | 32 | In order to analyze scope, you'll need to have an [ESTree](https://github.com/estree/estree) compliant AST structure to run it on. The primary method is `eslintScope.analyze()`, which takes two arguments: 33 | 34 | 1. `ast` - the ESTree-compliant AST structure to analyze. 35 | 2. `options` (optional) - Options to adjust how the scope is analyzed, including: 36 | * `ignoreEval` (default: `false`) - Set to `true` to ignore all `eval()` calls (which would normally create scopes). 37 | * `nodejsScope` (default: `false`) - Set to `true` to create a top-level function scope needed for CommonJS evaluation. 38 | * `impliedStrict` (default: `false`) - Set to `true` to evaluate the code in strict mode even outside of modules and without `"use strict"`. 39 | * `ecmaVersion` (default: `5`) - The version of ECMAScript to use to evaluate the code. 40 | * `sourceType` (default: `"script"`) - The type of JavaScript file to evaluate. Change to `"module"` for ECMAScript module code. 41 | * `childVisitorKeys` (default: `null`) - An object with visitor key information (like [`eslint-visitor-keys`](https://github.com/eslint/eslint-visitor-keys)). Without this, `eslint-scope` finds child nodes to visit algorithmically. Providing this option is a performance enhancement. 42 | * `fallback` (default: `"iteration"`) - The strategy to use when `childVisitorKeys` is not specified. May be a function. 43 | 44 | Example: 45 | 46 | ```js 47 | import * as eslintScope from 'eslint-scope'; 48 | import * as espree from 'espree'; 49 | import estraverse from 'estraverse'; 50 | 51 | const options = { 52 | ecmaVersion: 2022, 53 | sourceType: "module" 54 | }; 55 | 56 | const ast = espree.parse(code, { range: true, ...options }); 57 | const scopeManager = eslintScope.analyze(ast, options); 58 | 59 | const currentScope = scopeManager.acquire(ast); // global scope 60 | 61 | estraverse.traverse(ast, { 62 | enter (node, parent) { 63 | // do stuff 64 | 65 | if (/Function/.test(node.type)) { 66 | currentScope = scopeManager.acquire(node); // get current function scope 67 | } 68 | }, 69 | leave(node, parent) { 70 | if (/Function/.test(node.type)) { 71 | currentScope = currentScope.upper; // set to parent scope 72 | } 73 | 74 | // do stuff 75 | } 76 | }); 77 | ``` 78 | 79 | ## Contributing 80 | 81 | Issues and pull requests will be triaged and responded to as quickly as possible. We operate under the [ESLint Contributor Guidelines](http://eslint.org/docs/developer-guide/contributing), so please be sure to read them before contributing. If you're not sure where to dig in, check out the [issues](https://github.com/eslint/eslint-scope/issues). 82 | 83 | ## Security Policy 84 | 85 | We work hard to ensure that ESLint Scope is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/eslint/.github/blob/master/SECURITY.md). 86 | 87 | ## Build Commands 88 | 89 | * `npm test` - run all linting and tests 90 | * `npm run lint` - run all linting 91 | 92 | ## License 93 | 94 | ESLint Scope is licensed under a permissive BSD 2-clause license. 95 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import eslintConfigESLint from "eslint-config-eslint"; 2 | import globals from "globals"; 3 | import eslintPluginChaiFriendly from "eslint-plugin-chai-friendly"; 4 | 5 | export default [ 6 | { 7 | ignores: [ 8 | "dist/", 9 | "coverage/" 10 | ] 11 | }, 12 | ...eslintConfigESLint, 13 | { 14 | files: ["lib/**"], 15 | rules: { 16 | "no-underscore-dangle": "off" 17 | } 18 | }, 19 | { 20 | files: ["tests/**"], 21 | ignores: ["tests/util/**"], 22 | languageOptions: { 23 | globals: { 24 | ...globals.mocha 25 | } 26 | }, 27 | plugins: { 28 | "chai-friendly": eslintPluginChaiFriendly 29 | }, 30 | rules: { 31 | "no-unused-expressions": "off", 32 | "chai-friendly/no-unused-expressions": "error" 33 | } 34 | }, 35 | { 36 | files: ["Makefile.js"], 37 | languageOptions: { 38 | globals: { 39 | ...globals.shelljs 40 | } 41 | }, 42 | rules: { 43 | "no-console": "off" 44 | } 45 | } 46 | ]; 47 | -------------------------------------------------------------------------------- /lib/definition.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Yusuke Suzuki 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | import Variable from "./variable.js"; 26 | 27 | /** 28 | * @constructor Definition 29 | */ 30 | class Definition { 31 | constructor(type, name, node, parent, index, kind) { 32 | 33 | /** 34 | * @member {string} Definition#type - type of the occurrence (e.g. "Parameter", "Variable", ...). 35 | */ 36 | this.type = type; 37 | 38 | /** 39 | * @member {espree.Identifier} Definition#name - the identifier AST node of the occurrence. 40 | */ 41 | this.name = name; 42 | 43 | /** 44 | * @member {espree.Node} Definition#node - the enclosing node of the identifier. 45 | */ 46 | this.node = node; 47 | 48 | /** 49 | * @member {espree.Node?} Definition#parent - the enclosing statement node of the identifier. 50 | */ 51 | this.parent = parent; 52 | 53 | /** 54 | * @member {number?} Definition#index - the index in the declaration statement. 55 | */ 56 | this.index = index; 57 | 58 | /** 59 | * @member {string?} Definition#kind - the kind of the declaration statement. 60 | */ 61 | this.kind = kind; 62 | } 63 | } 64 | 65 | /** 66 | * @constructor ParameterDefinition 67 | */ 68 | class ParameterDefinition extends Definition { 69 | constructor(name, node, index, rest) { 70 | super(Variable.Parameter, name, node, null, index, null); 71 | 72 | /** 73 | * Whether the parameter definition is a part of a rest parameter. 74 | * @member {boolean} ParameterDefinition#rest 75 | */ 76 | this.rest = rest; 77 | } 78 | } 79 | 80 | export { 81 | ParameterDefinition, 82 | Definition 83 | }; 84 | 85 | /* vim: set sw=4 ts=4 et tw=80 : */ 86 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012-2014 Yusuke Suzuki 3 | Copyright (C) 2013 Alex Seville 4 | Copyright (C) 2014 Thiago de Arruda 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /** 28 | * Escope (escope) is an ECMAScript 30 | * scope analyzer extracted from the esmangle project. 32 | *

33 | * escope finds lexical scopes in a source program, i.e. areas of that 34 | * program where different occurrences of the same identifier refer to the same 35 | * variable. With each scope the contained variables are collected, and each 36 | * identifier reference in code is linked to its corresponding variable (if 37 | * possible). 38 | *

39 | * escope works on a syntax tree of the parsed source code which has 40 | * to adhere to the 42 | * Mozilla Parser API. E.g. espree is a parser 43 | * that produces such syntax trees. 44 | *

45 | * The main interface is the {@link analyze} function. 46 | * @module escope 47 | */ 48 | 49 | import assert from "assert"; 50 | 51 | import ScopeManager from "./scope-manager.js"; 52 | import Referencer from "./referencer.js"; 53 | import Reference from "./reference.js"; 54 | import Variable from "./variable.js"; 55 | 56 | import eslintScopeVersion from "./version.js"; 57 | 58 | /** 59 | * Set the default options 60 | * @returns {Object} options 61 | */ 62 | function defaultOptions() { 63 | return { 64 | optimistic: false, 65 | nodejsScope: false, 66 | impliedStrict: false, 67 | sourceType: "script", // one of ['script', 'module', 'commonjs'] 68 | ecmaVersion: 5, 69 | childVisitorKeys: null, 70 | fallback: "iteration" 71 | }; 72 | } 73 | 74 | /** 75 | * Preform deep update on option object 76 | * @param {Object} target Options 77 | * @param {Object} override Updates 78 | * @returns {Object} Updated options 79 | */ 80 | function updateDeeply(target, override) { 81 | 82 | /** 83 | * Is hash object 84 | * @param {Object} value Test value 85 | * @returns {boolean} Result 86 | */ 87 | function isHashObject(value) { 88 | return typeof value === "object" && value instanceof Object && !(value instanceof Array) && !(value instanceof RegExp); 89 | } 90 | 91 | for (const key in override) { 92 | if (Object.prototype.hasOwnProperty.call(override, key)) { 93 | const val = override[key]; 94 | 95 | if (isHashObject(val)) { 96 | if (isHashObject(target[key])) { 97 | updateDeeply(target[key], val); 98 | } else { 99 | target[key] = updateDeeply({}, val); 100 | } 101 | } else { 102 | target[key] = val; 103 | } 104 | } 105 | } 106 | return target; 107 | } 108 | 109 | /** 110 | * Main interface function. Takes an Espree syntax tree and returns the 111 | * analyzed scopes. 112 | * @function analyze 113 | * @param {espree.Tree} tree Abstract Syntax Tree 114 | * @param {Object} providedOptions Options that tailor the scope analysis 115 | * @param {boolean} [providedOptions.optimistic=false] the optimistic flag 116 | * @param {boolean} [providedOptions.ignoreEval=false] whether to check 'eval()' calls 117 | * @param {boolean} [providedOptions.nodejsScope=false] whether the whole 118 | * script is executed under node.js environment. When enabled, escope adds 119 | * a function scope immediately following the global scope. 120 | * @param {boolean} [providedOptions.impliedStrict=false] implied strict mode 121 | * (if ecmaVersion >= 5). 122 | * @param {string} [providedOptions.sourceType='script'] the source type of the script. one of 'script', 'module', and 'commonjs' 123 | * @param {number} [providedOptions.ecmaVersion=5] which ECMAScript version is considered 124 | * @param {Object} [providedOptions.childVisitorKeys=null] Additional known visitor keys. See [esrecurse](https://github.com/estools/esrecurse)'s the `childVisitorKeys` option. 125 | * @param {string} [providedOptions.fallback='iteration'] A kind of the fallback in order to encounter with unknown node. See [esrecurse](https://github.com/estools/esrecurse)'s the `fallback` option. 126 | * @returns {ScopeManager} ScopeManager 127 | */ 128 | function analyze(tree, providedOptions) { 129 | const options = updateDeeply(defaultOptions(), providedOptions); 130 | const scopeManager = new ScopeManager(options); 131 | const referencer = new Referencer(options, scopeManager); 132 | 133 | referencer.visit(tree); 134 | 135 | assert(scopeManager.__currentScope === null, "currentScope should be null."); 136 | 137 | return scopeManager; 138 | } 139 | 140 | export { 141 | 142 | /** @name module:escope.version */ 143 | eslintScopeVersion as version, 144 | 145 | /** @name module:escope.Reference */ 146 | Reference, 147 | 148 | /** @name module:escope.Variable */ 149 | Variable, 150 | 151 | /** @name module:escope.ScopeManager */ 152 | ScopeManager, 153 | 154 | /** @name module:escope.Referencer */ 155 | Referencer, 156 | 157 | analyze 158 | }; 159 | 160 | /** @name module:escope.Definition */ 161 | export { Definition } from "./definition.js"; 162 | 163 | /** @name module:escope.PatternVisitor */ 164 | export { default as PatternVisitor } from "./pattern-visitor.js"; 165 | 166 | /** @name module:escope.Scope */ 167 | export { Scope } from "./scope.js"; 168 | 169 | /* vim: set sw=4 ts=4 et tw=80 : */ 170 | -------------------------------------------------------------------------------- /lib/pattern-visitor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Yusuke Suzuki 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | import estraverse from "estraverse"; 26 | import esrecurse from "esrecurse"; 27 | 28 | const { Syntax } = estraverse; 29 | 30 | /** 31 | * Get last array element 32 | * @param {Array} xs array 33 | * @returns {any} Last elment 34 | */ 35 | function getLast(xs) { 36 | return xs[xs.length - 1] || null; 37 | } 38 | 39 | /** 40 | * Visitor for destructuring patterns. 41 | */ 42 | class PatternVisitor extends esrecurse.Visitor { 43 | static isPattern(node) { 44 | const nodeType = node.type; 45 | 46 | return ( 47 | nodeType === Syntax.Identifier || 48 | nodeType === Syntax.ObjectPattern || 49 | nodeType === Syntax.ArrayPattern || 50 | nodeType === Syntax.SpreadElement || 51 | nodeType === Syntax.RestElement || 52 | nodeType === Syntax.AssignmentPattern 53 | ); 54 | } 55 | 56 | constructor(options, rootPattern, callback) { 57 | super(null, options); 58 | this.rootPattern = rootPattern; 59 | this.callback = callback; 60 | this.assignments = []; 61 | this.rightHandNodes = []; 62 | this.restElements = []; 63 | } 64 | 65 | Identifier(pattern) { 66 | const lastRestElement = getLast(this.restElements); 67 | 68 | this.callback(pattern, { 69 | topLevel: pattern === this.rootPattern, 70 | rest: lastRestElement !== null && lastRestElement !== void 0 && lastRestElement.argument === pattern, 71 | assignments: this.assignments 72 | }); 73 | } 74 | 75 | Property(property) { 76 | 77 | // Computed property's key is a right hand node. 78 | if (property.computed) { 79 | this.rightHandNodes.push(property.key); 80 | } 81 | 82 | // If it's shorthand, its key is same as its value. 83 | // If it's shorthand and has its default value, its key is same as its value.left (the value is AssignmentPattern). 84 | // If it's not shorthand, the name of new variable is its value's. 85 | this.visit(property.value); 86 | } 87 | 88 | ArrayPattern(pattern) { 89 | for (let i = 0, iz = pattern.elements.length; i < iz; ++i) { 90 | const element = pattern.elements[i]; 91 | 92 | this.visit(element); 93 | } 94 | } 95 | 96 | AssignmentPattern(pattern) { 97 | this.assignments.push(pattern); 98 | this.visit(pattern.left); 99 | this.rightHandNodes.push(pattern.right); 100 | this.assignments.pop(); 101 | } 102 | 103 | RestElement(pattern) { 104 | this.restElements.push(pattern); 105 | this.visit(pattern.argument); 106 | this.restElements.pop(); 107 | } 108 | 109 | MemberExpression(node) { 110 | 111 | // Computed property's key is a right hand node. 112 | if (node.computed) { 113 | this.rightHandNodes.push(node.property); 114 | } 115 | 116 | // the object is only read, write to its property. 117 | this.rightHandNodes.push(node.object); 118 | } 119 | 120 | // 121 | // ForInStatement.left and AssignmentExpression.left are LeftHandSideExpression. 122 | // By spec, LeftHandSideExpression is Pattern or MemberExpression. 123 | // (see also: https://github.com/estree/estree/pull/20#issuecomment-74584758) 124 | // But espree 2.0 parses to ArrayExpression, ObjectExpression, etc... 125 | // 126 | 127 | SpreadElement(node) { 128 | this.visit(node.argument); 129 | } 130 | 131 | ArrayExpression(node) { 132 | node.elements.forEach(this.visit, this); 133 | } 134 | 135 | AssignmentExpression(node) { 136 | this.assignments.push(node); 137 | this.visit(node.left); 138 | this.rightHandNodes.push(node.right); 139 | this.assignments.pop(); 140 | } 141 | 142 | CallExpression(node) { 143 | 144 | // arguments are right hand nodes. 145 | node.arguments.forEach(a => { 146 | this.rightHandNodes.push(a); 147 | }); 148 | this.visit(node.callee); 149 | } 150 | } 151 | 152 | export default PatternVisitor; 153 | 154 | /* vim: set sw=4 ts=4 et tw=80 : */ 155 | -------------------------------------------------------------------------------- /lib/reference.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Yusuke Suzuki 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | const READ = 0x1; 26 | const WRITE = 0x2; 27 | const RW = READ | WRITE; 28 | 29 | /** 30 | * A Reference represents a single occurrence of an identifier in code. 31 | * @constructor Reference 32 | */ 33 | class Reference { 34 | constructor(ident, scope, flag, writeExpr, maybeImplicitGlobal, partial, init) { 35 | 36 | /** 37 | * Identifier syntax node. 38 | * @member {espreeIdentifier} Reference#identifier 39 | */ 40 | this.identifier = ident; 41 | 42 | /** 43 | * Reference to the enclosing Scope. 44 | * @member {Scope} Reference#from 45 | */ 46 | this.from = scope; 47 | 48 | /** 49 | * Whether the reference comes from a dynamic scope (such as 'eval', 50 | * 'with', etc.), and may be trapped by dynamic scopes. 51 | * @member {boolean} Reference#tainted 52 | */ 53 | this.tainted = false; 54 | 55 | /** 56 | * The variable this reference is resolved with. 57 | * @member {Variable} Reference#resolved 58 | */ 59 | this.resolved = null; 60 | 61 | /** 62 | * The read-write mode of the reference. (Value is one of {@link 63 | * Reference.READ}, {@link Reference.RW}, {@link Reference.WRITE}). 64 | * @member {number} Reference#flag 65 | * @private 66 | */ 67 | this.flag = flag; 68 | if (this.isWrite()) { 69 | 70 | /** 71 | * If reference is writeable, this is the tree being written to it. 72 | * @member {espreeNode} Reference#writeExpr 73 | */ 74 | this.writeExpr = writeExpr; 75 | 76 | /** 77 | * Whether the Reference might refer to a partial value of writeExpr. 78 | * @member {boolean} Reference#partial 79 | */ 80 | this.partial = partial; 81 | 82 | /** 83 | * Whether the Reference is to write of initialization. 84 | * @member {boolean} Reference#init 85 | */ 86 | this.init = init; 87 | } 88 | this.__maybeImplicitGlobal = maybeImplicitGlobal; 89 | } 90 | 91 | /** 92 | * Whether the reference is static. 93 | * @function Reference#isStatic 94 | * @returns {boolean} static 95 | */ 96 | isStatic() { 97 | return !this.tainted && this.resolved && this.resolved.scope.isStatic(); 98 | } 99 | 100 | /** 101 | * Whether the reference is writeable. 102 | * @function Reference#isWrite 103 | * @returns {boolean} write 104 | */ 105 | isWrite() { 106 | return !!(this.flag & Reference.WRITE); 107 | } 108 | 109 | /** 110 | * Whether the reference is readable. 111 | * @function Reference#isRead 112 | * @returns {boolean} read 113 | */ 114 | isRead() { 115 | return !!(this.flag & Reference.READ); 116 | } 117 | 118 | /** 119 | * Whether the reference is read-only. 120 | * @function Reference#isReadOnly 121 | * @returns {boolean} read only 122 | */ 123 | isReadOnly() { 124 | return this.flag === Reference.READ; 125 | } 126 | 127 | /** 128 | * Whether the reference is write-only. 129 | * @function Reference#isWriteOnly 130 | * @returns {boolean} write only 131 | */ 132 | isWriteOnly() { 133 | return this.flag === Reference.WRITE; 134 | } 135 | 136 | /** 137 | * Whether the reference is read-write. 138 | * @function Reference#isReadWrite 139 | * @returns {boolean} read write 140 | */ 141 | isReadWrite() { 142 | return this.flag === Reference.RW; 143 | } 144 | } 145 | 146 | /** 147 | * @constant Reference.READ 148 | * @private 149 | */ 150 | Reference.READ = READ; 151 | 152 | /** 153 | * @constant Reference.WRITE 154 | * @private 155 | */ 156 | Reference.WRITE = WRITE; 157 | 158 | /** 159 | * @constant Reference.RW 160 | * @private 161 | */ 162 | Reference.RW = RW; 163 | 164 | export default Reference; 165 | 166 | /* vim: set sw=4 ts=4 et tw=80 : */ 167 | -------------------------------------------------------------------------------- /lib/scope-manager.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Yusuke Suzuki 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | import { 26 | BlockScope, 27 | CatchScope, 28 | ClassFieldInitializerScope, 29 | ClassStaticBlockScope, 30 | ClassScope, 31 | ForScope, 32 | FunctionExpressionNameScope, 33 | FunctionScope, 34 | GlobalScope, 35 | ModuleScope, 36 | SwitchScope, 37 | WithScope 38 | } from "./scope.js"; 39 | import assert from "assert"; 40 | 41 | /** 42 | * @constructor ScopeManager 43 | */ 44 | class ScopeManager { 45 | constructor(options) { 46 | this.scopes = []; 47 | this.globalScope = null; 48 | this.__nodeToScope = new WeakMap(); 49 | this.__currentScope = null; 50 | this.__options = options; 51 | this.__declaredVariables = new WeakMap(); 52 | } 53 | 54 | __isOptimistic() { 55 | return this.__options.optimistic; 56 | } 57 | 58 | __ignoreEval() { 59 | return this.__options.ignoreEval; 60 | } 61 | 62 | isGlobalReturn() { 63 | return this.__options.nodejsScope || this.__options.sourceType === "commonjs"; 64 | } 65 | 66 | isModule() { 67 | return this.__options.sourceType === "module"; 68 | } 69 | 70 | isImpliedStrict() { 71 | return this.__options.impliedStrict; 72 | } 73 | 74 | isStrictModeSupported() { 75 | return this.__options.ecmaVersion >= 5; 76 | } 77 | 78 | // Returns appropriate scope for this node. 79 | __get(node) { 80 | return this.__nodeToScope.get(node); 81 | } 82 | 83 | /** 84 | * Get variables that are declared by the node. 85 | * 86 | * "are declared by the node" means the node is same as `Variable.defs[].node` or `Variable.defs[].parent`. 87 | * If the node declares nothing, this method returns an empty array. 88 | * CAUTION: This API is experimental. See https://github.com/estools/escope/pull/69 for more details. 89 | * @param {Espree.Node} node a node to get. 90 | * @returns {Variable[]} variables that declared by the node. 91 | */ 92 | getDeclaredVariables(node) { 93 | return this.__declaredVariables.get(node) || []; 94 | } 95 | 96 | /** 97 | * acquire scope from node. 98 | * @function ScopeManager#acquire 99 | * @param {Espree.Node} node node for the acquired scope. 100 | * @param {?boolean} [inner=false] look up the most inner scope, default value is false. 101 | * @returns {Scope?} Scope from node 102 | */ 103 | acquire(node, inner) { 104 | 105 | /** 106 | * predicate 107 | * @param {Scope} testScope scope to test 108 | * @returns {boolean} predicate 109 | */ 110 | function predicate(testScope) { 111 | if (testScope.type === "function" && testScope.functionExpressionScope) { 112 | return false; 113 | } 114 | return true; 115 | } 116 | 117 | const scopes = this.__get(node); 118 | 119 | if (!scopes || scopes.length === 0) { 120 | return null; 121 | } 122 | 123 | // Heuristic selection from all scopes. 124 | // If you would like to get all scopes, please use ScopeManager#acquireAll. 125 | if (scopes.length === 1) { 126 | return scopes[0]; 127 | } 128 | 129 | if (inner) { 130 | for (let i = scopes.length - 1; i >= 0; --i) { 131 | const scope = scopes[i]; 132 | 133 | if (predicate(scope)) { 134 | return scope; 135 | } 136 | } 137 | } else { 138 | for (let i = 0, iz = scopes.length; i < iz; ++i) { 139 | const scope = scopes[i]; 140 | 141 | if (predicate(scope)) { 142 | return scope; 143 | } 144 | } 145 | } 146 | 147 | return null; 148 | } 149 | 150 | /** 151 | * acquire all scopes from node. 152 | * @function ScopeManager#acquireAll 153 | * @param {Espree.Node} node node for the acquired scope. 154 | * @returns {Scopes?} Scope array 155 | */ 156 | acquireAll(node) { 157 | return this.__get(node); 158 | } 159 | 160 | /** 161 | * release the node. 162 | * @function ScopeManager#release 163 | * @param {Espree.Node} node releasing node. 164 | * @param {?boolean} [inner=false] look up the most inner scope, default value is false. 165 | * @returns {Scope?} upper scope for the node. 166 | */ 167 | release(node, inner) { 168 | const scopes = this.__get(node); 169 | 170 | if (scopes && scopes.length) { 171 | const scope = scopes[0].upper; 172 | 173 | if (!scope) { 174 | return null; 175 | } 176 | return this.acquire(scope.block, inner); 177 | } 178 | return null; 179 | } 180 | 181 | attach() { } // eslint-disable-line class-methods-use-this -- Desired as instance method 182 | 183 | detach() { } // eslint-disable-line class-methods-use-this -- Desired as instance method 184 | 185 | __nestScope(scope) { 186 | if (scope instanceof GlobalScope) { 187 | assert(this.__currentScope === null); 188 | this.globalScope = scope; 189 | } 190 | this.__currentScope = scope; 191 | return scope; 192 | } 193 | 194 | __nestGlobalScope(node) { 195 | return this.__nestScope(new GlobalScope(this, node)); 196 | } 197 | 198 | __nestBlockScope(node) { 199 | return this.__nestScope(new BlockScope(this, this.__currentScope, node)); 200 | } 201 | 202 | __nestFunctionScope(node, isMethodDefinition) { 203 | return this.__nestScope(new FunctionScope(this, this.__currentScope, node, isMethodDefinition)); 204 | } 205 | 206 | __nestForScope(node) { 207 | return this.__nestScope(new ForScope(this, this.__currentScope, node)); 208 | } 209 | 210 | __nestCatchScope(node) { 211 | return this.__nestScope(new CatchScope(this, this.__currentScope, node)); 212 | } 213 | 214 | __nestWithScope(node) { 215 | return this.__nestScope(new WithScope(this, this.__currentScope, node)); 216 | } 217 | 218 | __nestClassScope(node) { 219 | return this.__nestScope(new ClassScope(this, this.__currentScope, node)); 220 | } 221 | 222 | __nestClassFieldInitializerScope(node) { 223 | return this.__nestScope(new ClassFieldInitializerScope(this, this.__currentScope, node)); 224 | } 225 | 226 | __nestClassStaticBlockScope(node) { 227 | return this.__nestScope(new ClassStaticBlockScope(this, this.__currentScope, node)); 228 | } 229 | 230 | __nestSwitchScope(node) { 231 | return this.__nestScope(new SwitchScope(this, this.__currentScope, node)); 232 | } 233 | 234 | __nestModuleScope(node) { 235 | return this.__nestScope(new ModuleScope(this, this.__currentScope, node)); 236 | } 237 | 238 | __nestFunctionExpressionNameScope(node) { 239 | return this.__nestScope(new FunctionExpressionNameScope(this, this.__currentScope, node)); 240 | } 241 | 242 | __isES6() { 243 | return this.__options.ecmaVersion >= 6; 244 | } 245 | } 246 | 247 | export default ScopeManager; 248 | 249 | /* vim: set sw=4 ts=4 et tw=80 : */ 250 | -------------------------------------------------------------------------------- /lib/variable.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Yusuke Suzuki 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | /** 26 | * A Variable represents a locally scoped identifier. These include arguments to 27 | * functions. 28 | * @constructor Variable 29 | */ 30 | class Variable { 31 | constructor(name, scope) { 32 | 33 | /** 34 | * The variable name, as given in the source code. 35 | * @member {string} Variable#name 36 | */ 37 | this.name = name; 38 | 39 | /** 40 | * List of defining occurrences of this variable (like in 'var ...' 41 | * statements or as parameter), as AST nodes. 42 | * @member {espree.Identifier[]} Variable#identifiers 43 | */ 44 | this.identifiers = []; 45 | 46 | /** 47 | * List of {@link Reference|references} of this variable (excluding parameter entries) 48 | * in its defining scope and all nested scopes. For defining 49 | * occurrences only see {@link Variable#defs}. 50 | * @member {Reference[]} Variable#references 51 | */ 52 | this.references = []; 53 | 54 | /** 55 | * List of defining occurrences of this variable (like in 'var ...' 56 | * statements or as parameter), as custom objects. 57 | * @member {Definition[]} Variable#defs 58 | */ 59 | this.defs = []; 60 | 61 | this.tainted = false; 62 | 63 | /** 64 | * Whether this is a stack variable. 65 | * @member {boolean} Variable#stack 66 | */ 67 | this.stack = true; 68 | 69 | /** 70 | * Reference to the enclosing Scope. 71 | * @member {Scope} Variable#scope 72 | */ 73 | this.scope = scope; 74 | } 75 | } 76 | 77 | Variable.CatchClause = "CatchClause"; 78 | Variable.Parameter = "Parameter"; 79 | Variable.FunctionName = "FunctionName"; 80 | Variable.ClassName = "ClassName"; 81 | Variable.Variable = "Variable"; 82 | Variable.ImportBinding = "ImportBinding"; 83 | Variable.ImplicitGlobalVariable = "ImplicitGlobalVariable"; 84 | 85 | export default Variable; 86 | 87 | /* vim: set sw=4 ts=4 et tw=80 : */ 88 | -------------------------------------------------------------------------------- /lib/version.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Contains the current version number. This file is checked in 3 | * to source control and should always return "main". During the release 4 | * process, this file is overwritten with a new on that returns the actual 5 | * version number. The file with the actual version number is *not* checked 6 | * back into the repo. This is only necessary until Node.js supports import 7 | * assertions, at which time we can just import package.json for this info. 8 | * @author Nicholas C. Zakas 9 | */ 10 | const version = "main"; 11 | 12 | export default version; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-scope", 3 | "description": "ECMAScript scope analyzer for ESLint", 4 | "homepage": "http://github.com/eslint/eslint-scope", 5 | "main": "./dist/eslint-scope.cjs", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "import": "./lib/index.js", 10 | "require": "./dist/eslint-scope.cjs" 11 | }, 12 | "./package.json": "./package.json" 13 | }, 14 | "version": "8.0.2", 15 | "engines": { 16 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 17 | }, 18 | "repository": "eslint/eslint-scope", 19 | "funding": "https://opencollective.com/eslint", 20 | "bugs": { 21 | "url": "https://github.com/eslint/eslint-scope/issues" 22 | }, 23 | "license": "BSD-2-Clause", 24 | "scripts": { 25 | "build": "rollup -c", 26 | "build:update-version": "node tools/update-version.js", 27 | "lint": "eslint . --report-unused-disable-directives", 28 | "prelint": "npm run build", 29 | "prepublishOnly": "npm run build:update-version && npm run build", 30 | "pretest": "npm run build", 31 | "release:generate:latest": "eslint-generate-release", 32 | "release:generate:alpha": "eslint-generate-prerelease alpha", 33 | "release:generate:beta": "eslint-generate-prerelease beta", 34 | "release:generate:rc": "eslint-generate-prerelease rc", 35 | "release:publish": "eslint-publish-release", 36 | "test": "node Makefile.js test" 37 | }, 38 | "files": [ 39 | "LICENSE", 40 | "README.md", 41 | "lib", 42 | "dist/eslint-scope.cjs" 43 | ], 44 | "dependencies": { 45 | "esrecurse": "^4.3.0", 46 | "estraverse": "^5.2.0" 47 | }, 48 | "devDependencies": { 49 | "@typescript-eslint/parser": "^7.1.1", 50 | "c8": "^7.7.3", 51 | "chai": "^4.3.4", 52 | "eslint": "^8.57.0", 53 | "eslint-config-eslint": "^9.0.0", 54 | "eslint-plugin-chai-friendly": "^0.7.4", 55 | "eslint-release": "^3.2.0", 56 | "eslint-visitor-keys": "^4.0.0", 57 | "espree": "^10.0.1", 58 | "globals": "^14.0.0", 59 | "mocha": "^9.0.1", 60 | "npm-license": "^0.3.3", 61 | "rollup": "^2.52.7", 62 | "shelljs": "^0.8.5", 63 | "typescript": "^5.4.2" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | input: "./lib/index.js", 3 | external: ["assert", "estraverse", "esrecurse"], 4 | treeshake: false, 5 | output: { 6 | format: "cjs", 7 | file: "dist/eslint-scope.cjs", 8 | sourcemap: true 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /tests/arguments.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("arguments", () => { 29 | it("arguments are correctly materialized", () => { 30 | const ast = espree(` 31 | (function () { 32 | arguments; 33 | }()); 34 | `); 35 | 36 | const scopeManager = analyze(ast); 37 | 38 | expect(scopeManager.scopes).to.have.length(2); 39 | const globalScope = scopeManager.scopes[0]; 40 | 41 | expect(globalScope.type).to.be.equal("global"); 42 | expect(globalScope.variables).to.have.length(0); 43 | expect(globalScope.references).to.have.length(0); 44 | 45 | const scope = scopeManager.scopes[1]; 46 | 47 | expect(scope.type).to.be.equal("function"); 48 | expect(scope.variables).to.have.length(1); 49 | expect(scope.variables[0].name).to.be.equal("arguments"); 50 | expect(scope.isArgumentsMaterialized()).to.be.true; 51 | expect(scope.references).to.have.length(1); 52 | expect(scope.references[0].resolved).to.be.equal(scope.variables[0]); 53 | }); 54 | }); 55 | 56 | // vim: set sw=4 ts=4 et tw=80 : 57 | -------------------------------------------------------------------------------- /tests/catch-scope.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("catch", () => { 29 | it("creates scope", () => { 30 | const ast = espree(` 31 | (function () { 32 | try { 33 | } catch (e) { 34 | } 35 | }()); 36 | `); 37 | 38 | const scopeManager = analyze(ast); 39 | 40 | expect(scopeManager.scopes).to.have.length(3); 41 | const globalScope = scopeManager.scopes[0]; 42 | 43 | expect(globalScope.type).to.be.equal("global"); 44 | expect(globalScope.variables).to.have.length(0); 45 | expect(globalScope.references).to.have.length(0); 46 | 47 | let scope = scopeManager.scopes[1]; 48 | 49 | expect(scope.type).to.be.equal("function"); 50 | expect(scope.variables).to.have.length(1); 51 | expect(scope.variables[0].name).to.be.equal("arguments"); 52 | expect(scope.isArgumentsMaterialized()).to.be.false; 53 | expect(scope.references).to.have.length(0); 54 | 55 | scope = scopeManager.scopes[2]; 56 | expect(scope.type).to.be.equal("catch"); 57 | expect(scope.variables).to.have.length(1); 58 | expect(scope.variables[0].name).to.be.equal("e"); 59 | expect(scope.variables[0].defs).to.have.length(1); 60 | expect(scope.variables[0].defs[0].type).to.be.equal("CatchClause"); 61 | expect(scope.variables[0].defs[0].name.type).to.be.equal("Identifier"); 62 | expect(scope.variables[0].defs[0].name.name).to.be.equal("e"); 63 | expect(scope.variables[0].defs[0].node.type).to.be.equal("CatchClause"); 64 | expect(scope.variables[0].defs[0].parent).to.be.equal(null); 65 | expect(scope.isArgumentsMaterialized()).to.be.true; 66 | expect(scope.references).to.have.length(0); 67 | }); 68 | 69 | it("param can be a pattern", () => { 70 | const ast = espree(` 71 | (function () { 72 | const default_id = 0; 73 | try { 74 | } catch ({ message, id = default_id, args: [arg1, arg2] }) { 75 | } 76 | }()); 77 | `); 78 | 79 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 80 | 81 | expect(scopeManager.scopes).to.have.length(5); 82 | 83 | const globalScope = scopeManager.scopes[0]; 84 | 85 | expect(globalScope.type).to.be.equal("global"); 86 | expect(globalScope.variables).to.have.length(0); 87 | expect(globalScope.references).to.have.length(0); 88 | 89 | const functionScope = scopeManager.scopes[1]; 90 | 91 | expect(functionScope.type).to.be.equal("function"); 92 | expect(functionScope.variables).to.have.length(2); 93 | expect(functionScope.variables[0].name).to.be.equal("arguments"); 94 | expect(functionScope.variables[1].name).to.be.equal("default_id"); 95 | expect(functionScope.references).to.have.length(1); 96 | expect(functionScope.references[0].from).to.be.equal(functionScope); 97 | expect(functionScope.references[0].resolved).to.be.equal(functionScope.variables[1]); 98 | 99 | const tryBlockScope = scopeManager.scopes[2]; 100 | 101 | expect(tryBlockScope.type).to.be.equal("block"); 102 | expect(tryBlockScope.variables).to.have.length(0); 103 | expect(tryBlockScope.references).to.have.length(0); 104 | 105 | const catchScope = scopeManager.scopes[3]; 106 | 107 | expect(catchScope.type).to.be.equal("catch"); 108 | expect(catchScope.variables).to.have.length(4); 109 | expect(catchScope.variables[0].name).to.be.equal("message"); 110 | expect(catchScope.variables[0].defs).to.have.length(1); 111 | expect(catchScope.variables[0].defs[0].type).to.be.equal("CatchClause"); 112 | expect(catchScope.variables[0].defs[0].name.type).to.be.equal("Identifier"); 113 | expect(catchScope.variables[0].defs[0].name.name).to.be.equal("message"); 114 | expect(catchScope.variables[0].defs[0].node.type).to.be.equal("CatchClause"); 115 | expect(catchScope.variables[0].defs[0].parent).to.be.equal(null); 116 | expect(catchScope.variables[1].name).to.be.equal("id"); 117 | expect(catchScope.variables[1].defs).to.have.length(1); 118 | expect(catchScope.variables[1].defs[0].type).to.be.equal("CatchClause"); 119 | expect(catchScope.variables[1].defs[0].name.type).to.be.equal("Identifier"); 120 | expect(catchScope.variables[1].defs[0].name.name).to.be.equal("id"); 121 | expect(catchScope.variables[1].defs[0].node.type).to.be.equal("CatchClause"); 122 | expect(catchScope.variables[1].defs[0].parent).to.be.equal(null); 123 | expect(catchScope.variables[2].name).to.be.equal("arg1"); 124 | expect(catchScope.variables[2].defs).to.have.length(1); 125 | expect(catchScope.variables[2].defs[0].type).to.be.equal("CatchClause"); 126 | expect(catchScope.variables[2].defs[0].name.type).to.be.equal("Identifier"); 127 | expect(catchScope.variables[2].defs[0].name.name).to.be.equal("arg1"); 128 | expect(catchScope.variables[2].defs[0].node.type).to.be.equal("CatchClause"); 129 | expect(catchScope.variables[2].defs[0].parent).to.be.equal(null); 130 | expect(catchScope.variables[3].name).to.be.equal("arg2"); 131 | expect(catchScope.variables[3].defs).to.have.length(1); 132 | expect(catchScope.variables[3].defs[0].type).to.be.equal("CatchClause"); 133 | expect(catchScope.variables[3].defs[0].name.type).to.be.equal("Identifier"); 134 | expect(catchScope.variables[3].defs[0].name.name).to.be.equal("arg2"); 135 | expect(catchScope.variables[3].defs[0].node.type).to.be.equal("CatchClause"); 136 | expect(catchScope.variables[3].defs[0].parent).to.be.equal(null); 137 | expect(catchScope.references).to.have.length(2); 138 | expect(catchScope.references[0].from).to.be.equal(catchScope); 139 | expect(catchScope.references[0].resolved).to.be.equal(catchScope.variables[1]); 140 | expect(catchScope.references[1].from).to.be.equal(catchScope); 141 | expect(catchScope.references[1].resolved).to.be.equal(functionScope.variables[1]); 142 | 143 | const catchBlockScope = scopeManager.scopes[4]; 144 | 145 | expect(catchBlockScope.type).to.be.equal("block"); 146 | expect(catchBlockScope.variables).to.have.length(0); 147 | expect(catchBlockScope.references).to.have.length(0); 148 | }); 149 | }); 150 | 151 | // vim: set sw=4 ts=4 et tw=80 : 152 | -------------------------------------------------------------------------------- /tests/child-visitor-keys.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2016 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("childVisitorKeys option", () => { 29 | it("should handle as a known node if the childVisitorKeys option was given.", () => { 30 | const ast = espree(` 31 | var foo = 0; 32 | `); 33 | 34 | ast.body[0].declarations[0].init.type = "NumericLiteral"; 35 | 36 | // should no error 37 | analyze( 38 | ast, 39 | { 40 | fallback: "none", 41 | childVisitorKeys: { 42 | NumericLiteral: [] 43 | } 44 | } 45 | ); 46 | }); 47 | 48 | it("should not visit to properties which are not given.", () => { 49 | const ast = espree(` 50 | let foo = bar; 51 | `); 52 | 53 | ast.body[0].declarations[0].init = { 54 | type: "TestNode", 55 | argument: ast.body[0].declarations[0].init 56 | }; 57 | 58 | const result = analyze( 59 | ast, 60 | { 61 | childVisitorKeys: { 62 | TestNode: [] 63 | } 64 | } 65 | ); 66 | 67 | expect(result.scopes).to.have.length(1); 68 | const globalScope = result.scopes[0]; 69 | 70 | // `bar` in TestNode has not been visited. 71 | expect(globalScope.through).to.have.length(0); 72 | }); 73 | 74 | it("should visit to given properties.", () => { 75 | const ast = espree(` 76 | let foo = bar; 77 | `); 78 | 79 | ast.body[0].declarations[0].init = { 80 | type: "TestNode", 81 | argument: ast.body[0].declarations[0].init 82 | }; 83 | 84 | const result = analyze( 85 | ast, 86 | { 87 | childVisitorKeys: { 88 | TestNode: ["argument"] 89 | } 90 | } 91 | ); 92 | 93 | expect(result.scopes).to.have.length(1); 94 | const globalScope = result.scopes[0]; 95 | 96 | // `bar` in TestNode has been visited. 97 | expect(globalScope.through).to.have.length(1); 98 | expect(globalScope.through[0].identifier.name).to.equal("bar"); 99 | }); 100 | }); 101 | 102 | // vim: set sw=4 ts=4 et tw=80 : 103 | -------------------------------------------------------------------------------- /tests/class-fields.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for class fields syntax. 3 | * @author Toru Nagashima 4 | */ 5 | 6 | import assert from "assert"; 7 | import * as espree from "espree"; 8 | import { KEYS } from "eslint-visitor-keys"; 9 | import { analyze } from "../lib/index.js"; 10 | 11 | describe("Class fields", () => { 12 | describe("class C { f = g }", () => { 13 | let scopes; 14 | 15 | beforeEach(() => { 16 | const ast = espree.parse("class C { f = g }", { ecmaVersion: 13 }); 17 | const manager = analyze(ast, { ecmaVersion: 13, childVisitorKeys: KEYS }); 18 | 19 | scopes = manager.globalScope.childScopes; 20 | }); 21 | 22 | it("should create a class scope.", () => { 23 | assert.strictEqual(scopes.length, 1); 24 | assert.strictEqual(scopes[0].type, "class"); 25 | }); 26 | 27 | it("The class scope has no references.", () => { 28 | const classScope = scopes[0]; 29 | 30 | assert.strictEqual(classScope.references.length, 0); 31 | }); 32 | 33 | it("The class scope has only the variable 'C'; it doesn't have the field name 'f'.", () => { 34 | const classScope = scopes[0]; 35 | 36 | assert.strictEqual(classScope.variables.length, 1); 37 | assert.strictEqual(classScope.variables[0].name, "C"); 38 | }); 39 | 40 | it("The class scope has a class-field-initializer scope.", () => { 41 | const classScope = scopes[0]; 42 | 43 | assert.strictEqual(classScope.childScopes.length, 1); 44 | assert.strictEqual(classScope.childScopes[0].type, "class-field-initializer"); 45 | }); 46 | 47 | it("The class-field-initializer scope's block is the node of the field initializer.", () => { 48 | const classScope = scopes[0]; 49 | const fieldInitializerScope = classScope.childScopes[0]; 50 | 51 | assert.strictEqual(fieldInitializerScope.block.type, "Identifier"); 52 | assert.strictEqual(fieldInitializerScope.block.name, "g"); 53 | }); 54 | 55 | it("The class-field-initializer scope's variableScope is itself.", () => { 56 | const classScope = scopes[0]; 57 | const fieldInitializerScope = classScope.childScopes[0]; 58 | 59 | assert.strictEqual(fieldInitializerScope.variableScope, fieldInitializerScope); 60 | }); 61 | 62 | it("The class-field-initializer scope has only the reference 'g'.", () => { 63 | const classScope = scopes[0]; 64 | const fieldInitializerScope = classScope.childScopes[0]; 65 | 66 | assert.strictEqual(fieldInitializerScope.references.length, 1); 67 | assert.strictEqual(fieldInitializerScope.references[0].identifier.name, "g"); 68 | }); 69 | 70 | it("The class-field-initializer scope has no variables.", () => { 71 | const classScope = scopes[0]; 72 | const fieldInitializerScope = classScope.childScopes[0]; 73 | 74 | assert.strictEqual(fieldInitializerScope.variables.length, 0); 75 | }); 76 | }); 77 | 78 | describe("class C { f }", () => { 79 | let scopes; 80 | 81 | beforeEach(() => { 82 | const ast = espree.parse("class C { f }", { ecmaVersion: 13 }); 83 | const manager = analyze(ast, { ecmaVersion: 13, childVisitorKeys: KEYS }); 84 | 85 | scopes = manager.globalScope.childScopes; 86 | }); 87 | 88 | it("should create a class scope.", () => { 89 | assert.strictEqual(scopes.length, 1); 90 | assert.strictEqual(scopes[0].type, "class"); 91 | }); 92 | 93 | it("The class scope has no references.", () => { 94 | const classScope = scopes[0]; 95 | 96 | assert.strictEqual(classScope.references.length, 0); 97 | }); 98 | 99 | it("The class scope has no child scopes; fields that don't have initializers don't create any class-field-initializer scopes.", () => { 100 | const classScope = scopes[0]; 101 | 102 | assert.strictEqual(classScope.childScopes.length, 0); 103 | }); 104 | 105 | it("The class scope has only the variable 'C'; it doesn't have the field name 'f'.", () => { 106 | const classScope = scopes[0]; 107 | 108 | assert.strictEqual(classScope.variables.length, 1); 109 | assert.strictEqual(classScope.variables[0].name, "C"); 110 | }); 111 | }); 112 | 113 | describe("class C { #f = g }", () => { 114 | let scopes; 115 | 116 | beforeEach(() => { 117 | const ast = espree.parse("class C { #f = g }", { ecmaVersion: 13 }); 118 | const manager = analyze(ast, { ecmaVersion: 13, childVisitorKeys: KEYS }); 119 | 120 | scopes = manager.globalScope.childScopes; 121 | }); 122 | 123 | it("should create a class scope.", () => { 124 | assert.strictEqual(scopes.length, 1); 125 | assert.strictEqual(scopes[0].type, "class"); 126 | }); 127 | 128 | it("The class scope has no references.", () => { 129 | const classScope = scopes[0]; 130 | 131 | assert.strictEqual(classScope.references.length, 0); 132 | }); 133 | 134 | it("The class scope has only the variable 'C'; it doesn't have the field name '#f'.", () => { 135 | const classScope = scopes[0]; 136 | 137 | assert.strictEqual(classScope.variables.length, 1); 138 | assert.strictEqual(classScope.variables[0].name, "C"); 139 | }); 140 | 141 | it("The class scope has a class-field-initializer scope.", () => { 142 | const classScope = scopes[0]; 143 | 144 | assert.strictEqual(classScope.childScopes.length, 1); 145 | assert.strictEqual(classScope.childScopes[0].type, "class-field-initializer"); 146 | }); 147 | 148 | it("The class-field-initializer scope has only the reference 'g'.", () => { 149 | const classScope = scopes[0]; 150 | const fieldInitializerScope = classScope.childScopes[0]; 151 | 152 | assert.strictEqual(fieldInitializerScope.references.length, 1); 153 | assert.strictEqual(fieldInitializerScope.references[0].identifier.name, "g"); 154 | }); 155 | 156 | it("The class-field-initializer scope has no variables.", () => { 157 | const classScope = scopes[0]; 158 | const fieldInitializerScope = classScope.childScopes[0]; 159 | 160 | assert.strictEqual(fieldInitializerScope.variables.length, 0); 161 | }); 162 | }); 163 | 164 | describe("class C { [fname] }", () => { 165 | let scopes; 166 | 167 | beforeEach(() => { 168 | const ast = espree.parse("class C { [fname] }", { ecmaVersion: 13 }); 169 | const manager = analyze(ast, { ecmaVersion: 13, childVisitorKeys: KEYS }); 170 | 171 | scopes = manager.globalScope.childScopes; 172 | }); 173 | 174 | it("should create a class scope.", () => { 175 | assert.strictEqual(scopes.length, 1); 176 | assert.strictEqual(scopes[0].type, "class"); 177 | }); 178 | 179 | it("The class scope has only the reference 'fname'.", () => { 180 | const classScope = scopes[0]; 181 | 182 | assert.strictEqual(classScope.references.length, 1); 183 | assert.strictEqual(classScope.references[0].identifier.name, "fname"); 184 | }); 185 | 186 | it("The class scope has no child scopes; fields that don't have initializers don't create any class-field-initializer scopes.", () => { 187 | const classScope = scopes[0]; 188 | 189 | assert.strictEqual(classScope.childScopes.length, 0); 190 | }); 191 | }); 192 | 193 | describe("class C { [fname] = value }", () => { 194 | let scopes; 195 | 196 | beforeEach(() => { 197 | const ast = espree.parse("class C { [fname] = value }", { ecmaVersion: 13 }); 198 | const manager = analyze(ast, { ecmaVersion: 13, childVisitorKeys: KEYS }); 199 | 200 | scopes = manager.globalScope.childScopes; 201 | }); 202 | 203 | it("should create a class scope.", () => { 204 | assert.strictEqual(scopes.length, 1); 205 | assert.strictEqual(scopes[0].type, "class"); 206 | }); 207 | 208 | it("The class scope has only the reference 'fname'; it doesn't have the reference 'value'.", () => { 209 | const classScope = scopes[0]; 210 | 211 | assert.strictEqual(classScope.references.length, 1); 212 | assert.strictEqual(classScope.references[0].identifier.name, "fname"); 213 | }); 214 | 215 | it("The class scope has a class-field-initializer scope.", () => { 216 | const classScope = scopes[0]; 217 | 218 | assert.strictEqual(classScope.childScopes.length, 1); 219 | assert.strictEqual(classScope.childScopes[0].type, "class-field-initializer"); 220 | }); 221 | 222 | it("The class-field-initializer scope has the reference 'value'.", () => { 223 | const classScope = scopes[0]; 224 | const fieldInitializerScope = classScope.childScopes[0]; 225 | 226 | assert.strictEqual(fieldInitializerScope.references.length, 1); 227 | assert.strictEqual(fieldInitializerScope.references[0].identifier.name, "value"); 228 | }); 229 | 230 | it("The class-field-initializer scope has no variables.", () => { 231 | const classScope = scopes[0]; 232 | const fieldInitializerScope = classScope.childScopes[0]; 233 | 234 | assert.strictEqual(fieldInitializerScope.variables.length, 0); 235 | }); 236 | }); 237 | 238 | describe("class C { #f = g; e = this.#f }", () => { 239 | let scopes; 240 | 241 | beforeEach(() => { 242 | const ast = espree.parse("class C { #f = g; e = this.#f }", { ecmaVersion: 13 }); 243 | const manager = analyze(ast, { ecmaVersion: 13, childVistorKeys: KEYS }); 244 | 245 | scopes = manager.globalScope.childScopes; 246 | }); 247 | 248 | it("should create a class scope.", () => { 249 | assert.strictEqual(scopes.length, 1); 250 | assert.strictEqual(scopes[0].type, "class"); 251 | }); 252 | 253 | it("The class scope has no references.", () => { 254 | const classScope = scopes[0]; 255 | 256 | assert.strictEqual(classScope.references.length, 0); 257 | }); 258 | 259 | it("The class scope has only the variable 'C'; it doesn't have the field names '#f' or 'e'.", () => { 260 | const classScope = scopes[0]; 261 | 262 | assert.strictEqual(classScope.variables.length, 1); 263 | assert.strictEqual(classScope.variables[0].name, "C"); 264 | }); 265 | 266 | it("The class scope has two class-field-initializer scopes.", () => { 267 | const classScope = scopes[0]; 268 | 269 | assert.strictEqual(classScope.childScopes.length, 2); 270 | assert.strictEqual(classScope.childScopes[0].type, "class-field-initializer"); 271 | assert.strictEqual(classScope.childScopes[1].type, "class-field-initializer"); 272 | }); 273 | 274 | it("The first class-field-initializer scope has only the reference 'g'.", () => { 275 | const classScope = scopes[0]; 276 | const fieldInitializerScope = classScope.childScopes[0]; 277 | 278 | assert.strictEqual(fieldInitializerScope.references.length, 1); 279 | assert.strictEqual(fieldInitializerScope.references[0].identifier.name, "g"); 280 | }); 281 | 282 | it("The first class-field-initializer scope has no variables.", () => { 283 | const classScope = scopes[0]; 284 | const fieldInitializerScope = classScope.childScopes[0]; 285 | 286 | assert.strictEqual(fieldInitializerScope.variables.length, 0); 287 | }); 288 | 289 | it("The second class-field-initializer scope has no references.", () => { 290 | const classScope = scopes[0]; 291 | const fieldInitializerScope = classScope.childScopes[1]; 292 | 293 | assert.strictEqual(fieldInitializerScope.references.length, 0); 294 | }); 295 | 296 | it("The second class-field-initializer scope has no variables.", () => { 297 | const classScope = scopes[0]; 298 | const fieldInitializerScope = classScope.childScopes[1]; 299 | 300 | assert.strictEqual(fieldInitializerScope.variables.length, 0); 301 | }); 302 | }); 303 | }); 304 | -------------------------------------------------------------------------------- /tests/commonjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for checking that the commonjs entry points are still accessible 3 | * @author Mike Reinstein 4 | */ 5 | 6 | "use strict"; 7 | 8 | //------------------------------------------------------------------------------ 9 | // Requirements 10 | //------------------------------------------------------------------------------ 11 | 12 | const assert = require("assert"); 13 | const eslintScope = require("../dist/eslint-scope.cjs"); 14 | 15 | 16 | //------------------------------------------------------------------------------ 17 | // Tests 18 | //------------------------------------------------------------------------------ 19 | 20 | describe("commonjs", () => { 21 | it("is an object", () => { 22 | assert.strictEqual(typeof eslintScope, "object"); 23 | }); 24 | 25 | it("has exports", () => { 26 | assert.strictEqual(typeof eslintScope.version, "string"); 27 | 28 | [ 29 | "analyze", 30 | "Definition", 31 | "PatternVisitor", 32 | "Reference", 33 | "Referencer", 34 | "Scope", 35 | "ScopeManager", 36 | "Variable" 37 | ].forEach(prop => { 38 | assert.strictEqual(typeof eslintScope[prop], "function"); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /tests/es6-arrow-function-expression.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 arrow function expression", () => { 29 | it("materialize scope for arrow function expression", () => { 30 | const ast = espree(` 31 | var arrow = () => { 32 | let i = 0; 33 | var j = 20; 34 | console.log(i); 35 | } 36 | `); 37 | 38 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 39 | 40 | expect(scopeManager.scopes).to.have.length(2); 41 | 42 | let scope = scopeManager.scopes[0]; 43 | 44 | expect(scope.type).to.be.equal("global"); 45 | expect(scope.block.type).to.be.equal("Program"); 46 | expect(scope.isStrict).to.be.false; 47 | expect(scope.variables).to.have.length(1); 48 | 49 | scope = scopeManager.scopes[1]; 50 | expect(scope.type).to.be.equal("function"); 51 | expect(scope.block.type).to.be.equal("ArrowFunctionExpression"); 52 | expect(scope.isStrict).to.be.false; 53 | expect(scope.variables).to.have.length(2); 54 | 55 | // There's no "arguments" 56 | expect(scope.variables[0].name).to.be.equal("i"); 57 | expect(scope.variables[1].name).to.be.equal("j"); 58 | }); 59 | 60 | it("generate bindings for parameters", () => { 61 | const ast = espree("var arrow = (a, b, c, d) => {}"); 62 | 63 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 64 | 65 | expect(scopeManager.scopes).to.have.length(2); 66 | 67 | let scope = scopeManager.scopes[0]; 68 | 69 | expect(scope.type).to.be.equal("global"); 70 | expect(scope.block.type).to.be.equal("Program"); 71 | expect(scope.isStrict).to.be.false; 72 | expect(scope.variables).to.have.length(1); 73 | 74 | scope = scopeManager.scopes[1]; 75 | expect(scope.type).to.be.equal("function"); 76 | expect(scope.block.type).to.be.equal("ArrowFunctionExpression"); 77 | expect(scope.isStrict).to.be.false; 78 | expect(scope.variables).to.have.length(4); 79 | 80 | // There's no "arguments" 81 | expect(scope.variables[0].name).to.be.equal("a"); 82 | expect(scope.variables[1].name).to.be.equal("b"); 83 | expect(scope.variables[2].name).to.be.equal("c"); 84 | expect(scope.variables[3].name).to.be.equal("d"); 85 | }); 86 | 87 | it("inherits upper scope strictness", () => { 88 | const ast = espree(` 89 | "use strict"; 90 | var arrow = () => {}; 91 | `); 92 | 93 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 94 | 95 | expect(scopeManager.scopes).to.have.length(2); 96 | 97 | let scope = scopeManager.scopes[0]; 98 | 99 | expect(scope.type).to.be.equal("global"); 100 | expect(scope.block.type).to.be.equal("Program"); 101 | expect(scope.isStrict).to.be.true; 102 | expect(scope.variables).to.have.length(1); 103 | 104 | scope = scopeManager.scopes[1]; 105 | 106 | expect(scope.type).to.be.equal("function"); 107 | expect(scope.block.type).to.be.equal("ArrowFunctionExpression"); 108 | expect(scope.isStrict).to.be.true; 109 | expect(scope.variables).to.have.length(0); 110 | }); 111 | 112 | it("is strict when a strictness directive is used", () => { 113 | const ast = espree(` 114 | var arrow = () => { 115 | "use strict"; 116 | }; 117 | `); 118 | 119 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 120 | 121 | expect(scopeManager.scopes).to.have.length(2); 122 | 123 | let scope = scopeManager.scopes[0]; 124 | 125 | expect(scope.type).to.be.equal("global"); 126 | expect(scope.block.type).to.be.equal("Program"); 127 | expect(scope.isStrict).to.be.false; 128 | expect(scope.variables).to.have.length(1); 129 | 130 | scope = scopeManager.scopes[1]; 131 | 132 | expect(scope.type).to.be.equal("function"); 133 | expect(scope.block.type).to.be.equal("ArrowFunctionExpression"); 134 | expect(scope.isStrict).to.be.true; 135 | expect(scope.variables).to.have.length(0); 136 | }); 137 | 138 | it("works with no body", () => { 139 | const ast = espree("var arrow = a => a;"); 140 | 141 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 142 | 143 | expect(scopeManager.scopes).to.have.length(2); 144 | 145 | const scope = scopeManager.scopes[1]; 146 | 147 | expect(scope.type).to.be.equal("function"); 148 | expect(scope.block.type).to.be.equal("ArrowFunctionExpression"); 149 | expect(scope.isStrict).to.be.false; 150 | expect(scope.variables).to.have.length(1); 151 | }); 152 | }); 153 | 154 | // vim: set sw=4 ts=4 et tw=80 : 155 | -------------------------------------------------------------------------------- /tests/es6-block-scope.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { getSupportedEcmaVersions } from "./util/ecma-version.js"; 27 | import { analyze } from "../lib/index.js"; 28 | 29 | describe("ES6 block scope", () => { 30 | it("let is materialized in ES6 block scope#1", () => { 31 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 32 | const ast = espree(` 33 | { 34 | let i = 20; 35 | i; 36 | } 37 | `); 38 | 39 | const scopeManager = analyze(ast, { ecmaVersion }); 40 | 41 | expect(scopeManager.scopes).to.have.length(2); // Program and BlockStatement scope. 42 | 43 | let scope = scopeManager.scopes[0]; 44 | 45 | expect(scope.type).to.be.equal("global"); 46 | expect(scope.variables).to.have.length(0); // No variable in Program scope. 47 | 48 | scope = scopeManager.scopes[1]; 49 | expect(scope.type).to.be.equal("block"); 50 | expect(scope.variables).to.have.length(1); // `i` in block scope. 51 | expect(scope.variables[0].name).to.be.equal("i"); 52 | expect(scope.references).to.have.length(2); 53 | expect(scope.references[0].identifier.name).to.be.equal("i"); 54 | expect(scope.references[1].identifier.name).to.be.equal("i"); 55 | }); 56 | }); 57 | 58 | it("function delaration is materialized in ES6 block scope", () => { 59 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 60 | const ast = espree(` 61 | { 62 | function test() { 63 | } 64 | test(); 65 | } 66 | `); 67 | 68 | const scopeManager = analyze(ast, { ecmaVersion }); 69 | 70 | expect(scopeManager.scopes).to.have.length(3); 71 | 72 | let scope = scopeManager.scopes[0]; 73 | 74 | expect(scope.type).to.be.equal("global"); 75 | expect(scope.variables).to.have.length(0); 76 | 77 | scope = scopeManager.scopes[1]; 78 | expect(scope.type).to.be.equal("block"); 79 | expect(scope.variables).to.have.length(1); 80 | expect(scope.variables[0].name).to.be.equal("test"); 81 | expect(scope.references).to.have.length(1); 82 | expect(scope.references[0].identifier.name).to.be.equal("test"); 83 | 84 | scope = scopeManager.scopes[2]; 85 | expect(scope.type).to.be.equal("function"); 86 | expect(scope.variables).to.have.length(1); 87 | expect(scope.variables[0].name).to.be.equal("arguments"); 88 | expect(scope.references).to.have.length(0); 89 | }); 90 | }); 91 | 92 | it("let is not hoistable#1", () => { 93 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 94 | const ast = espree(` 95 | var i = 42; (1) 96 | { 97 | i; // (2) ReferenceError at runtime. 98 | let i = 20; // (2) 99 | i; // (2) 100 | } 101 | `); 102 | 103 | const scopeManager = analyze(ast, { ecmaVersion }); 104 | 105 | expect(scopeManager.scopes).to.have.length(2); 106 | 107 | const globalScope = scopeManager.scopes[0]; 108 | 109 | expect(globalScope.type).to.be.equal("global"); 110 | expect(globalScope.variables).to.have.length(1); 111 | expect(globalScope.variables[0].name).to.be.equal("i"); 112 | expect(globalScope.references).to.have.length(1); 113 | 114 | const scope = scopeManager.scopes[1]; 115 | 116 | expect(scope.type).to.be.equal("block"); 117 | expect(scope.variables).to.have.length(1); 118 | expect(scope.variables[0].name).to.be.equal("i"); 119 | expect(scope.references).to.have.length(3); 120 | expect(scope.references[0].resolved).to.be.equal(scope.variables[0]); 121 | expect(scope.references[1].resolved).to.be.equal(scope.variables[0]); 122 | expect(scope.references[2].resolved).to.be.equal(scope.variables[0]); 123 | }); 124 | 125 | }); 126 | 127 | it("let is not hoistable#2", () => { 128 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 129 | const ast = espree(` 130 | (function () { 131 | var i = 42; // (1) 132 | i; // (1) 133 | { 134 | i; // (3) 135 | { 136 | i; // (2) 137 | let i = 20; // (2) 138 | i; // (2) 139 | } 140 | let i = 30; // (3) 141 | i; // (3) 142 | } 143 | i; // (1) 144 | }()); 145 | `); 146 | 147 | const scopeManager = analyze(ast, { ecmaVersion }); 148 | 149 | expect(scopeManager.scopes).to.have.length(4); 150 | 151 | const globalScope = scopeManager.scopes[0]; 152 | 153 | expect(globalScope.type).to.be.equal("global"); 154 | expect(globalScope.variables).to.have.length(0); 155 | expect(globalScope.references).to.have.length(0); 156 | 157 | let scope = scopeManager.scopes[1]; 158 | 159 | expect(scope.type).to.be.equal("function"); 160 | expect(scope.variables).to.have.length(2); 161 | expect(scope.variables[0].name).to.be.equal("arguments"); 162 | expect(scope.variables[1].name).to.be.equal("i"); 163 | const v1 = scope.variables[1]; 164 | 165 | expect(scope.references).to.have.length(3); 166 | expect(scope.references[0].resolved).to.be.equal(v1); 167 | expect(scope.references[1].resolved).to.be.equal(v1); 168 | expect(scope.references[2].resolved).to.be.equal(v1); 169 | 170 | scope = scopeManager.scopes[2]; 171 | expect(scope.type).to.be.equal("block"); 172 | expect(scope.variables).to.have.length(1); 173 | expect(scope.variables[0].name).to.be.equal("i"); 174 | const v3 = scope.variables[0]; 175 | 176 | expect(scope.references).to.have.length(3); 177 | expect(scope.references[0].resolved).to.be.equal(v3); 178 | expect(scope.references[1].resolved).to.be.equal(v3); 179 | expect(scope.references[2].resolved).to.be.equal(v3); 180 | 181 | scope = scopeManager.scopes[3]; 182 | expect(scope.type).to.be.equal("block"); 183 | expect(scope.variables).to.have.length(1); 184 | expect(scope.variables[0].name).to.be.equal("i"); 185 | const v2 = scope.variables[0]; 186 | 187 | expect(scope.references).to.have.length(3); 188 | expect(scope.references[0].resolved).to.be.equal(v2); 189 | expect(scope.references[1].resolved).to.be.equal(v2); 190 | expect(scope.references[2].resolved).to.be.equal(v2); 191 | }); 192 | }); 193 | }); 194 | 195 | // vim: set sw=4 ts=4 et tw=80 : 196 | -------------------------------------------------------------------------------- /tests/es6-catch.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 catch", () => { 29 | it("takes binding pattern", () => { 30 | const ast = espree(` 31 | try { 32 | } catch ({ a, b, c, d }) { 33 | let e = 20; 34 | a; 35 | b; 36 | c; 37 | d; 38 | } 39 | `); 40 | 41 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 42 | 43 | expect(scopeManager.scopes).to.have.length(4); 44 | 45 | let scope = scopeManager.scopes[0]; 46 | 47 | expect(scope.type).to.be.equal("global"); 48 | expect(scope.block.type).to.be.equal("Program"); 49 | expect(scope.isStrict).to.be.false; 50 | expect(scope.variables).to.have.length(0); 51 | expect(scope.references).to.have.length(0); 52 | 53 | scope = scopeManager.scopes[1]; 54 | expect(scope.type).to.be.equal("block"); 55 | expect(scope.block.type).to.be.equal("BlockStatement"); 56 | expect(scope.isStrict).to.be.false; 57 | expect(scope.variables).to.have.length(0); 58 | expect(scope.references).to.have.length(0); 59 | 60 | scope = scopeManager.scopes[2]; 61 | expect(scope.type).to.be.equal("catch"); 62 | expect(scope.block.type).to.be.equal("CatchClause"); 63 | expect(scope.isStrict).to.be.false; 64 | 65 | expect(scope.variables).to.have.length(4); 66 | expect(scope.variables[0].name).to.be.equal("a"); 67 | expect(scope.variables[1].name).to.be.equal("b"); 68 | expect(scope.variables[2].name).to.be.equal("c"); 69 | expect(scope.variables[3].name).to.be.equal("d"); 70 | expect(scope.references).to.have.length(0); 71 | 72 | scope = scopeManager.scopes[3]; 73 | expect(scope.type).to.be.equal("block"); 74 | expect(scope.block.type).to.be.equal("BlockStatement"); 75 | expect(scope.isStrict).to.be.false; 76 | expect(scope.variables).to.have.length(1); 77 | expect(scope.variables.map(variable => variable.name)).to.be.eql([ 78 | "e" 79 | ]); 80 | expect(scope.references.map(ref => ref.identifier.name)).to.be.eql([ 81 | "e", 82 | "a", 83 | "b", 84 | "c", 85 | "d" 86 | ]); 87 | }); 88 | }); 89 | 90 | // vim: set sw=4 ts=4 et tw=80 : 91 | -------------------------------------------------------------------------------- /tests/es6-export.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("export declaration", () => { 29 | 30 | // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-static-and-runtme-semantics-module-records 31 | it("should create variable bindings", () => { 32 | const ast = espree("export var v;"); 33 | 34 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 35 | 36 | expect(scopeManager.scopes).to.have.length(2); 37 | const globalScope = scopeManager.scopes[0]; 38 | 39 | expect(globalScope.type).to.be.equal("global"); 40 | expect(globalScope.variables).to.have.length(0); 41 | expect(globalScope.references).to.have.length(0); 42 | 43 | const scope = scopeManager.scopes[1]; 44 | 45 | expect(scope.type).to.be.equal("module"); 46 | expect(scope.variables).to.have.length(1); 47 | expect(scope.variables[0].name).to.be.equal("v"); 48 | expect(scope.variables[0].defs[0].type).to.be.equal("Variable"); 49 | expect(scope.references).to.have.length(0); 50 | }); 51 | 52 | it("should create function declaration bindings", () => { 53 | const ast = espree("export default function f(){};"); 54 | 55 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 56 | 57 | expect(scopeManager.scopes).to.have.length(3); 58 | const globalScope = scopeManager.scopes[0]; 59 | 60 | expect(globalScope.type).to.be.equal("global"); 61 | expect(globalScope.variables).to.have.length(0); 62 | expect(globalScope.references).to.have.length(0); 63 | 64 | let scope = scopeManager.scopes[1]; 65 | 66 | expect(scope.type).to.be.equal("module"); 67 | expect(scope.variables).to.have.length(1); 68 | expect(scope.variables[0].name).to.be.equal("f"); 69 | expect(scope.variables[0].defs[0].type).to.be.equal("FunctionName"); 70 | expect(scope.references).to.have.length(0); 71 | 72 | scope = scopeManager.scopes[2]; 73 | expect(scope.type).to.be.equal("function"); 74 | expect(scope.variables).to.have.length(1); 75 | expect(scope.variables[0].name).to.be.equal("arguments"); 76 | expect(scope.references).to.have.length(0); 77 | }); 78 | 79 | it("should export function expression", () => { 80 | const ast = espree("export default function(){};"); 81 | 82 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 83 | 84 | expect(scopeManager.scopes).to.have.length(3); 85 | const globalScope = scopeManager.scopes[0]; 86 | 87 | expect(globalScope.type).to.be.equal("global"); 88 | expect(globalScope.variables).to.have.length(0); 89 | expect(globalScope.references).to.have.length(0); 90 | 91 | let scope = scopeManager.scopes[1]; 92 | 93 | expect(scope.type).to.be.equal("module"); 94 | expect(scope.variables).to.have.length(0); 95 | expect(scope.references).to.have.length(0); 96 | 97 | scope = scopeManager.scopes[2]; 98 | expect(scope.type).to.be.equal("function"); 99 | expect(scope.variables).to.have.length(1); 100 | expect(scope.variables[0].name).to.be.equal("arguments"); 101 | expect(scope.references).to.have.length(0); 102 | }); 103 | 104 | it("should export literal", () => { 105 | const ast = espree("export default 42;"); 106 | 107 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 108 | 109 | expect(scopeManager.scopes).to.have.length(2); 110 | const globalScope = scopeManager.scopes[0]; 111 | 112 | expect(globalScope.type).to.be.equal("global"); 113 | expect(globalScope.variables).to.have.length(0); 114 | expect(globalScope.references).to.have.length(0); 115 | 116 | const scope = scopeManager.scopes[1]; 117 | 118 | expect(scope.type).to.be.equal("module"); 119 | expect(scope.variables).to.have.length(0); 120 | expect(scope.references).to.have.length(0); 121 | }); 122 | 123 | it("should refer exported references#1", () => { 124 | const ast = espree("const x = 1; export {x};"); 125 | 126 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 127 | 128 | expect(scopeManager.scopes).to.have.length(2); 129 | const globalScope = scopeManager.scopes[0]; 130 | 131 | expect(globalScope.type).to.be.equal("global"); 132 | expect(globalScope.variables).to.have.length(0); 133 | expect(globalScope.references).to.have.length(0); 134 | 135 | const scope = scopeManager.scopes[1]; 136 | 137 | expect(scope.type).to.be.equal("module"); 138 | expect(scope.variables).to.have.length(1); 139 | expect(scope.references).to.have.length(2); 140 | expect(scope.references[0].identifier.name).to.be.equal("x"); 141 | expect(scope.references[1].identifier.name).to.be.equal("x"); 142 | }); 143 | 144 | it("should refer exported references#2", () => { 145 | const ast = espree("const v = 1; export {v as x};"); 146 | 147 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 148 | 149 | expect(scopeManager.scopes).to.have.length(2); 150 | const globalScope = scopeManager.scopes[0]; 151 | 152 | expect(globalScope.type).to.be.equal("global"); 153 | expect(globalScope.variables).to.have.length(0); 154 | expect(globalScope.references).to.have.length(0); 155 | 156 | const scope = scopeManager.scopes[1]; 157 | 158 | expect(scope.type).to.be.equal("module"); 159 | expect(scope.variables).to.have.length(1); 160 | expect(scope.references).to.have.length(2); 161 | expect(scope.references[0].identifier.name).to.be.equal("v"); 162 | expect(scope.references[1].identifier.name).to.be.equal("v"); 163 | }); 164 | 165 | it("should not refer exported references from other source#1", () => { 166 | const ast = espree("export {x} from \"mod\";"); 167 | 168 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 169 | 170 | expect(scopeManager.scopes).to.have.length(2); 171 | const globalScope = scopeManager.scopes[0]; 172 | 173 | expect(globalScope.type).to.be.equal("global"); 174 | expect(globalScope.variables).to.have.length(0); 175 | expect(globalScope.references).to.have.length(0); 176 | 177 | const scope = scopeManager.scopes[1]; 178 | 179 | expect(scope.type).to.be.equal("module"); 180 | expect(scope.variables).to.have.length(0); 181 | expect(scope.references).to.have.length(0); 182 | }); 183 | 184 | it("should not refer exported references from other source#2", () => { 185 | const ast = espree("export {v as x} from \"mod\";"); 186 | 187 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 188 | 189 | expect(scopeManager.scopes).to.have.length(2); 190 | const globalScope = scopeManager.scopes[0]; 191 | 192 | expect(globalScope.type).to.be.equal("global"); 193 | expect(globalScope.variables).to.have.length(0); 194 | expect(globalScope.references).to.have.length(0); 195 | 196 | const scope = scopeManager.scopes[1]; 197 | 198 | expect(scope.type).to.be.equal("module"); 199 | expect(scope.variables).to.have.length(0); 200 | expect(scope.references).to.have.length(0); 201 | }); 202 | 203 | it("should not refer exported references from other source#3", () => { 204 | const ast = espree("export * from \"mod\";"); 205 | 206 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "module" }); 207 | 208 | expect(scopeManager.scopes).to.have.length(2); 209 | const globalScope = scopeManager.scopes[0]; 210 | 211 | expect(globalScope.type).to.be.equal("global"); 212 | expect(globalScope.variables).to.have.length(0); 213 | expect(globalScope.references).to.have.length(0); 214 | 215 | const scope = scopeManager.scopes[1]; 216 | 217 | expect(scope.type).to.be.equal("module"); 218 | expect(scope.variables).to.have.length(0); 219 | expect(scope.references).to.have.length(0); 220 | }); 221 | }); 222 | 223 | // vim: set sw=4 ts=4 et tw=80 : 224 | -------------------------------------------------------------------------------- /tests/es6-import.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { getSupportedEcmaVersions } from "./util/ecma-version.js"; 27 | import { analyze } from "../lib/index.js"; 28 | 29 | describe("import declaration", () => { 30 | 31 | // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-static-and-runtme-semantics-module-records 32 | it("should import names from source", () => { 33 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 34 | const ast = espree("import v from \"mod\";"); 35 | 36 | const scopeManager = analyze(ast, { ecmaVersion, sourceType: "module" }); 37 | 38 | expect(scopeManager.scopes).to.have.length(2); 39 | const globalScope = scopeManager.scopes[0]; 40 | 41 | expect(globalScope.type).to.be.equal("global"); 42 | expect(globalScope.variables).to.have.length(0); 43 | expect(globalScope.references).to.have.length(0); 44 | 45 | const scope = scopeManager.scopes[1]; 46 | 47 | expect(scope.type).to.be.equal("module"); 48 | expect(scope.isStrict).to.be.true; 49 | expect(scope.variables).to.have.length(1); 50 | expect(scope.variables[0].name).to.be.equal("v"); 51 | expect(scope.variables[0].defs[0].type).to.be.equal("ImportBinding"); 52 | expect(scope.references).to.have.length(0); 53 | }); 54 | 55 | }); 56 | 57 | it("should import namespaces", () => { 58 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 59 | const ast = espree("import * as ns from \"mod\";"); 60 | 61 | const scopeManager = analyze(ast, { ecmaVersion, sourceType: "module" }); 62 | 63 | expect(scopeManager.scopes).to.have.length(2); 64 | const globalScope = scopeManager.scopes[0]; 65 | 66 | expect(globalScope.type).to.be.equal("global"); 67 | expect(globalScope.variables).to.have.length(0); 68 | expect(globalScope.references).to.have.length(0); 69 | 70 | const scope = scopeManager.scopes[1]; 71 | 72 | expect(scope.type).to.be.equal("module"); 73 | expect(scope.isStrict).to.be.true; 74 | expect(scope.variables).to.have.length(1); 75 | expect(scope.variables[0].name).to.be.equal("ns"); 76 | expect(scope.variables[0].defs[0].type).to.be.equal("ImportBinding"); 77 | expect(scope.references).to.have.length(0); 78 | }); 79 | }); 80 | 81 | it("should import insided names#1", () => { 82 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 83 | const ast = espree("import {x} from \"mod\";"); 84 | 85 | const scopeManager = analyze(ast, { ecmaVersion, sourceType: "module" }); 86 | 87 | expect(scopeManager.scopes).to.have.length(2); 88 | const globalScope = scopeManager.scopes[0]; 89 | 90 | expect(globalScope.type).to.be.equal("global"); 91 | expect(globalScope.variables).to.have.length(0); 92 | expect(globalScope.references).to.have.length(0); 93 | 94 | const scope = scopeManager.scopes[1]; 95 | 96 | expect(scope.type).to.be.equal("module"); 97 | expect(scope.isStrict).to.be.true; 98 | expect(scope.variables).to.have.length(1); 99 | expect(scope.variables[0].name).to.be.equal("x"); 100 | expect(scope.variables[0].defs[0].type).to.be.equal("ImportBinding"); 101 | expect(scope.references).to.have.length(0); 102 | }); 103 | }); 104 | 105 | it("should import insided names#2", () => { 106 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 107 | const ast = espree("import {x as v} from \"mod\";"); 108 | 109 | const scopeManager = analyze(ast, { ecmaVersion, sourceType: "module" }); 110 | 111 | expect(scopeManager.scopes).to.have.length(2); 112 | const globalScope = scopeManager.scopes[0]; 113 | 114 | expect(globalScope.type).to.be.equal("global"); 115 | expect(globalScope.variables).to.have.length(0); 116 | expect(globalScope.references).to.have.length(0); 117 | 118 | const scope = scopeManager.scopes[1]; 119 | 120 | expect(scope.type).to.be.equal("module"); 121 | expect(scope.isStrict).to.be.true; 122 | expect(scope.variables).to.have.length(1); 123 | expect(scope.variables[0].name).to.be.equal("v"); 124 | expect(scope.variables[0].defs[0].type).to.be.equal("ImportBinding"); 125 | expect(scope.references).to.have.length(0); 126 | }); 127 | }); 128 | 129 | // TODO: Should parse it. 130 | // import from "mod"; 131 | }); 132 | 133 | // vim: set sw=4 ts=4 et tw=80 : 134 | -------------------------------------------------------------------------------- /tests/es6-iteration-scope.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { getSupportedEcmaVersions } from "./util/ecma-version.js"; 27 | import { analyze } from "../lib/index.js"; 28 | 29 | describe("ES6 iteration scope", () => { 30 | it("let materialize iteration scope for ForInStatement#1", () => { 31 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 32 | const ast = espree(` 33 | (function () { 34 | let i = 20; 35 | for (let i in i) { 36 | console.log(i); 37 | } 38 | }()); 39 | `); 40 | 41 | const scopeManager = analyze(ast, { ecmaVersion }); 42 | 43 | expect(scopeManager.scopes).to.have.length(4); 44 | 45 | let scope = scopeManager.scopes[0]; 46 | 47 | expect(scope.type).to.be.equal("global"); 48 | expect(scope.variables).to.have.length(0); 49 | 50 | scope = scopeManager.scopes[1]; 51 | expect(scope.type).to.be.equal("function"); 52 | expect(scope.variables).to.have.length(2); 53 | expect(scope.variables[0].name).to.be.equal("arguments"); 54 | expect(scope.variables[1].name).to.be.equal("i"); 55 | expect(scope.references).to.have.length(1); 56 | expect(scope.references[0].identifier.name).to.be.equal("i"); 57 | expect(scope.references[0].resolved).to.be.equal(scope.variables[1]); 58 | 59 | const iterScope = scope = scopeManager.scopes[2]; 60 | 61 | expect(scope.type).to.be.equal("for"); 62 | expect(scope.variables).to.have.length(1); 63 | expect(scope.variables[0].name).to.be.equal("i"); 64 | expect(scope.references).to.have.length(2); 65 | expect(scope.references[0].identifier.name).to.be.equal("i"); 66 | expect(scope.references[0].resolved).to.be.equal(scope.variables[0]); 67 | expect(scope.references[1].identifier.name).to.be.equal("i"); 68 | expect(scope.references[1].resolved).to.be.equal(scope.variables[0]); 69 | 70 | scope = scopeManager.scopes[3]; 71 | expect(scope.type).to.be.equal("block"); 72 | expect(scope.variables).to.have.length(0); 73 | expect(scope.references).to.have.length(2); 74 | expect(scope.references[0].identifier.name).to.be.equal("console"); 75 | expect(scope.references[0].resolved).to.be.equal(null); 76 | expect(scope.references[1].identifier.name).to.be.equal("i"); 77 | expect(scope.references[1].resolved).to.be.equal(iterScope.variables[0]); 78 | }); 79 | }); 80 | 81 | it("let materialize iteration scope for ForInStatement#2", () => { 82 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 83 | const ast = espree(` 84 | (function () { 85 | let i = 20; 86 | for (let { i, j, k } in i) { 87 | console.log(i); 88 | } 89 | }()); 90 | `); 91 | 92 | const scopeManager = analyze(ast, { ecmaVersion }); 93 | 94 | expect(scopeManager.scopes).to.have.length(4); 95 | 96 | let scope = scopeManager.scopes[0]; 97 | 98 | expect(scope.type).to.be.equal("global"); 99 | expect(scope.variables).to.have.length(0); 100 | 101 | scope = scopeManager.scopes[1]; 102 | expect(scope.type).to.be.equal("function"); 103 | expect(scope.variables).to.have.length(2); 104 | expect(scope.variables[0].name).to.be.equal("arguments"); 105 | expect(scope.variables[1].name).to.be.equal("i"); 106 | expect(scope.references).to.have.length(1); 107 | expect(scope.references[0].identifier.name).to.be.equal("i"); 108 | expect(scope.references[0].resolved).to.be.equal(scope.variables[1]); 109 | 110 | const iterScope = scope = scopeManager.scopes[2]; 111 | 112 | expect(scope.type).to.be.equal("for"); 113 | expect(scope.variables).to.have.length(3); 114 | expect(scope.variables[0].name).to.be.equal("i"); 115 | expect(scope.variables[1].name).to.be.equal("j"); 116 | expect(scope.variables[2].name).to.be.equal("k"); 117 | expect(scope.references).to.have.length(4); 118 | expect(scope.references[0].identifier.name).to.be.equal("i"); 119 | expect(scope.references[0].resolved).to.be.equal(scope.variables[0]); 120 | expect(scope.references[1].identifier.name).to.be.equal("j"); 121 | expect(scope.references[1].resolved).to.be.equal(scope.variables[1]); 122 | expect(scope.references[2].identifier.name).to.be.equal("k"); 123 | expect(scope.references[2].resolved).to.be.equal(scope.variables[2]); 124 | expect(scope.references[3].identifier.name).to.be.equal("i"); 125 | expect(scope.references[3].resolved).to.be.equal(scope.variables[0]); 126 | 127 | scope = scopeManager.scopes[3]; 128 | expect(scope.type).to.be.equal("block"); 129 | expect(scope.variables).to.have.length(0); 130 | expect(scope.references).to.have.length(2); 131 | expect(scope.references[0].identifier.name).to.be.equal("console"); 132 | expect(scope.references[0].resolved).to.be.equal(null); 133 | expect(scope.references[1].identifier.name).to.be.equal("i"); 134 | expect(scope.references[1].resolved).to.be.equal(iterScope.variables[0]); 135 | }); 136 | }); 137 | 138 | it("let materialize iteration scope for ForStatement#2", () => { 139 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 140 | const ast = espree(` 141 | (function () { 142 | let i = 20; 143 | let obj = {}; 144 | for (let { i, j, k } = obj; i < okok; ++i) { 145 | console.log(i, j, k); 146 | } 147 | }()); 148 | `); 149 | 150 | const scopeManager = analyze(ast, { ecmaVersion }); 151 | 152 | expect(scopeManager.scopes).to.have.length(4); 153 | 154 | let scope = scopeManager.scopes[0]; 155 | 156 | expect(scope.type).to.be.equal("global"); 157 | expect(scope.variables).to.have.length(0); 158 | 159 | const functionScope = scope = scopeManager.scopes[1]; 160 | 161 | expect(scope.type).to.be.equal("function"); 162 | expect(scope.variables).to.have.length(3); 163 | expect(scope.variables[0].name).to.be.equal("arguments"); 164 | expect(scope.variables[1].name).to.be.equal("i"); 165 | expect(scope.variables[2].name).to.be.equal("obj"); 166 | expect(scope.references).to.have.length(2); 167 | expect(scope.references[0].identifier.name).to.be.equal("i"); 168 | expect(scope.references[0].resolved).to.be.equal(scope.variables[1]); 169 | expect(scope.references[1].identifier.name).to.be.equal("obj"); 170 | expect(scope.references[1].resolved).to.be.equal(scope.variables[2]); 171 | 172 | const iterScope = scope = scopeManager.scopes[2]; 173 | 174 | expect(scope.type).to.be.equal("for"); 175 | expect(scope.variables).to.have.length(3); 176 | expect(scope.variables[0].name).to.be.equal("i"); 177 | expect(scope.variables[0].defs[0].type).to.be.equal("Variable"); 178 | expect(scope.variables[1].name).to.be.equal("j"); 179 | expect(scope.variables[1].defs[0].type).to.be.equal("Variable"); 180 | expect(scope.variables[2].name).to.be.equal("k"); 181 | expect(scope.variables[2].defs[0].type).to.be.equal("Variable"); 182 | expect(scope.references).to.have.length(7); 183 | expect(scope.references[0].identifier.name).to.be.equal("i"); 184 | expect(scope.references[0].resolved).to.be.equal(scope.variables[0]); 185 | expect(scope.references[1].identifier.name).to.be.equal("j"); 186 | expect(scope.references[1].resolved).to.be.equal(scope.variables[1]); 187 | expect(scope.references[2].identifier.name).to.be.equal("k"); 188 | expect(scope.references[2].resolved).to.be.equal(scope.variables[2]); 189 | expect(scope.references[3].identifier.name).to.be.equal("obj"); 190 | expect(scope.references[3].resolved).to.be.equal(functionScope.variables[2]); 191 | expect(scope.references[4].identifier.name).to.be.equal("i"); 192 | expect(scope.references[4].resolved).to.be.equal(scope.variables[0]); 193 | expect(scope.references[5].identifier.name).to.be.equal("okok"); 194 | expect(scope.references[5].resolved).to.be.null; 195 | expect(scope.references[6].identifier.name).to.be.equal("i"); 196 | expect(scope.references[6].resolved).to.be.equal(scope.variables[0]); 197 | 198 | scope = scopeManager.scopes[3]; 199 | expect(scope.type).to.be.equal("block"); 200 | expect(scope.variables).to.have.length(0); 201 | expect(scope.references).to.have.length(4); 202 | expect(scope.references[0].identifier.name).to.be.equal("console"); 203 | expect(scope.references[0].resolved).to.be.null; 204 | expect(scope.references[1].identifier.name).to.be.equal("i"); 205 | expect(scope.references[1].resolved).to.be.equal(iterScope.variables[0]); 206 | expect(scope.references[2].identifier.name).to.be.equal("j"); 207 | expect(scope.references[2].resolved).to.be.equal(iterScope.variables[1]); 208 | expect(scope.references[3].identifier.name).to.be.equal("k"); 209 | expect(scope.references[3].resolved).to.be.equal(iterScope.variables[2]); 210 | }); 211 | }); 212 | }); 213 | 214 | // vim: set sw=4 ts=4 et tw=80 : 215 | -------------------------------------------------------------------------------- /tests/es6-new-target.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 new.target", () => { 29 | it("should not make references of new.target", () => { 30 | const ast = espree(` 31 | class A { 32 | constructor() { 33 | new.target; 34 | } 35 | } 36 | `); 37 | 38 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 39 | 40 | expect(scopeManager.scopes).to.have.length(3); 41 | 42 | const scope = scopeManager.scopes[2]; 43 | 44 | expect(scope.type).to.be.equal("function"); 45 | expect(scope.block.type).to.be.equal("FunctionExpression"); 46 | expect(scope.isStrict).to.be.true; 47 | expect(scope.variables).to.have.length(1); 48 | expect(scope.variables[0].name).to.be.equal("arguments"); 49 | expect(scope.references).to.have.length(0); 50 | }); 51 | }); 52 | 53 | // vim: set sw=4 ts=4 et tw=80 : 54 | -------------------------------------------------------------------------------- /tests/es6-object.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 object", () => { 29 | it("method definition", () => { 30 | const ast = espree(` 31 | ({ 32 | constructor() { 33 | } 34 | })`); 35 | 36 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 37 | 38 | expect(scopeManager.scopes).to.have.length(2); 39 | 40 | let scope = scopeManager.scopes[0]; 41 | 42 | expect(scope.type).to.be.equal("global"); 43 | expect(scope.block.type).to.be.equal("Program"); 44 | expect(scope.isStrict).to.be.false; 45 | 46 | scope = scopeManager.scopes[1]; 47 | expect(scope.type).to.be.equal("function"); 48 | expect(scope.block.type).to.be.equal("FunctionExpression"); 49 | expect(scope.isStrict).to.be.false; 50 | expect(scope.variables).to.have.length(1); 51 | expect(scope.variables[0].name).to.be.equal("arguments"); 52 | expect(scope.references).to.have.length(0); 53 | }); 54 | 55 | it("computed property key may refer variables", () => { 56 | const ast = espree(` 57 | (function () { 58 | var yuyushiki = 42; 59 | ({ 60 | [yuyushiki]() { 61 | }, 62 | 63 | [yuyushiki + 40]() { 64 | } 65 | }) 66 | }()); 67 | `); 68 | 69 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 70 | 71 | expect(scopeManager.scopes).to.have.length(4); 72 | 73 | let scope = scopeManager.scopes[0]; 74 | 75 | expect(scope.type).to.be.equal("global"); 76 | expect(scope.block.type).to.be.equal("Program"); 77 | expect(scope.isStrict).to.be.false; 78 | 79 | scope = scopeManager.scopes[1]; 80 | expect(scope.type).to.be.equal("function"); 81 | expect(scope.block.type).to.be.equal("FunctionExpression"); 82 | expect(scope.isStrict).to.be.false; 83 | expect(scope.variables).to.have.length(2); 84 | expect(scope.variables[0].name).to.be.equal("arguments"); 85 | expect(scope.variables[1].name).to.be.equal("yuyushiki"); 86 | expect(scope.references).to.have.length(3); 87 | expect(scope.references[0].identifier.name).to.be.equal("yuyushiki"); 88 | expect(scope.references[1].identifier.name).to.be.equal("yuyushiki"); 89 | expect(scope.references[2].identifier.name).to.be.equal("yuyushiki"); 90 | }); 91 | }); 92 | 93 | // vim: set sw=4 ts=4 et tw=80 : 94 | -------------------------------------------------------------------------------- /tests/es6-rest-args.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 rest arguments", () => { 29 | it("materialize rest argument in scope", () => { 30 | const ast = espree(` 31 | function foo(...bar) { 32 | return bar; 33 | } 34 | `); 35 | 36 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 37 | 38 | expect(scopeManager.scopes).to.have.length(2); 39 | 40 | let scope = scopeManager.scopes[0]; 41 | 42 | expect(scope.type).to.be.equal("global"); 43 | expect(scope.block.type).to.be.equal("Program"); 44 | expect(scope.isStrict).to.be.false; 45 | expect(scope.variables).to.have.length(1); 46 | 47 | scope = scopeManager.scopes[1]; 48 | expect(scope.type).to.be.equal("function"); 49 | expect(scope.variables).to.have.length(2); 50 | expect(scope.variables[0].name).to.be.equal("arguments"); 51 | expect(scope.variables[1].name).to.be.equal("bar"); 52 | expect(scope.variables[1].defs[0].name.name).to.be.equal("bar"); 53 | expect(scope.variables[1].defs[0].rest).to.be.true; 54 | }); 55 | }); 56 | 57 | // vim: set sw=4 ts=4 et tw=80 : 58 | -------------------------------------------------------------------------------- /tests/es6-super.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 super", () => { 29 | it("is not handled as reference", () => { 30 | const ast = espree(` 31 | class Foo extends Bar { 32 | constructor() { 33 | super(); 34 | } 35 | 36 | method() { 37 | super.method(); 38 | } 39 | } 40 | `); 41 | 42 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 43 | 44 | expect(scopeManager.scopes).to.have.length(4); 45 | 46 | let scope = scopeManager.scopes[0]; 47 | 48 | expect(scope.type).to.be.equal("global"); 49 | expect(scope.variables).to.have.length(1); 50 | expect(scope.variables[0].name).to.be.equal("Foo"); 51 | expect(scope.references).to.have.length(0); 52 | 53 | scope = scopeManager.scopes[1]; 54 | expect(scope.type).to.be.equal("class"); 55 | expect(scope.variables).to.have.length(1); 56 | expect(scope.variables[0].name).to.be.equal("Foo"); 57 | expect(scope.references).to.have.length(1); 58 | expect(scope.references[0].identifier.name).to.be.equal("Bar"); 59 | 60 | scope = scopeManager.scopes[2]; 61 | expect(scope.type).to.be.equal("function"); 62 | expect(scope.variables).to.have.length(1); 63 | expect(scope.variables[0].name).to.be.equal("arguments"); 64 | expect(scope.references).to.have.length(0); // super is specially handled like `this`. 65 | 66 | scope = scopeManager.scopes[3]; 67 | expect(scope.type).to.be.equal("function"); 68 | expect(scope.variables).to.have.length(1); 69 | expect(scope.variables[0].name).to.be.equal("arguments"); 70 | expect(scope.references).to.have.length(0); // super is specially handled like `this`. 71 | }); 72 | }); 73 | 74 | // vim: set sw=4 ts=4 et tw=80 : 75 | -------------------------------------------------------------------------------- /tests/es6-switch.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { getSupportedEcmaVersions } from "./util/ecma-version.js"; 27 | import { analyze } from "../lib/index.js"; 28 | 29 | describe("ES6 switch", () => { 30 | it("materialize scope", () => { 31 | getSupportedEcmaVersions({ min: 6 }).forEach(ecmaVersion => { 32 | const ast = espree(` 33 | switch (ok) { 34 | case hello: 35 | let i = 20; 36 | i; 37 | break; 38 | 39 | default: 40 | let test = 30; 41 | test; 42 | } 43 | `); 44 | 45 | const scopeManager = analyze(ast, { ecmaVersion }); 46 | 47 | expect(scopeManager.scopes).to.have.length(2); 48 | 49 | let scope = scopeManager.scopes[0]; 50 | 51 | expect(scope.type).to.be.equal("global"); 52 | expect(scope.block.type).to.be.equal("Program"); 53 | expect(scope.isStrict).to.be.false; 54 | expect(scope.variables).to.have.length(0); 55 | expect(scope.references).to.have.length(1); 56 | expect(scope.references[0].identifier.name).to.be.equal("ok"); 57 | 58 | scope = scopeManager.scopes[1]; 59 | expect(scope.type).to.be.equal("switch"); 60 | expect(scope.block.type).to.be.equal("SwitchStatement"); 61 | expect(scope.isStrict).to.be.false; 62 | expect(scope.variables).to.have.length(2); 63 | expect(scope.variables[0].name).to.be.equal("i"); 64 | expect(scope.variables[1].name).to.be.equal("test"); 65 | expect(scope.references).to.have.length(5); 66 | expect(scope.references[0].identifier.name).to.be.equal("hello"); 67 | expect(scope.references[1].identifier.name).to.be.equal("i"); 68 | expect(scope.references[2].identifier.name).to.be.equal("i"); 69 | expect(scope.references[3].identifier.name).to.be.equal("test"); 70 | expect(scope.references[4].identifier.name).to.be.equal("test"); 71 | }); 72 | }); 73 | }); 74 | 75 | // vim: set sw=4 ts=4 et tw=80 : 76 | -------------------------------------------------------------------------------- /tests/es6-template-literal.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2014 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("ES6 template literal", () => { 29 | it("refer variables", () => { 30 | const ast = espree(` 31 | (function () { 32 | let i, j, k; 33 | function testing() { } 34 | let template = testing\`testing \${i} and \${j}\` 35 | return template; 36 | }()); 37 | `); 38 | 39 | const scopeManager = analyze(ast, { ecmaVersion: 6 }); 40 | 41 | expect(scopeManager.scopes).to.have.length(3); 42 | 43 | let scope = scopeManager.scopes[0]; 44 | 45 | expect(scope.type).to.be.equal("global"); 46 | expect(scope.block.type).to.be.equal("Program"); 47 | expect(scope.isStrict).to.be.false; 48 | expect(scope.variables).to.have.length(0); 49 | 50 | scope = scopeManager.scopes[1]; 51 | expect(scope.type).to.be.equal("function"); 52 | expect(scope.block.type).to.be.equal("FunctionExpression"); 53 | expect(scope.isStrict).to.be.false; 54 | expect(scope.variables).to.have.length(6); 55 | expect(scope.variables[0].name).to.be.equal("arguments"); 56 | expect(scope.variables[1].name).to.be.equal("i"); 57 | expect(scope.variables[2].name).to.be.equal("j"); 58 | expect(scope.variables[3].name).to.be.equal("k"); 59 | expect(scope.variables[4].name).to.be.equal("testing"); 60 | expect(scope.variables[5].name).to.be.equal("template"); 61 | expect(scope.references).to.have.length(5); 62 | expect(scope.references[0].identifier.name).to.be.equal("template"); 63 | expect(scope.references[1].identifier.name).to.be.equal("testing"); 64 | expect(scope.references[2].identifier.name).to.be.equal("i"); 65 | expect(scope.references[3].identifier.name).to.be.equal("j"); 66 | expect(scope.references[4].identifier.name).to.be.equal("template"); 67 | }); 68 | }); 69 | 70 | // vim: set sw=4 ts=4 et tw=80 : 71 | -------------------------------------------------------------------------------- /tests/export-star-as-ns-from-source.js: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import * as espree from "espree"; 3 | import { KEYS } from "eslint-visitor-keys"; 4 | import { analyze } from "../lib/index.js"; 5 | 6 | describe("export * as ns from 'source'", () => { 7 | let scopes; 8 | 9 | beforeEach(() => { 10 | const ast = espree.parse("export * as ns from 'source'", { 11 | ecmaVersion: 2020, 12 | sourceType: "module" 13 | }); 14 | const manager = analyze(ast, { 15 | ecmaVersion: 11, 16 | sourceType: "module", 17 | childVisitorKeys: KEYS 18 | }); 19 | 20 | scopes = [manager.globalScope, ...manager.globalScope.childScopes]; 21 | }); 22 | 23 | it("should not have any references", () => { 24 | for (const scope of scopes) { 25 | assert.strictEqual(scope.references.length, 0, scope.type); 26 | assert.strictEqual(scope.through.length, 0, scope.type); 27 | } 28 | }); 29 | 30 | it("should not have any variables", () => { 31 | for (const scope of scopes) { 32 | assert.strictEqual(scope.variables.length, 0, scope.type); 33 | } 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/fallback.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2016 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("fallback option", () => { 29 | it("should raise an error when it encountered an unknown node if no fallback.", () => { 30 | const ast = espree(` 31 | var foo = 0; 32 | `); 33 | 34 | ast.body[0].declarations[0].init.type = "NumericLiteral"; 35 | 36 | expect(() => { 37 | analyze(ast, { fallback: "none" }); 38 | }).to.throw("Unknown node type NumericLiteral"); 39 | }); 40 | 41 | it("should not raise an error even if it encountered an unknown node when fallback is iteration.", () => { 42 | const ast = espree(` 43 | var foo = 0; 44 | `); 45 | 46 | ast.body[0].declarations[0].init.type = "NumericLiteral"; 47 | 48 | analyze(ast); // default is `fallback: "iteration"` 49 | analyze(ast, { fallback: "iteration" }); 50 | }); 51 | 52 | it("should not raise an error even if it encountered an unknown node when fallback is a function.", () => { 53 | const ast = espree(` 54 | var foo = 0; 55 | `); 56 | 57 | ast.body[0].declarations[0].init.type = "NumericLiteral"; 58 | 59 | analyze(ast, { fallback: node => Object.keys(node) }); 60 | }); 61 | }); 62 | 63 | // vim: set sw=4 ts=4 et tw=80 : 64 | -------------------------------------------------------------------------------- /tests/function-expression-name.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("function name", () => { 29 | it("should create its special scope", () => { 30 | const ast = espree(` 31 | (function name() { 32 | }()); 33 | `); 34 | 35 | const scopeManager = analyze(ast); 36 | 37 | expect(scopeManager.scopes).to.have.length(3); 38 | const globalScope = scopeManager.scopes[0]; 39 | 40 | expect(globalScope.type).to.be.equal("global"); 41 | expect(globalScope.variables).to.have.length(0); 42 | expect(globalScope.references).to.have.length(0); 43 | expect(globalScope.isArgumentsMaterialized()).to.be.true; 44 | 45 | // Function expression name scope 46 | let scope = scopeManager.scopes[1]; 47 | 48 | expect(scope.type).to.be.equal("function-expression-name"); 49 | expect(scope.variables).to.have.length(1); 50 | expect(scope.variables[0].name).to.be.equal("name"); 51 | expect(scope.isArgumentsMaterialized()).to.be.true; 52 | expect(scope.references).to.have.length(0); 53 | expect(scope.upper === globalScope).to.be.true; 54 | 55 | // Function scope 56 | scope = scopeManager.scopes[2]; 57 | expect(scope.type).to.be.equal("function"); 58 | expect(scope.variables).to.have.length(1); 59 | expect(scope.variables[0].name).to.be.equal("arguments"); 60 | expect(scope.isArgumentsMaterialized()).to.be.false; 61 | expect(scope.references).to.have.length(0); 62 | expect(scope.upper === scopeManager.scopes[1]).to.be.true; 63 | }); 64 | }); 65 | 66 | 67 | // vim: set sw=4 ts=4 et tw=80 : 68 | -------------------------------------------------------------------------------- /tests/get-declared-variables.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Toru Nagashima 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import esrecurse from "esrecurse"; 26 | import espree from "./util/espree.js"; 27 | import { analyze } from "../lib/index.js"; 28 | 29 | const { visit } = esrecurse; 30 | 31 | describe("ScopeManager.prototype.getDeclaredVariables", () => { 32 | 33 | 34 | /** 35 | * Asserts that nodes of the given type declare expected variables. 36 | * @param {Object} ast The AST to check. 37 | * @param {string} type The node type to check. 38 | * @param {string[][]} expectedNamesList List of expected variable names for each node of the given type. 39 | * @throws {Error} If the list of declared variables doesn't match the expected list. 40 | * @returns {void} 41 | */ 42 | function verify(ast, type, expectedNamesList) { 43 | const scopeManager = analyze(ast, { 44 | ecmaVersion: 6, 45 | sourceType: "module" 46 | }); 47 | 48 | visit(ast, { 49 | [type](node) { 50 | const expected = expectedNamesList.shift(); 51 | const actual = scopeManager.getDeclaredVariables(node); 52 | 53 | expect(actual).to.have.length(expected.length); 54 | if (actual.length > 0) { 55 | const end = actual.length - 1; 56 | 57 | for (let i = 0; i <= end; i++) { 58 | expect(actual[i].name).to.be.equal(expected[i]); 59 | } 60 | } 61 | 62 | this.visitChildren(node); 63 | } 64 | }); 65 | 66 | expect(expectedNamesList).to.have.length(0); 67 | } 68 | 69 | it("should get variables that declared on `VariableDeclaration`", () => { 70 | const ast = espree(` 71 | var {a, x: [b], y: {c = 0}} = foo; 72 | let {d, x: [e], y: {f = 0}} = foo; 73 | const {g, x: [h], y: {i = 0}} = foo, {j, k = function() { let l; }} = bar; 74 | `); 75 | 76 | verify(ast, "VariableDeclaration", [ 77 | ["a", "b", "c"], 78 | ["d", "e", "f"], 79 | ["g", "h", "i", "j", "k"], 80 | ["l"] 81 | ]); 82 | }); 83 | 84 | 85 | it("should get variables that declared on `VariableDeclaration` in for-in/of", () => { 86 | const ast = espree(` 87 | for (var {a, x: [b], y: {c = 0}} in foo) { 88 | let g; 89 | } 90 | for (let {d, x: [e], y: {f = 0}} of foo) { 91 | let h; 92 | } 93 | `); 94 | 95 | verify(ast, "VariableDeclaration", [ 96 | ["a", "b", "c"], 97 | ["g"], 98 | ["d", "e", "f"], 99 | ["h"] 100 | ]); 101 | }); 102 | 103 | 104 | it("should get variables that declared on `VariableDeclarator`", () => { 105 | const ast = espree(` 106 | var {a, x: [b], y: {c = 0}} = foo; 107 | let {d, x: [e], y: {f = 0}} = foo; 108 | const {g, x: [h], y: {i = 0}} = foo, {j, k = function() { let l; }} = bar; 109 | `); 110 | 111 | verify(ast, "VariableDeclarator", [ 112 | ["a", "b", "c"], 113 | ["d", "e", "f"], 114 | ["g", "h", "i"], 115 | ["j", "k"], 116 | ["l"] 117 | ]); 118 | }); 119 | 120 | 121 | it("should get variables that declared on `FunctionDeclaration`", () => { 122 | const ast = espree(` 123 | function foo({a, x: [b], y: {c = 0}}, [d, e]) { 124 | let z; 125 | } 126 | function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) { 127 | let z; 128 | } 129 | `); 130 | 131 | verify(ast, "FunctionDeclaration", [ 132 | ["foo", "a", "b", "c", "d", "e"], 133 | ["bar", "f", "g", "h", "i", "j"] 134 | ]); 135 | }); 136 | 137 | 138 | it("should get variables that declared on `FunctionExpression`", () => { 139 | const ast = espree(` 140 | (function foo({a, x: [b], y: {c = 0}}, [d, e]) { 141 | let z; 142 | }); 143 | (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) { 144 | let z; 145 | }); 146 | `); 147 | 148 | verify(ast, "FunctionExpression", [ 149 | ["foo", "a", "b", "c", "d", "e"], 150 | ["bar", "f", "g", "h", "i", "j"], 151 | ["q"] 152 | ]); 153 | }); 154 | 155 | 156 | it("should get variables that declared on `ArrowFunctionExpression`", () => { 157 | const ast = espree(` 158 | (({a, x: [b], y: {c = 0}}, [d, e]) => { 159 | let z; 160 | }); 161 | (({f, x: [g], y: {h = 0}}, [i, j]) => { 162 | let z; 163 | }); 164 | `); 165 | 166 | verify(ast, "ArrowFunctionExpression", [ 167 | ["a", "b", "c", "d", "e"], 168 | ["f", "g", "h", "i", "j"] 169 | ]); 170 | }); 171 | 172 | 173 | it("should get variables that declared on `ClassDeclaration`", () => { 174 | const ast = espree(` 175 | class A { foo(x) { let y; } } 176 | class B { foo(x) { let y; } } 177 | `); 178 | 179 | verify(ast, "ClassDeclaration", [ 180 | ["A", "A"], // outer scope"s and inner scope"s. 181 | ["B", "B"] 182 | ]); 183 | }); 184 | 185 | 186 | it("should get variables that declared on `ClassExpression`", () => { 187 | const ast = espree(` 188 | (class A { foo(x) { let y; } }); 189 | (class B { foo(x) { let y; } }); 190 | `); 191 | 192 | verify(ast, "ClassExpression", [ 193 | ["A"], 194 | ["B"] 195 | ]); 196 | }); 197 | 198 | 199 | it("should get variables that declared on `CatchClause`", () => { 200 | const ast = espree(` 201 | try {} catch ({a, b}) { 202 | let x; 203 | try {} catch ({c, d}) { 204 | let y; 205 | } 206 | } 207 | `); 208 | 209 | verify(ast, "CatchClause", [ 210 | ["a", "b"], 211 | ["c", "d"] 212 | ]); 213 | }); 214 | 215 | 216 | it("should get variables that declared on `ImportDeclaration`", () => { 217 | const ast = espree(` 218 | import "aaa"; 219 | import * as a from "bbb"; 220 | import b, {c, x as d} from "ccc";`); 221 | 222 | verify(ast, "ImportDeclaration", [ 223 | [], 224 | ["a"], 225 | ["b", "c", "d"] 226 | ]); 227 | }); 228 | 229 | 230 | it("should get variables that declared on `ImportSpecifier`", () => { 231 | const ast = espree(` 232 | import "aaa"; 233 | import * as a from "bbb"; 234 | import b, {c, x as d} from "ccc";`); 235 | 236 | verify(ast, "ImportSpecifier", [ 237 | ["c"], 238 | ["d"] 239 | ]); 240 | }); 241 | 242 | 243 | it("should get variables that declared on `ImportDefaultSpecifier`", () => { 244 | const ast = espree(` 245 | import "aaa"; 246 | import * as a from "bbb"; 247 | import b, {c, x as d} from "ccc";`); 248 | 249 | verify(ast, "ImportDefaultSpecifier", [ 250 | ["b"] 251 | ]); 252 | }); 253 | 254 | 255 | it("should get variables that declared on `ImportNamespaceSpecifier`", () => { 256 | const ast = espree(` 257 | import "aaa"; 258 | import * as a from "bbb"; 259 | import b, {c, x as d} from "ccc";`); 260 | 261 | verify(ast, "ImportNamespaceSpecifier", [ 262 | ["a"] 263 | ]); 264 | }); 265 | 266 | 267 | it("should not get duplicate even if it's declared twice", () => { 268 | const ast = espree("var a = 0, a = 1;"); 269 | 270 | verify(ast, "VariableDeclaration", [ 271 | ["a"] 272 | ]); 273 | }); 274 | }); 275 | 276 | // vim: set sw=4 ts=4 et tw=80 : 277 | -------------------------------------------------------------------------------- /tests/global-increment.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("global increment", () => { 29 | it("becomes read/write", () => { 30 | const ast = espree("b++;"); 31 | 32 | const scopeManager = analyze(ast); 33 | 34 | expect(scopeManager.scopes).to.have.length(1); 35 | const globalScope = scopeManager.scopes[0]; 36 | 37 | expect(globalScope.type).to.be.equal("global"); 38 | expect(globalScope.variables).to.have.length(0); 39 | expect(globalScope.references).to.have.length(1); 40 | expect(globalScope.references[0].isReadWrite()).to.be.true; 41 | }); 42 | }); 43 | 44 | // vim: set sw=4 ts=4 et tw=80 : 45 | -------------------------------------------------------------------------------- /tests/implicit-global-reference.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013 Yusuke Suzuki 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 16 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | import { expect } from "chai"; 24 | import espree from "./util/espree.js"; 25 | import { analyze } from "../lib/index.js"; 26 | 27 | describe("implicit global reference", () => { 28 | it("assignments global scope", () => { 29 | const ast = espree(` 30 | var x = 20; 31 | x = 300; 32 | `); 33 | 34 | const scopes = analyze(ast).scopes; 35 | 36 | expect(scopes.map(scope => scope.variables.map(variable => variable.defs.map(def => def.type)))).to.be.eql( 37 | [ 38 | [ 39 | [ 40 | "Variable" 41 | ] 42 | ] 43 | ] 44 | ); 45 | 46 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]); 47 | }); 48 | 49 | it("assignments global scope without definition", () => { 50 | const ast = espree(` 51 | x = 300; 52 | x = 300; 53 | `); 54 | 55 | const scopes = analyze(ast).scopes; 56 | 57 | expect(scopes.map(scope => scope.variables.map(variable => variable.defs.map(def => def.type)))).to.be.eql( 58 | [ 59 | [ 60 | ] 61 | ] 62 | ); 63 | 64 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql( 65 | [ 66 | "x" 67 | ] 68 | ); 69 | }); 70 | 71 | it("assignments global scope without definition eval", () => { 72 | const ast = espree(` 73 | function inner() { 74 | eval(str); 75 | x = 300; 76 | } 77 | `); 78 | 79 | const scopes = analyze(ast).scopes; 80 | 81 | expect(scopes.map(scope => scope.variables.map(variable => variable.defs.map(def => def.type)))).to.be.eql( 82 | [ 83 | [ 84 | [ 85 | "FunctionName" 86 | ] 87 | ], 88 | [ 89 | [ 90 | ] 91 | ] 92 | ] 93 | ); 94 | 95 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]); 96 | }); 97 | 98 | it("assignment leaks", () => { 99 | const ast = espree(` 100 | function outer() { 101 | x = 20; 102 | } 103 | `); 104 | 105 | const scopes = analyze(ast).scopes; 106 | 107 | expect(scopes.map(scope => scope.variables.map(variable => variable.name))).to.be.eql( 108 | [ 109 | [ 110 | "outer" 111 | ], 112 | [ 113 | "arguments" 114 | ] 115 | ] 116 | ); 117 | 118 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql( 119 | [ 120 | "x" 121 | ] 122 | ); 123 | }); 124 | 125 | it("assignment doesn't leak", () => { 126 | const ast = espree(` 127 | function outer() { 128 | function inner() { 129 | x = 20; 130 | } 131 | var x; 132 | } 133 | `); 134 | 135 | const scopes = analyze(ast).scopes; 136 | 137 | expect(scopes.map(scope => scope.variables.map(variable => variable.name))).to.be.eql( 138 | [ 139 | [ 140 | "outer" 141 | ], 142 | [ 143 | "arguments", 144 | "inner", 145 | "x" 146 | ], 147 | [ 148 | "arguments" 149 | ] 150 | ] 151 | ); 152 | 153 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]); 154 | }); 155 | 156 | it("for-in-statement leaks", () => { 157 | const ast = espree(` 158 | function outer() { 159 | for (x in y) { } 160 | }`); 161 | 162 | const scopes = analyze(ast).scopes; 163 | 164 | expect(scopes.map(scope => scope.variables.map(variable => variable.name))).to.be.eql( 165 | [ 166 | [ 167 | "outer" 168 | ], 169 | [ 170 | "arguments" 171 | ] 172 | ] 173 | ); 174 | 175 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql( 176 | [ 177 | "x" 178 | ] 179 | ); 180 | }); 181 | 182 | it("for-in-statement doesn't leaks", () => { 183 | const ast = espree(` 184 | function outer() { 185 | function inner() { 186 | for (x in y) { } 187 | } 188 | var x; 189 | } 190 | `); 191 | 192 | const scopes = analyze(ast).scopes; 193 | 194 | expect(scopes.map(scope => scope.variables.map(variable => variable.name))).to.be.eql( 195 | [ 196 | [ 197 | "outer" 198 | ], 199 | [ 200 | "arguments", 201 | "inner", 202 | "x" 203 | ], 204 | [ 205 | "arguments" 206 | ] 207 | ] 208 | ); 209 | 210 | expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]); 211 | }); 212 | }); 213 | -------------------------------------------------------------------------------- /tests/implied-strict.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { getSupportedEcmaVersions } from "./util/ecma-version.js"; 27 | import { analyze } from "../lib/index.js"; 28 | 29 | describe("impliedStrict option", () => { 30 | it("ensures all user scopes are strict if ecmaVersion >= 5", () => { 31 | const ast = espree(` 32 | function foo() { 33 | function bar() { 34 | "use strict"; 35 | } 36 | } 37 | `); 38 | 39 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 40 | const scopeManager = analyze(ast, { ecmaVersion, impliedStrict: true }); 41 | 42 | expect(scopeManager.scopes).to.have.length(3); 43 | 44 | let scope = scopeManager.scopes[0]; 45 | 46 | expect(scope.type).to.be.equal("global"); 47 | expect(scope.block.type).to.be.equal("Program"); 48 | expect(scope.isStrict).to.be.true; 49 | 50 | scope = scopeManager.scopes[1]; 51 | expect(scope.type).to.be.equal("function"); 52 | expect(scope.block.type).to.be.equal("FunctionDeclaration"); 53 | expect(scope.isStrict).to.be.true; 54 | 55 | scope = scopeManager.scopes[2]; 56 | expect(scope.type).to.be.equal("function"); 57 | expect(scope.block.type).to.be.equal("FunctionDeclaration"); 58 | expect(scope.isStrict).to.be.true; 59 | }); 60 | }); 61 | 62 | it("ensures impliedStrict option is only effective when ecmaVersion option >= 5", () => { 63 | const ast = espree(` 64 | function foo() {} 65 | `); 66 | 67 | const scopeManager = analyze(ast, { ecmaVersion: 3, impliedStrict: true }); 68 | 69 | expect(scopeManager.scopes).to.have.length(2); 70 | 71 | let scope = scopeManager.scopes[0]; 72 | 73 | expect(scope.type).to.be.equal("global"); 74 | expect(scope.block.type).to.be.equal("Program"); 75 | expect(scope.isStrict).to.be.false; 76 | 77 | scope = scopeManager.scopes[1]; 78 | expect(scope.type).to.be.equal("function"); 79 | expect(scope.block.type).to.be.equal("FunctionDeclaration"); 80 | expect(scope.isStrict).to.be.false; 81 | }); 82 | 83 | it("omits a nodejs global scope when ensuring all user scopes are strict", () => { 84 | const ast = espree(` 85 | function foo() {} 86 | `); 87 | 88 | const scopeManager = analyze(ast, { ecmaVersion: 5, nodejsScope: true, impliedStrict: true }); 89 | 90 | expect(scopeManager.scopes).to.have.length(3); 91 | 92 | let scope = scopeManager.scopes[0]; 93 | 94 | expect(scope.type).to.be.equal("global"); 95 | expect(scope.block.type).to.be.equal("Program"); 96 | expect(scope.isStrict).to.be.false; 97 | 98 | scope = scopeManager.scopes[1]; 99 | expect(scope.type).to.be.equal("function"); 100 | expect(scope.block.type).to.be.equal("Program"); 101 | expect(scope.isStrict).to.be.true; 102 | 103 | scope = scopeManager.scopes[2]; 104 | expect(scope.type).to.be.equal("function"); 105 | expect(scope.block.type).to.be.equal("FunctionDeclaration"); 106 | expect(scope.isStrict).to.be.true; 107 | }); 108 | 109 | it("omits a module global scope when ensuring all user scopes are strict", () => { 110 | const ast = espree("function foo() {}"); 111 | 112 | const scopeManager = analyze(ast, { ecmaVersion: 6, impliedStrict: true, sourceType: "module" }); 113 | 114 | expect(scopeManager.scopes).to.have.length(3); 115 | 116 | let scope = scopeManager.scopes[0]; 117 | 118 | expect(scope.type).to.be.equal("global"); 119 | expect(scope.block.type).to.be.equal("Program"); 120 | expect(scope.isStrict).to.be.false; 121 | 122 | scope = scopeManager.scopes[1]; 123 | expect(scope.type).to.be.equal("module"); 124 | expect(scope.isStrict).to.be.true; 125 | 126 | scope = scopeManager.scopes[2]; 127 | expect(scope.type).to.be.equal("function"); 128 | expect(scope.block.type).to.be.equal("FunctionDeclaration"); 129 | expect(scope.isStrict).to.be.true; 130 | }); 131 | }); 132 | 133 | // vim: set sw=4 ts=4 et tw=80 : 134 | -------------------------------------------------------------------------------- /tests/label.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("label", () => { 29 | it("should not create variables", () => { 30 | const ast = espree("function bar() { q: for(;;) { break q; } }"); 31 | 32 | const scopeManager = analyze(ast); 33 | 34 | expect(scopeManager.scopes).to.have.length(2); 35 | const globalScope = scopeManager.scopes[0]; 36 | 37 | expect(globalScope.type).to.be.equal("global"); 38 | expect(globalScope.variables).to.have.length(1); 39 | expect(globalScope.variables[0].name).to.be.equal("bar"); 40 | expect(globalScope.references).to.have.length(0); 41 | 42 | const scope = scopeManager.scopes[1]; 43 | 44 | expect(scope.type).to.be.equal("function"); 45 | expect(scope.variables).to.have.length(1); 46 | expect(scope.variables[0].name).to.be.equal("arguments"); 47 | expect(scope.isArgumentsMaterialized()).to.be.false; 48 | expect(scope.references).to.have.length(0); 49 | }); 50 | 51 | it("should count child node references", () => { 52 | const ast = espree(` 53 | var foo = 5; 54 | 55 | label: while (true) { 56 | console.log(foo); 57 | break; 58 | } 59 | `); 60 | 61 | const scopeManager = analyze(ast); 62 | 63 | expect(scopeManager.scopes).to.have.length(1); 64 | const globalScope = scopeManager.scopes[0]; 65 | 66 | expect(globalScope.type).to.be.equal("global"); 67 | expect(globalScope.variables).to.have.length(1); 68 | expect(globalScope.variables[0].name).to.be.equal("foo"); 69 | expect(globalScope.through.length).to.be.equal(3); 70 | expect(globalScope.through[2].identifier.name).to.be.equal("foo"); 71 | expect(globalScope.through[2].isRead()).to.be.true; 72 | }); 73 | }); 74 | 75 | // vim: set sw=4 ts=4 et tw=80 : 76 | -------------------------------------------------------------------------------- /tests/nodejs-scope.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("nodejsScope option", () => { 29 | 30 | it("creates a function scope following the global scope immediately when nodejscope: true", () => { 31 | const ast = espree(` 32 | "use strict"; 33 | var hello = 20; 34 | `); 35 | 36 | const scopeManager = analyze(ast, { ecmaVersion: 6, nodejsScope: true }); 37 | 38 | expect(scopeManager.scopes).to.have.length(2); 39 | expect(scopeManager.isGlobalReturn()).to.be.true; 40 | 41 | let scope = scopeManager.scopes[0]; 42 | 43 | expect(scope.type).to.be.equal("global"); 44 | expect(scope.block.type).to.be.equal("Program"); 45 | expect(scope.isStrict).to.be.false; 46 | expect(scope.variables).to.have.length(0); 47 | 48 | scope = scopeManager.scopes[1]; 49 | expect(scope.type).to.be.equal("function"); 50 | expect(scope.block.type).to.be.equal("Program"); 51 | expect(scope.isStrict).to.be.true; 52 | expect(scope.variables).to.have.length(2); 53 | expect(scope.variables[0].name).to.be.equal("arguments"); 54 | expect(scope.variables[1].name).to.be.equal("hello"); 55 | }); 56 | 57 | it("creates a function scope following the global scope immediately when sourceType:commonjs", () => { 58 | const ast = espree(` 59 | "use strict"; 60 | var hello = 20; 61 | `); 62 | 63 | const scopeManager = analyze(ast, { ecmaVersion: 6, sourceType: "commonjs" }); 64 | 65 | expect(scopeManager.scopes).to.have.length(2); 66 | expect(scopeManager.isGlobalReturn()).to.be.true; 67 | 68 | let scope = scopeManager.scopes[0]; 69 | 70 | expect(scope.type).to.be.equal("global"); 71 | expect(scope.block.type).to.be.equal("Program"); 72 | expect(scope.isStrict).to.be.false; 73 | expect(scope.variables).to.have.length(0); 74 | 75 | scope = scopeManager.scopes[1]; 76 | expect(scope.type).to.be.equal("function"); 77 | expect(scope.block.type).to.be.equal("Program"); 78 | expect(scope.isStrict).to.be.true; 79 | expect(scope.variables).to.have.length(2); 80 | expect(scope.variables[0].name).to.be.equal("arguments"); 81 | expect(scope.variables[1].name).to.be.equal("hello"); 82 | }); 83 | 84 | it("creates a function scope following the global scope immediately and creates module scope", () => { 85 | const ast = espree("import {x as v} from 'mod';"); 86 | 87 | const scopeManager = analyze(ast, { ecmaVersion: 6, nodejsScope: true, sourceType: "module" }); 88 | 89 | expect(scopeManager.scopes).to.have.length(3); 90 | expect(scopeManager.isGlobalReturn()).to.be.true; 91 | 92 | let scope = scopeManager.scopes[0]; 93 | 94 | expect(scope.type).to.be.equal("global"); 95 | expect(scope.block.type).to.be.equal("Program"); 96 | expect(scope.isStrict).to.be.false; 97 | expect(scope.variables).to.have.length(0); 98 | 99 | scope = scopeManager.scopes[1]; 100 | expect(scope.type).to.be.equal("function"); 101 | expect(scope.block.type).to.be.equal("Program"); 102 | expect(scope.isStrict).to.be.false; 103 | expect(scope.variables).to.have.length(1); 104 | expect(scope.variables[0].name).to.be.equal("arguments"); 105 | 106 | scope = scopeManager.scopes[2]; 107 | expect(scope.type).to.be.equal("module"); 108 | expect(scope.variables).to.have.length(1); 109 | expect(scope.variables[0].name).to.be.equal("v"); 110 | expect(scope.variables[0].defs[0].type).to.be.equal("ImportBinding"); 111 | expect(scope.references).to.have.length(0); 112 | }); 113 | }); 114 | 115 | // vim: set sw=4 ts=4 et tw=80 : 116 | -------------------------------------------------------------------------------- /tests/object-expression.js: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { analyze } from "../lib/index.js"; 3 | 4 | describe("object expression", () => { 5 | it("doesn't require property type", () => { 6 | 7 | // Hardcoded AST. Escope adds an extra "Property" 8 | // key/value to ObjectExpressions, so we're not using 9 | // it parse a program string. 10 | const ast = { 11 | type: "Program", 12 | body: [{ 13 | type: "VariableDeclaration", 14 | declarations: [{ 15 | type: "VariableDeclarator", 16 | id: { 17 | type: "Identifier", 18 | name: "a" 19 | }, 20 | init: { 21 | type: "ObjectExpression", 22 | properties: [{ 23 | kind: "init", 24 | key: { 25 | type: "Identifier", 26 | name: "foo" 27 | }, 28 | value: { 29 | type: "Identifier", 30 | name: "a" 31 | } 32 | }] 33 | } 34 | }] 35 | }] 36 | }; 37 | 38 | const scope = analyze(ast).scopes[0]; 39 | 40 | expect(scope.variables).to.have.length(1); 41 | expect(scope.references).to.have.length(2); 42 | expect(scope.variables[0].name).to.be.equal("a"); 43 | expect(scope.references[0].identifier.name).to.be.equal("a"); 44 | expect(scope.references[1].identifier.name).to.be.equal("a"); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/optimistic.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013 Yusuke Suzuki 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 16 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | import { expect } from "chai"; 24 | import espree from "./util/espree.js"; 25 | import { analyze } from "../lib/index.js"; 26 | 27 | describe("optimistic", () => { 28 | it("direct call to eval", () => { 29 | const ast = espree(` 30 | function outer() { 31 | eval(str); 32 | var i = 20; 33 | function inner() { 34 | i; 35 | } 36 | } 37 | `); 38 | 39 | const scopes = analyze(ast, { optimistic: true }).scopes; 40 | 41 | expect(scopes.map(scope => scope.variables.map(variable => variable.name))).to.be.eql( 42 | [ 43 | [ 44 | "outer" 45 | ], 46 | [ 47 | "arguments", 48 | "i", 49 | "inner" 50 | ], 51 | [ 52 | "arguments" 53 | ] 54 | ] 55 | ); 56 | }); 57 | 58 | it("with statement", () => { 59 | const ast = espree(` 60 | function outer() { 61 | eval(str); 62 | var i = 20; 63 | with (obj) { 64 | i; 65 | } 66 | } 67 | `, "script"); 68 | 69 | const scopes = analyze(ast, { optimistic: true }).scopes; 70 | 71 | expect(scopes.map(scope => scope.variables.map(variable => variable.name))).to.be.eql( 72 | [ 73 | [ 74 | "outer" 75 | ], 76 | [ 77 | "arguments", 78 | "i" 79 | ], 80 | [ 81 | ] 82 | ] 83 | ); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /tests/typescript.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Typescript scope tests 3 | * @author Reyad Attiyat 4 | */ 5 | 6 | //------------------------------------------------------------------------------ 7 | // Requirements 8 | //------------------------------------------------------------------------------ 9 | 10 | import { expect } from "chai"; 11 | import typescriptEslintParser from "@typescript-eslint/parser"; 12 | import { analyze } from "../lib/index.js"; 13 | 14 | const { parse } = typescriptEslintParser; 15 | 16 | //------------------------------------------------------------------------------ 17 | // Tests 18 | //------------------------------------------------------------------------------ 19 | 20 | describe("typescript", () => { 21 | describe("multiple call signatures", () => { 22 | it("should create a function scope", () => { 23 | const ast = parse(` 24 | function foo(bar: number): number; 25 | function foo(bar: string): string; 26 | function foo(bar: string | number): string | number { 27 | return bar; 28 | } 29 | `, { 30 | range: true 31 | }); 32 | 33 | const scopeManager = analyze(ast); 34 | 35 | expect(scopeManager.scopes).to.have.length(2); 36 | 37 | const globalScope = scopeManager.scopes[0]; 38 | 39 | expect(globalScope.type).to.be.equal("global"); 40 | expect(globalScope.variables).to.have.length(1); 41 | expect(globalScope.references).to.have.length(4); 42 | expect(globalScope.isArgumentsMaterialized()).to.be.true; 43 | 44 | const scope = scopeManager.scopes[1]; 45 | 46 | expect(scope.type).to.be.equal("function"); 47 | expect(scope.variables).to.have.length(2); 48 | expect(scope.variables[0].name).to.be.equal("arguments"); 49 | expect(scope.isArgumentsMaterialized()).to.be.false; 50 | expect(scope.references).to.have.length(1); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /tests/use-strict.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for "use strict" directives. 3 | * @author Milos Djermanovic 4 | */ 5 | 6 | import assert from "assert"; 7 | import * as espree from "espree"; 8 | import { KEYS } from "eslint-visitor-keys"; 9 | import { analyze } from "../lib/index.js"; 10 | import { getSupportedEcmaVersions } from "./util/ecma-version.js"; 11 | 12 | /** 13 | * Asserts `isStrict` property value for the given scope and all its descendants. 14 | * @param {Scope} scope The scope to check. 15 | * @param {boolean} expected The expected value for `isStrict` property. 16 | * @throws {AssertionError} If `isStrict` property value of `scope` or of 17 | * any of its descendant scopes doesn't match `expected`. 18 | * @returns {void} 19 | */ 20 | function assertIsStrictRecursively(scope, expected) { 21 | assert.strictEqual(scope.isStrict, expected); 22 | 23 | scope.childScopes.forEach(childScope => { 24 | assertIsStrictRecursively(childScope, expected); 25 | }); 26 | } 27 | 28 | describe("'use strict' directives", () => { 29 | 30 | it("should be ignored when ecmaVersion = 3", () => { 31 | const ecmaVersion = 3; 32 | 33 | const ast = espree.parse(` 34 | "use strict"; 35 | function a() { 36 | "use strict"; 37 | function b() { 38 | foo(); 39 | } 40 | } 41 | `, { ecmaVersion, range: true }); 42 | 43 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 44 | 45 | assertIsStrictRecursively(globalScope, false); 46 | }); 47 | 48 | it("at the top level should make all scopes strict when ecmaVersion >= 5", () => { 49 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 50 | const ast = espree.parse(` 51 | "use strict"; 52 | if (a) { 53 | foo(); 54 | } 55 | function b() { 56 | if (c) { 57 | foo(); 58 | } 59 | function d() { 60 | if (e) { 61 | foo(); 62 | } 63 | } 64 | } 65 | `, { ecmaVersion, range: true }); 66 | 67 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 68 | 69 | assertIsStrictRecursively(globalScope, true); 70 | }); 71 | }); 72 | 73 | it("at the function level should make the function's scope and all its descendants strict when ecmaVersion >= 5", () => { 74 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 75 | const ast = espree.parse(` 76 | function a() { 77 | "use strict"; 78 | if (b) { 79 | foo(); 80 | } 81 | function c() { 82 | if (d) { 83 | foo(); 84 | } 85 | } 86 | } 87 | function e() { 88 | if (f) { 89 | foo(); 90 | } 91 | } 92 | `, { ecmaVersion, range: true }); 93 | 94 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 95 | 96 | assert.strictEqual(globalScope.isStrict, false); 97 | assertIsStrictRecursively(globalScope.childScopes[0], true); // function a() { ... } 98 | assertIsStrictRecursively(globalScope.childScopes[1], false); // function e() { ... } 99 | }); 100 | }); 101 | 102 | it("can be with single quotes at the top level", () => { 103 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 104 | const ast = espree.parse(` 105 | 'use strict'; 106 | `, { ecmaVersion, range: true }); 107 | 108 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 109 | 110 | assert.strictEqual(globalScope.isStrict, true); 111 | }); 112 | }); 113 | 114 | it("can be without the semicolon at the top level", () => { 115 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 116 | const ast = espree.parse(` 117 | "use strict" 118 | foo() 119 | `, { ecmaVersion, range: true }); 120 | 121 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 122 | 123 | assert.strictEqual(globalScope.isStrict, true); 124 | }); 125 | }); 126 | 127 | it("can be anywhere in the directive prologue at the top level", () => { 128 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 129 | const ast = espree.parse(` 130 | "foo"; 131 | "use strict"; 132 | `, { ecmaVersion, range: true }); 133 | 134 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 135 | 136 | assert.strictEqual(globalScope.isStrict, true); 137 | }); 138 | }); 139 | 140 | it("cannot be after the directive prologue at the top level", () => { 141 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 142 | const ast = espree.parse(` 143 | foo(); 144 | "use strict"; 145 | `, { ecmaVersion, range: true }); 146 | 147 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 148 | 149 | assert.strictEqual(globalScope.isStrict, false); 150 | }); 151 | }); 152 | 153 | it("cannot contain escapes at the top level", () => { 154 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 155 | const ast = espree.parse(` 156 | "use \\strict"; 157 | `, { ecmaVersion, range: true }); 158 | 159 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 160 | 161 | assert.strictEqual(globalScope.isStrict, false); 162 | }); 163 | }); 164 | 165 | it("cannot be parenthesized at the top level", () => { 166 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 167 | const ast = espree.parse(` 168 | ("use strict"); 169 | `, { ecmaVersion, range: true }); 170 | 171 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 172 | 173 | assert.strictEqual(globalScope.isStrict, false); 174 | }); 175 | }); 176 | 177 | it("can be with single quotes in a function", () => { 178 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 179 | const ast = espree.parse(` 180 | function foo() { 181 | 'use strict'; 182 | } 183 | `, { ecmaVersion, range: true }); 184 | 185 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 186 | 187 | assert.strictEqual(globalScope.isStrict, false); 188 | assert.strictEqual(globalScope.childScopes.length, 1); 189 | assert.strictEqual(globalScope.childScopes[0].type, "function"); 190 | assert.strictEqual(globalScope.childScopes[0].isStrict, true); 191 | }); 192 | }); 193 | 194 | it("can be without the semicolon in a function", () => { 195 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 196 | const ast = espree.parse(` 197 | function foo() { 198 | "use strict" 199 | bar() 200 | } 201 | `, { ecmaVersion, range: true }); 202 | 203 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 204 | 205 | assert.strictEqual(globalScope.isStrict, false); 206 | assert.strictEqual(globalScope.childScopes.length, 1); 207 | assert.strictEqual(globalScope.childScopes[0].type, "function"); 208 | assert.strictEqual(globalScope.childScopes[0].isStrict, true); 209 | }); 210 | }); 211 | 212 | it("can be anywhere in the directive prologue in a function", () => { 213 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 214 | const ast = espree.parse(` 215 | function foo() { 216 | "foo"; 217 | "use strict"; 218 | } 219 | `, { ecmaVersion, range: true }); 220 | 221 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 222 | 223 | assert.strictEqual(globalScope.isStrict, false); 224 | assert.strictEqual(globalScope.childScopes.length, 1); 225 | assert.strictEqual(globalScope.childScopes[0].type, "function"); 226 | assert.strictEqual(globalScope.childScopes[0].isStrict, true); 227 | }); 228 | }); 229 | 230 | it("cannot be after the directive prologue in a function", () => { 231 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 232 | const ast = espree.parse(` 233 | function foo() { 234 | bar(); 235 | "use strict"; 236 | } 237 | `, { ecmaVersion, range: true }); 238 | 239 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 240 | 241 | assert.strictEqual(globalScope.isStrict, false); 242 | assert.strictEqual(globalScope.childScopes.length, 1); 243 | assert.strictEqual(globalScope.childScopes[0].type, "function"); 244 | assert.strictEqual(globalScope.childScopes[0].isStrict, false); 245 | }); 246 | }); 247 | 248 | it("cannot contain escapes in a function", () => { 249 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 250 | const ast = espree.parse(` 251 | function foo() { 252 | "use \\strict"; 253 | } 254 | `, { ecmaVersion, range: true }); 255 | 256 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 257 | 258 | assert.strictEqual(globalScope.isStrict, false); 259 | assert.strictEqual(globalScope.childScopes.length, 1); 260 | assert.strictEqual(globalScope.childScopes[0].type, "function"); 261 | assert.strictEqual(globalScope.childScopes[0].isStrict, false); 262 | }); 263 | }); 264 | 265 | it("cannot be parenthesized in a function", () => { 266 | getSupportedEcmaVersions({ min: 5 }).forEach(ecmaVersion => { 267 | const ast = espree.parse(` 268 | function foo() { 269 | ("use strict"); 270 | } 271 | `, { ecmaVersion, range: true }); 272 | 273 | const { globalScope } = analyze(ast, { ecmaVersion, childVisitorKeys: KEYS }); 274 | 275 | assert.strictEqual(globalScope.isStrict, false); 276 | assert.strictEqual(globalScope.childScopes.length, 1); 277 | assert.strictEqual(globalScope.childScopes[0].type, "function"); 278 | assert.strictEqual(globalScope.childScopes[0].isStrict, false); 279 | }); 280 | }); 281 | }); 282 | -------------------------------------------------------------------------------- /tests/util/ecma-version.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview ECMAScript version utilities used in tests 3 | * @author Milos Djermanovic 4 | */ 5 | 6 | import * as espree from "espree"; 7 | 8 | /** 9 | * Gets an array of supported ECMAScript versions. 10 | * @param {number} [min] Minimum ECMAScript version to get. This should be a revision-based number (3, 5, 6, 7...). 11 | * @returns {number[]} Supported ECMAScript versions, including their year-based representations. 12 | * @example 13 | * 14 | * getSupportedEcmaVersions() 15 | * // => [3, 5, 6, 2015, 7, 2016, 8, 2017, 9, 2018, 10, 2019, 11, 2020, 12, 2021, 13, 2022] 16 | * 17 | * getSupportedEcmaVersions({ min: 8 }) 18 | * // => [8, 2017, 9, 2018, 10, 2019, 11, 2020, 12, 2021, 13, 2022] 19 | * 20 | */ 21 | export function getSupportedEcmaVersions({ min = 0 } = {}) { 22 | return espree.supportedEcmaVersions 23 | .filter( 24 | ecmaVersion => ecmaVersion >= min 25 | ) 26 | .flatMap( 27 | ecmaVersion => (ecmaVersion >= 6 ? [ecmaVersion, ecmaVersion + 2009] : [ecmaVersion]) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /tests/util/espree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Yusuke Suzuki 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | import * as espree from "espree"; 26 | 27 | /** 28 | * Parse into Espree AST. 29 | * @param {string} code The code 30 | * @param {"module"|"script"} [sourceType="module"] The source type 31 | * @returns {Object} The parsed Espree AST 32 | */ 33 | export default function(code, sourceType = "module") { 34 | return espree.parse(code, { 35 | range: true, 36 | ecmaVersion: 7, 37 | sourceType 38 | }); 39 | } 40 | 41 | /* vim: set sw=4 ts=4 et tw=80 : */ 42 | -------------------------------------------------------------------------------- /tests/with-scope.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // Copyright (C) 2015 Yusuke Suzuki 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import { expect } from "chai"; 25 | import espree from "./util/espree.js"; 26 | import { analyze } from "../lib/index.js"; 27 | 28 | describe("with", () => { 29 | it("creates scope", () => { 30 | const ast = espree(` 31 | (function () { 32 | with (obj) { 33 | testing; 34 | } 35 | }()); 36 | `, "script"); 37 | 38 | const scopeManager = analyze(ast); 39 | 40 | expect(scopeManager.scopes).to.have.length(3); 41 | const globalScope = scopeManager.scopes[0]; 42 | 43 | expect(globalScope.type).to.be.equal("global"); 44 | expect(globalScope.variables).to.have.length(0); 45 | expect(globalScope.references).to.have.length(0); 46 | 47 | let scope = scopeManager.scopes[1]; 48 | 49 | expect(scope.type).to.be.equal("function"); 50 | expect(scope.variables).to.have.length(1); 51 | expect(scope.variables[0].name).to.be.equal("arguments"); 52 | expect(scope.isArgumentsMaterialized()).to.be.false; 53 | expect(scope.references).to.have.length(1); 54 | expect(scope.references[0].resolved).to.be.null; 55 | 56 | scope = scopeManager.scopes[2]; 57 | expect(scope.type).to.be.equal("with"); 58 | expect(scope.variables).to.have.length(0); 59 | expect(scope.isArgumentsMaterialized()).to.be.true; 60 | expect(scope.references).to.have.length(1); 61 | expect(scope.references[0].resolved).to.be.null; 62 | }); 63 | }); 64 | 65 | // vim: set sw=4 ts=4 et tw=80 : 66 | -------------------------------------------------------------------------------- /tools/update-version.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Script to update lib/version.js to the value in package.json 3 | * @author Nicholas C. Zakas 4 | */ 5 | 6 | import fs from "fs"; 7 | 8 | /* 9 | * IMPORTANT: This must be run *before* Rollup so the built package will have 10 | * the correct version number exported. 11 | * 12 | * This is necessary because ESM can't import JSON files directly and we want 13 | * this value to be available in the browser as well as in Node.js. 14 | */ 15 | 16 | const pkg = JSON.parse(fs.readFileSync("./package.json", "utf8")); 17 | 18 | fs.writeFileSync("lib/version.js", `const version = "${pkg.version}";\n\nexport default version;\n`); 19 | --------------------------------------------------------------------------------