From 8d2cca72897e091fa1269e011740fda7364fab1c Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 24 Feb 2020 16:52:23 -0800 Subject: [PATCH] Mass reformat ts/js/md with ESLint quick fixes and prettier (#113) --- .eslintignore | 5 +- .eslintrc.json | 1 + .prettierignore | 6 + .prettierrc | 6 + .vscode/extensions.json | 6 + .vscode/settings.json | 17 + README.md | 81 +- client/README.md | 42 +- client/package-lock.json | 46 +- client/package.json | 4 +- client/schemas/pyrightconfig.schema.json | 15 +- client/src/extension.ts | 185 +- client/src/progress.ts | 33 +- docs/build-debug.md | 11 +- docs/command-line.md | 47 +- docs/commands.md | 5 +- docs/comments.md | 5 +- docs/configuration.md | 118 +- docs/getting-started.md | 8 +- docs/import-resolution.md | 7 +- docs/internals.md | 35 +- docs/settings.md | 4 +- docs/type-stubs.md | 27 +- index.debug.js | 2 +- index.js | 2 +- package-lock.json | 85 +- package.json | 11 +- server/.eslintignore | 3 +- server/.vscode/settings.json | 4 - server/copyTypeshedFallback.js | 2 +- server/customInstallServerIntoExtension.js | 89 +- server/jest.config.js | 48 +- server/package-lock.json | 228 +- server/package.json | 18 +- server/src/analyzer/analyzerFileInfo.ts | 16 +- server/src/analyzer/analyzerNodeInfo.ts | 34 +- server/src/analyzer/binder.ts | 519 +-- server/src/analyzer/checker.ts | 517 ++- server/src/analyzer/circularDependency.ts | 23 +- server/src/analyzer/codeFlow.ts | 66 +- server/src/analyzer/commentUtils.ts | 34 +- server/src/analyzer/declaration.ts | 50 +- server/src/analyzer/declarationUtils.ts | 22 +- server/src/analyzer/docStringUtils.ts | 18 +- server/src/analyzer/importResolver.ts | 418 +- server/src/analyzer/importResult.ts | 14 +- server/src/analyzer/importStatementUtils.ts | 96 +- server/src/analyzer/parseTreeCleaner.ts | 24 +- server/src/analyzer/parseTreeUtils.ts | 315 +- server/src/analyzer/parseTreeWalker.ts | 98 +- server/src/analyzer/program.ts | 265 +- server/src/analyzer/pythonPathUtils.ts | 77 +- server/src/analyzer/scope.ts | 32 +- server/src/analyzer/scopeUtils.ts | 16 +- server/src/analyzer/service.ts | 219 +- server/src/analyzer/sourceFile.ts | 327 +- server/src/analyzer/staticExpressions.ts | 137 +- server/src/analyzer/symbol.ts | 31 +- server/src/analyzer/symbolNameUtils.ts | 26 +- server/src/analyzer/symbolUtils.ts | 14 +- server/src/analyzer/testWalker.ts | 16 +- server/src/analyzer/typeEvaluator.ts | 3460 ++++++++++------- server/src/analyzer/typeStubWriter.ts | 176 +- server/src/analyzer/typeUtils.ts | 284 +- server/src/analyzer/typeVarMap.ts | 22 +- server/src/analyzer/types.ts | 204 +- server/src/commands/commands.ts | 14 +- server/src/commands/createTypeStub.ts | 8 +- server/src/commands/quickActionCommand.ts | 2 +- server/src/common/collectionUtils.ts | 75 +- server/src/common/commandLineOptions.ts | 18 +- server/src/common/configOptions.ts | 319 +- server/src/common/console.ts | 16 +- server/src/common/core.ts | 38 +- server/src/common/debug.ts | 25 +- server/src/common/diagnostic.ts | 18 +- server/src/common/diagnosticRules.ts | 16 +- server/src/common/diagnosticSink.ts | 16 +- server/src/common/editAction.ts | 16 +- server/src/common/extensions.ts | 18 +- server/src/common/pathUtils.ts | 232 +- server/src/common/positionUtils.ts | 26 +- server/src/common/pythonVersion.ts | 24 +- server/src/common/textRange.ts | 17 +- server/src/common/textRangeCollection.ts | 26 +- server/src/common/timing.ts | 18 +- server/src/common/vfs.ts | 45 +- server/src/index.d.ts | 18 +- server/src/languageServerBase.ts | 234 +- .../analyzerServiceExecutor.ts | 38 +- .../src/languageService/codeActionProvider.ts | 40 +- .../src/languageService/completionProvider.ts | 498 ++- .../src/languageService/definitionProvider.ts | 28 +- .../languageService/documentSymbolProvider.ts | 85 +- server/src/languageService/hoverProvider.ts | 56 +- server/src/languageService/importSorter.ts | 89 +- server/src/languageService/quickActions.ts | 46 +- .../src/languageService/referencesProvider.ts | 80 +- .../languageService/signatureHelpProvider.ts | 27 +- server/src/parser/characterStream.ts | 20 +- server/src/parser/characters.ts | 98 +- server/src/parser/parseNodes.ts | 217 +- server/src/parser/parser.ts | 569 +-- server/src/parser/stringTokenUtils.ts | 24 +- server/src/parser/tokenizer.ts | 263 +- server/src/parser/tokenizerTypes.ts | 106 +- server/src/parser/unicode.ts | 2581 ++++++++++-- server/src/pyright.ts | 98 +- server/src/server.ts | 2 +- server/src/tests/checker.test.ts | 186 +- server/src/tests/collectionUtils.test.ts | 31 +- server/src/tests/config.test.ts | 35 +- server/src/tests/debug.test.ts | 46 +- server/src/tests/filesystem.test.ts | 20 +- server/src/tests/fourSlashParser.test.ts | 31 +- .../tests/fourslash/dataclass1.fourslash.ts | 14 +- .../tests/fourslash/dataclass3.fourslash.ts | 10 +- .../tests/fourslash/dataclass4.fourslash.ts | 41 +- .../tests/fourslash/dataclass5.fourslash.ts | 30 +- .../tests/fourslash/dataclass6.fourslash.ts | 24 +- .../tests/fourslash/dataclass7.fourslash.ts | 39 +- server/src/tests/fourslash/fourslash.ts | 52 +- .../fourslash/importnotresolved.fourslash.ts | 11 +- .../missingTypeStub.codeAction.fourslash.ts | 16 +- .../missingTypeStub.command.fourslash.ts | 6 +- .../fourslash/missingTypeStub.fourslash.ts | 2 +- ...singTypeStub.invokeCodeAction.fourslash.ts | 4 +- .../harness/fourslash/fourSlashParser.ts | 93 +- server/src/tests/harness/fourslash/runner.ts | 35 +- .../harness/fourslash/testLanguageService.ts | 21 +- .../src/tests/harness/fourslash/testState.ts | 244 +- server/src/tests/harness/host.ts | 50 +- server/src/tests/harness/utils.ts | 49 +- server/src/tests/harness/vfs/factory.ts | 49 +- server/src/tests/harness/vfs/filesystem.ts | 696 +++- .../src/tests/harness/vfs/pathValidation.ts | 105 +- server/src/tests/pathUtils.test.ts | 41 +- server/src/tests/stringUtils.test.ts | 8 +- server/src/tests/testState.test.ts | 16 +- server/src/tests/testUtils.ts | 53 +- server/src/tests/tokenizer.test.ts | 186 +- server/webpack.config-cli.js | 19 +- server/webpack.config-server.js | 19 +- 143 files changed, 11201 insertions(+), 6061 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json delete mode 100644 server/.vscode/settings.json diff --git a/.eslintignore b/.eslintignore index 1cb9e4788..14d5870e3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,4 @@ -**/tests/fourslash/** \ No newline at end of file +**/dist/** +**/out/** +**/node_modules/** +**/tests/fourslash/** diff --git a/.eslintrc.json b/.eslintrc.json index f37021fc7..7a4e2f264 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "extends": [ "eslint:recommended", + "prettier", "plugin:@typescript-eslint/recommended" ], "env": { diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..5ab5c6cd2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +*.json +**/dist/** +**/out/** +**/client/server/** +**/typeshed-fallback/** +**/.github/** diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..9d1ee8539 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "tabWidth": 4, + "useTabs": false, + "printWidth": 120 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..3c4ccadca --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..1a0d49825 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } +} diff --git a/README.md b/README.md index 36e35242a..3e126b592 100644 --- a/README.md +++ b/README.md @@ -3,54 +3,64 @@ # Static type checker for Python ### Speed -Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fast incremental updates when files are modified. + +Pyright is a fast type checker meant for large Python source bases. It can run in a "watch" mode and performs fast incremental updates when files are modified. ### Configurability -Pyright supports [configuration files](/docs/configuration.md) that provide granular control over settings. Different “execution environments” can be associated with subdirectories within a source base. Each environment can specify different module search paths, python language versions, and platform targets. + +Pyright supports [configuration files](/docs/configuration.md) that provide granular control over settings. Different "execution environments" can be associated with subdirectories within a source base. Each environment can specify different module search paths, python language versions, and platform targets. ### Type Checking Features -* [PEP 484](https://www.python.org/dev/peps/pep-0484/) type hints including generics -* [PEP 526](https://www.python.org/dev/peps/pep-0526/) syntax for variable annotations -* [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping -* [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries -* Type inference for function return values, instance variables, class variables, and globals -* Smart type constraints that understand conditional code flow constructs like if/else statements + +- [PEP 484](https://www.python.org/dev/peps/pep-0484/) type hints including generics +- [PEP 526](https://www.python.org/dev/peps/pep-0526/) syntax for variable annotations +- [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping +- [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries +- Type inference for function return values, instance variables, class variables, and globals +- Smart type constraints that understand conditional code flow constructs like if/else statements ### VS Code Integration + Pyright ships as both a command-line tool and a VS Code extension that provides many powerful features that help improve programming efficiency. ### VS Code Language Features + The VS Code extension supports many time-saving language features including: -* Intelligent type completion of keywords, symbols, and import names appears when editing -* Import statements are automatically inserted when necessary for type completions -* Signature completion tips help when filling in arguments for a call -* Hover over symbols to provide type information and doc strings -* Find Definitions to quickly go to the location of a symbol’s definition -* Find References to find all references to a symbol within a code base -* Rename Symbol to rename all references to a symbol within a code base -* Find Symbols within the current document or within the entire workspace -* Organize Imports command for automatically ordering imports according to PEP8 rules -* Type stub generation for third-party libraries +- Intelligent type completion of keywords, symbols, and import names appears when editing +- Import statements are automatically inserted when necessary for type completions +- Signature completion tips help when filling in arguments for a call +- Hover over symbols to provide type information and doc strings +- Find Definitions to quickly go to the location of a symbol's definition +- Find References to find all references to a symbol within a code base +- Rename Symbol to rename all references to a symbol within a code base +- Find Symbols within the current document or within the entire workspace +- Organize Imports command for automatically ordering imports according to PEP8 rules +- Type stub generation for third-party libraries ### Built-in Type Stubs + Pyright includes a recent copy of the stdlib type stubs from [Typeshed](https://github.com/python/typeshed). It can be configured to use another (perhaps more recent or modified) copy of the Typeshed type stubs. Of course, it also works with custom type stub files that are part of your project. ### Command-line Tool or Visual Studio Code Extension + Pyright includes both a [command-line tool](/docs/command-line.md) and an [extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-pyright.pyright) that implements the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/). For rich Python editing and debugging capabilities with Visual Studio Code, be sure to also install the official [Microsoft Python extension for Visual Studio Code](https://marketplace.visualstudio.com/itemdetails?itemName=ms-python.python) as Pyright only provides syntax and type checking. - ## Installation + ### VS Code Extension + You can install the latest-published version of the Pyright VS Code extension directly from VS Code. Simply open the extensions panel and search for `pyright`. ### Vim + For vim/neovim users, you can install [coc-pyright](https://github.com/fannheyward/coc-pyright), Pyright extension for coc.nvim. ### Command-line -The latest version of the command-line tool can be installed with npm, which is part of node. If you don't have a recent version of node on your system, install that first from [nodejs.org](nodejs.org). + +The latest version of the command-line tool can be installed with npm, which is part of node. If you don't have a recent version of node on your system, install that first from [nodejs.org](nodejs.org). To install pyright globally: `npm install -g pyright` @@ -64,42 +74,41 @@ Once installed, you can run the tool from the command line as follows: To update to the latest version: `sudo npm update -g pyright` - ## Using Pyright with VS Code Python Extension -Pyright provides some features that overlap with functionality provided by the standard VS Code Python extension: “hover”, type completion, definitions, references, rename symbols, etc. You may see duplicate results if Pyright is installed alongside the Python extension. There is currently no way to disable this functionality in the Python extension. If you want to disable these features in Pyright, there is a setting to do so: `pyright.disableLanguageServices`. +Pyright provides some features that overlap with functionality provided by the standard VS Code Python extension: "hover", type completion, definitions, references, rename symbols, etc. You may see duplicate results if Pyright is installed alongside the Python extension. There is currently no way to disable this functionality in the Python extension. If you want to disable these features in Pyright, there is a setting to do so: `pyright.disableLanguageServices`. ## Documentation -* [Getting Started with Type Checking](/docs/getting-started.md) -* [Command-line Options](/docs/command-line.md) -* [Configuration](/docs/configuration.md) -* [Settings](/docs/settings.md) -* [Comments](/docs/comments.md) -* [Import Resolution](/docs/import-resolution.md) -* [Type Stubs](/docs/type-stubs.md) -* [Commands](/docs/commands.md) -* [Building & Debugging](/docs/build-debug.md) -* [Pyright Internals](/docs/internals.md) +- [Getting Started with Type Checking](/docs/getting-started.md) +- [Command-line Options](/docs/command-line.md) +- [Configuration](/docs/configuration.md) +- [Settings](/docs/settings.md) +- [Comments](/docs/comments.md) +- [Import Resolution](/docs/import-resolution.md) +- [Type Stubs](/docs/type-stubs.md) +- [Commands](/docs/commands.md) +- [Building & Debugging](/docs/build-debug.md) +- [Pyright Internals](/docs/internals.md) ## Limitations + Pyright currently provides support for Python 3.0 and newer. There is currently no plan to support older versions. - ## Community + Do you have questions about Pyright or Python type annotations in general? Post your questions in this [gitter channel](https://gitter.im/microsoft-pyright/community). - ## FAQ + **Q:** What is the difference between pyright and the [Microsoft Python Visual Studio Code plugin](https://github.com/Microsoft/vscode-python)? -**A:** Pyright is focused on type checking. The Python VS Code plugin is Microsoft’s officially-supported extension for VS Code and provides a diverse array of features including debugging, test case management, linter plugins, and more. Pyright can be used alongside the Microsoft Python extension. +**A:** Pyright is focused on type checking. The Python VS Code plugin is Microsoft's officially-supported extension for VS Code and provides a diverse array of features including debugging, test case management, linter plugins, and more. Pyright can be used alongside the Microsoft Python extension. **Q:** What is the long-term plan for Pyright? **A:** Pyright is a side project with no dedicated team. There is no guarantee of continued development on the project. If you find it useful, feel free to use it and contribute to the code base. - ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. diff --git a/client/README.md b/client/README.md index 72ce71c50..dbc5bccef 100644 --- a/client/README.md +++ b/client/README.md @@ -1,35 +1,39 @@ # Static type checker for Python ### Speed -Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fast incremental updates when files are modified. + +Pyright is a fast type checker meant for large Python source bases. It can run in a "watch" mode and performs fast incremental updates when files are modified. ### Configurability -Pyright supports [configuration files](/docs/configuration.md) that provide granular control over settings. Different “execution environments” can be associated with subdirectories within a source base. Each environment can specify different module search paths, python language versions, and platform targets. + +Pyright supports [configuration files](/docs/configuration.md) that provide granular control over settings. Different "execution environments" can be associated with subdirectories within a source base. Each environment can specify different module search paths, python language versions, and platform targets. ### Type Checking Features -* [PEP 484](https://www.python.org/dev/peps/pep-0484/) type hints including generics -* [PEP 526](https://www.python.org/dev/peps/pep-0526/) syntax for variable annotations -* [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping -* [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries -* Type inference for function return values, instance variables, class variables, and globals -* Smart type constraints that understand conditional code flow constructs like if/else statements + +- [PEP 484](https://www.python.org/dev/peps/pep-0484/) type hints including generics +- [PEP 526](https://www.python.org/dev/peps/pep-0526/) syntax for variable annotations +- [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping +- [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries +- Type inference for function return values, instance variables, class variables, and globals +- Smart type constraints that understand conditional code flow constructs like if/else statements ### VS Code Language Features + The VS Code extension supports many time-saving language features including: -* Intelligent type completion of keywords, symbols, and import names appears when editing -* Import statements are automatically inserted when necessary for type completions -* Signature completion tips help when filling in arguments for a call -* Hover over symbols to provide type information and doc strings -* Find Definitions to quickly go to the location of a symbol’s definition -* Find References to find all references to a symbol within a code base -* Rename Symbol to rename all references to a symbol within a code base -* Find Symbols within the current document or within the entire workspace -* Organize Imports command for automatically ordering imports according to PEP8 rules -* Type stub generation for third-party libraries +- Intelligent type completion of keywords, symbols, and import names appears when editing +- Import statements are automatically inserted when necessary for type completions +- Signature completion tips help when filling in arguments for a call +- Hover over symbols to provide type information and doc strings +- Find Definitions to quickly go to the location of a symbol's definition +- Find References to find all references to a symbol within a code base +- Rename Symbol to rename all references to a symbol within a code base +- Find Symbols within the current document or within the entire workspace +- Organize Imports command for automatically ordering imports according to PEP8 rules +- Type stub generation for third-party libraries ### Built-in Type Stubs + Pyright includes a recent copy of the stdlib type stubs from [Typeshed](https://github.com/python/typeshed). It can be configured to use another (perhaps more recent or modified) copy of the Typeshed type stubs. Of course, it also works with custom type stub files that are part of your project. - For more details, refer to the [README](https://github.com/Microsoft/pyright/blob/master/README.md) on the Pyright GitHub site. diff --git a/client/package-lock.json b/client/package-lock.json index e3fcac15b..e104f6442 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@types/node": { - "version": "12.12.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", - "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==", + "version": "13.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.4.tgz", + "integrity": "sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw==", "dev": true }, "agent-base": { @@ -838,9 +838,9 @@ } }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -1016,9 +1016,9 @@ } }, "typescript": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", - "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", + "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==", "dev": true }, "uc.micro": { @@ -1082,9 +1082,9 @@ } }, "vsce": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.70.0.tgz", - "integrity": "sha512-mBTbVrWL348jODwfmaR+yXrlzb8EABGCT067C4shKOXriWiuMQi4/uCbFm6TUBcfnzTYLJv+DKa0VnKU8yEAjA==", + "version": "1.73.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.73.0.tgz", + "integrity": "sha512-6W37Ebbkj3uF3WhT+SCfRtsneRQEFcGvf/XYz+b6OAgDCj4gPurWyDVrqw/HLsbP1WflGIyUfVZ8t5M7kQp6Uw==", "dev": true, "requires": { "azure-devops-node-api": "^7.2.0", @@ -1125,9 +1125,9 @@ } }, "vscode-jsonrpc": { - "version": "5.0.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz", - "integrity": "sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", + "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" }, "vscode-languageclient": { "version": "5.3.0-next.9", @@ -1155,18 +1155,18 @@ } }, "vscode-languageserver-protocol": { - "version": "3.15.0-next.9", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz", - "integrity": "sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g==", + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", + "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", "requires": { - "vscode-jsonrpc": "^5.0.0-next.2", - "vscode-languageserver-types": "^3.15.0-next.5" + "vscode-jsonrpc": "^5.0.1", + "vscode-languageserver-types": "3.15.1" } }, "vscode-languageserver-types": { - "version": "3.15.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz", - "integrity": "sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw==" + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", + "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" }, "vscode-test": { "version": "0.4.3", diff --git a/client/package.json b/client/package.json index 6a7880ca6..f4bdfe6c0 100644 --- a/client/package.json +++ b/client/package.json @@ -116,8 +116,8 @@ "postinstall": "node ./node_modules/vscode/bin/install" }, "devDependencies": { - "typescript": "^3.7.3", - "vsce": "^1.70.0", + "typescript": "^3.8.2", + "vsce": "^1.73.0", "vscode": "^1.1.36" }, "dependencies": { diff --git a/client/schemas/pyrightconfig.schema.json b/client/schemas/pyrightconfig.schema.json index 25d001e1f..2a4e9e903 100644 --- a/client/schemas/pyrightconfig.schema.json +++ b/client/schemas/pyrightconfig.schema.json @@ -5,8 +5,17 @@ "definitions": { "diagnostic": { "anyOf": [ - { "type": "boolean" }, - { "type": "string", "enum": [ "none", "warning", "error" ] } + { + "type": "boolean" + }, + { + "type": "string", + "enum": [ + "none", + "warning", + "error" + ] + } ] } }, @@ -93,7 +102,7 @@ "enableTypeIgnoreComments": { "$id": "#/properties/enableTypeIgnoreComments", "type": "boolean", - "title": "Allow “# type: ignore” comments", + "title": "Allow \"# type: ignore\" comments", "default": "true" }, "reportTypeshedErrors": { diff --git a/client/src/extension.ts b/client/src/extension.ts index 222a1a8f0..3d8a235ac 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,102 +1,117 @@ /* -* extension.ts -* -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* -* Provides client for Pyright Python language server. This portion runs -* in the context of the VS Code process and talks to the server, which -* runs in another process. -*/ + * extension.ts + * + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * + * Provides client for Pyright Python language server. This portion runs + * in the context of the VS Code process and talks to the server, which + * runs in another process. + */ import * as fs from 'fs'; import * as path from 'path'; import { commands, ExtensionContext, Position, Range, TextEditor, TextEditorEdit } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TextEdit,TransportKind } from 'vscode-languageclient'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TextEdit, TransportKind } from 'vscode-languageclient'; import { Commands } from '../../server/src/commands/commands'; import { ProgressReporting } from './progress'; export function activate(context: ExtensionContext) { - const bundlePath = context.asAbsolutePath(path.join('server', 'server.bundle.js')); - const nonBundlePath = context.asAbsolutePath(path.join('server', 'src', 'server.js')); - const debugOptions = { execArgv: ["--nolazy", "--inspect=6600"] }; - - // If the extension is launched in debug mode, then the debug server options are used. - const serverOptions: ServerOptions = { - run : { module: bundlePath, transport: TransportKind.ipc }, - // In debug mode, use the non-bundled code if it's present. The production - // build includes only the bundled package, so we don't want to crash if - // someone starts the production extension in debug mode. - debug: { module: fs.existsSync(nonBundlePath) ? nonBundlePath : bundlePath, - transport: TransportKind.ipc, options: debugOptions } - } - - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for python source files. - documentSelector: [{ - scheme: 'file', - language: 'python' - }], - synchronize: { - // Synchronize the setting section to the server. - configurationSection: ['python', 'pyright'] - } - } - - // Create the language client and start the client. - const languageClient = new LanguageClient('python', 'Pyright', serverOptions, clientOptions); - const disposable = languageClient.start(); - - // Push the disposable to the context's subscriptions so that the - // client can be deactivated on extension deactivation. - context.subscriptions.push(disposable); + const bundlePath = context.asAbsolutePath(path.join('server', 'server.bundle.js')); + const nonBundlePath = context.asAbsolutePath(path.join('server', 'src', 'server.js')); + const debugOptions = { execArgv: ['--nolazy', '--inspect=6600'] }; - // Allocate a progress reporting object. - const progressReporting = new ProgressReporting(languageClient); - context.subscriptions.push(progressReporting); + // If the extension is launched in debug mode, then the debug server options are used. + const serverOptions: ServerOptions = { + run: { module: bundlePath, transport: TransportKind.ipc }, + // In debug mode, use the non-bundled code if it's present. The production + // build includes only the bundled package, so we don't want to crash if + // someone starts the production extension in debug mode. + debug: { + module: fs.existsSync(nonBundlePath) ? nonBundlePath : bundlePath, + transport: TransportKind.ipc, + options: debugOptions + } + }; - // Register our custom commands. - const textEditorCommands = [Commands.orderImports, Commands.addMissingOptionalToParam]; - textEditorCommands.forEach(commandName => { - context.subscriptions.push(commands.registerTextEditorCommand(commandName, - (editor: TextEditor, edit: TextEditorEdit, ...args: any[]) => { - const cmd = { - command: commandName, - arguments: [editor.document.uri.toString(), ...args] - }; - - languageClient.sendRequest('workspace/executeCommand', cmd).then((edits: TextEdit[] | undefined) => { - if (edits && edits.length > 0) { - editor.edit(editBuilder => { - edits.forEach(edit => { - const startPos = new Position(edit.range.start.line, edit.range.start.character); - const endPos = new Position(edit.range.end.line, edit.range.end.character); - const range = new Range(startPos, endPos); - editBuilder.replace(range, edit.newText); - }); - }); - } - }); - }, - () => { - // Error received. For now, do nothing. - })); - }); + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for python source files. + documentSelector: [ + { + scheme: 'file', + language: 'python' + } + ], + synchronize: { + // Synchronize the setting section to the server. + configurationSection: ['python', 'pyright'] + } + }; - const genericCommands = [ Commands.createTypeStub, ]; - genericCommands.forEach(command => { - context.subscriptions.push(commands.registerCommand(command, (...args: any[]) => { - languageClient.sendRequest('workspace/executeCommand', { command, arguments: args }); - })); - }); + // Create the language client and start the client. + const languageClient = new LanguageClient('python', 'Pyright', serverOptions, clientOptions); + const disposable = languageClient.start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation. + context.subscriptions.push(disposable); + + // Allocate a progress reporting object. + const progressReporting = new ProgressReporting(languageClient); + context.subscriptions.push(progressReporting); + + // Register our custom commands. + const textEditorCommands = [Commands.orderImports, Commands.addMissingOptionalToParam]; + textEditorCommands.forEach(commandName => { + context.subscriptions.push( + commands.registerTextEditorCommand( + commandName, + (editor: TextEditor, edit: TextEditorEdit, ...args: any[]) => { + const cmd = { + command: commandName, + arguments: [editor.document.uri.toString(), ...args] + }; + + languageClient + .sendRequest('workspace/executeCommand', cmd) + .then((edits: TextEdit[] | undefined) => { + if (edits && edits.length > 0) { + editor.edit(editBuilder => { + edits.forEach(edit => { + const startPos = new Position( + edit.range.start.line, + edit.range.start.character + ); + const endPos = new Position(edit.range.end.line, edit.range.end.character); + const range = new Range(startPos, endPos); + editBuilder.replace(range, edit.newText); + }); + }); + } + }); + }, + () => { + // Error received. For now, do nothing. + } + ) + ); + }); + + const genericCommands = [Commands.createTypeStub]; + genericCommands.forEach(command => { + context.subscriptions.push( + commands.registerCommand(command, (...args: any[]) => { + languageClient.sendRequest('workspace/executeCommand', { command, arguments: args }); + }) + ); + }); } export function deactivate() { - // Return undefined rather than a promise to indicate - // that deactivation is done synchronously. We don't have - // anything to do here. - return undefined; + // Return undefined rather than a promise to indicate + // that deactivation is done synchronously. We don't have + // anything to do here. + return undefined; } - diff --git a/client/src/progress.ts b/client/src/progress.ts index dfe8d4cd2..ca37f9911 100644 --- a/client/src/progress.ts +++ b/client/src/progress.ts @@ -1,12 +1,12 @@ /* -* progress.ts -* -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* -* Provides a way for the pyright language server to report progress -* back to the client and display it in the editor. -*/ + * progress.ts + * + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * + * Provides a way for the pyright language server to report progress + * back to the client and display it in the editor. + */ import { Progress, ProgressLocation, window } from 'vscode'; import { Disposable, LanguageClient } from 'vscode-languageclient'; @@ -25,13 +25,16 @@ export class ProgressReporting implements Disposable { this._resolveProgress = resolve; }); - window.withProgress({ - location: ProgressLocation.Window, - title: '' - }, progress => { - this._progress = progress; - return progressPromise; - }); + window.withProgress( + { + location: ProgressLocation.Window, + title: '' + }, + progress => { + this._progress = progress; + return progressPromise; + } + ); this._primeTimeoutTimer(); }); diff --git a/docs/build-debug.md b/docs/build-debug.md index eeba42b52..3b9cd69c5 100644 --- a/docs/build-debug.md +++ b/docs/build-debug.md @@ -1,6 +1,7 @@ ## Building Pyright To build the project: + 1. Install [nodejs](https://nodejs.org/en/) 2. Open terminal window in main directory of cloned source 3. Execute `npm run install:all` to install dependencies @@ -8,11 +9,11 @@ To build the project: To build the VS Code extension package: Same as above, plus + 1. Execute `npm run package` The resulting package (pyright-X.Y.Z.vsix) can be found in the client directory. -To install in VS Code, go to the extensions panel and choose “Install from VSIX...” from the menu, then select the package. - +To install in VS Code, go to the extensions panel and choose "Install from VSIX..." from the menu, then select the package. ## Running Pyright Locally @@ -20,10 +21,8 @@ Once built, you can run the command-line tool directly from the built sources by `node ./index.js` - ## Debugging Pyright -To debug pyright, open the root source directory within VS Code. Open the debug sub-panel and choose “Pyright CLI” from the debug target menu. Click on the green “run” icon or press F5 to build and launch the command-line version in the VS Code debugger. - -To debug the VS Code extension, select “Pyright Language Client” from the debug target menu. Click on the green “run” icon or press F5 to build and launch a second copy of VS Code with the extension. Within the second VS Code instance, open a python source file so the pyright extension is loaded. Return to the first instance of VS Code and select “Pyright Language Server” from the debug target menu and click the green “run” icon. This will attach the debugger to the process that hosts the type checker. You can now set breakpoints, etc. +To debug pyright, open the root source directory within VS Code. Open the debug sub-panel and choose "Pyright CLI" from the debug target menu. Click on the green "run" icon or press F5 to build and launch the command-line version in the VS Code debugger. +To debug the VS Code extension, select "Pyright Language Client" from the debug target menu. Click on the green "run" icon or press F5 to build and launch a second copy of VS Code with the extension. Within the second VS Code instance, open a python source file so the pyright extension is loaded. Return to the first instance of VS Code and select "Pyright Language Server" from the debug target menu and click the green "run" icon. This will attach the debugger to the process that hosts the type checker. You can now set breakpoints, etc. diff --git a/docs/command-line.md b/docs/command-line.md index 695784261..28cca2654 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -2,35 +2,32 @@ Pyright can be run as either a VS Code extension or as a node-based command-line tool. The command-line version allows for the following options: -| Flag | Description | -| :--------------------------------- | :--------------------------------------------------- | -| --createstub IMPORT | Create type stub file(s) for import | -| --dependencies | Emit import dependency information | -| -h, --help | Show help message | -| --lib | Use library code for types when stubs are missing | -| --outputjson | Output results in JSON format | -| -p, --project FILE OR DIRECTORY | Use the configuration file at this location | -| --stats | Print detailed performance stats | -| -t, --typeshed-path DIRECTORY | Use typeshed type stubs at this location (1) | -| -v, --venv-path DIRECTORY | Directory that contains virtual environments (2) | -| --verbose | Emit verbose diagnostics | -| --version | Print pyright version | -| -w, --watch | Continue to run and watch for changes (3) | - +| Flag | Description | +| :------------------------------ | :------------------------------------------------ | +| --createstub IMPORT | Create type stub file(s) for import | +| --dependencies | Emit import dependency information | +| -h, --help | Show help message | +| --lib | Use library code for types when stubs are missing | +| --outputjson | Output results in JSON format | +| -p, --project FILE OR DIRECTORY | Use the configuration file at this location | +| --stats | Print detailed performance stats | +| -t, --typeshed-path DIRECTORY | Use typeshed type stubs at this location (1) | +| -v, --venv-path DIRECTORY | Directory that contains virtual environments (2) | +| --verbose | Emit verbose diagnostics | +| --version | Print pyright version | +| -w, --watch | Continue to run and watch for changes (3) | (1) Pyright has built-in typeshed type stubs for Python stdlib functionality. To use a different version of typeshed type stubs, specify the directory with this option. -(2) This option is used in conjunction with configuration file, which can refer to different virtual environments by name. For more details, refer to the [configuration](/docs/configuration.md) documentation. This allows a common config file to be checked in to the project and shared by everyone on the development team without making assumptions about the local paths to the venv directory on each developer’s computer. - -(3) When running in watch mode, pyright will reanalyze only those files that have been modified. These “deltas” are typically much faster than the initial analysis, which needs to analyze all files in the source tree. +(2) This option is used in conjunction with configuration file, which can refer to different virtual environments by name. For more details, refer to the [configuration](/docs/configuration.md) documentation. This allows a common config file to be checked in to the project and shared by everyone on the development team without making assumptions about the local paths to the venv directory on each developer's computer. +(3) When running in watch mode, pyright will reanalyze only those files that have been modified. These "deltas" are typically much faster than the initial analysis, which needs to analyze all files in the source tree. # Pyright Exit Codes -| Exit Code | Meaning | -| :---------- | :--------------------------------------------------------------- | -| 0 | No errors reported | -| 1 | One or more errors reported | -| 2 | Fatal error occurred with no errors or warnings reported | -| 3 | Config file could not be read or parsed | - +| Exit Code | Meaning | +| :-------- | :------------------------------------------------------- | +| 0 | No errors reported | +| 1 | One or more errors reported | +| 2 | Fatal error occurred with no errors or warnings reported | +| 3 | Config file could not be read or parsed | diff --git a/docs/commands.md b/docs/commands.md index 70080cbca..50a79a973 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,8 +1,9 @@ # VS Code Commands -Pyright offers the following commands, which can be invoked from VS Code’s “Command Palette”, which can be accessed from the View menu or by pressing Cmd-Shift-P. +Pyright offers the following commands, which can be invoked from VS Code's "Command Palette", which can be accessed from the View menu or by pressing Cmd-Shift-P. ## Organize Imports + This command reorders all imports found in the global (module-level) scope of the source file. As recommended in PEP8, imports are grouped into three groups, each separated by an empty line. The first group includes all built-in modules, the second group includes all third-party modules, and the third group includes all local modules. -Within each group, imports are sorted alphabetically. And within each “from X import Y” statement, the imported symbols are sorted alphabetically. Pyright also rewraps any imports that don't fit within a single line, switching to multi-line formatting. +Within each group, imports are sorted alphabetically. And within each "from X import Y" statement, the imported symbols are sorted alphabetically. Pyright also rewraps any imports that don't fit within a single line, switching to multi-line formatting. diff --git a/docs/comments.md b/docs/comments.md index 4432c5419..57e2d10d5 100644 --- a/docs/comments.md +++ b/docs/comments.md @@ -3,6 +3,7 @@ Some behaviors of pyright can be controlled through the use of comments within the source file. ## Type Annotations + Versions of Python prior to 3.6 did not support type annotations for variables. Pyright honors type annotations found within a comment at the end of the same line where a variable is assigned. ``` @@ -12,13 +13,14 @@ self._target = 3 # type: Union[int, str] ``` ## File-level Type Controls + Strict typing controls (where all supported type-checking switches generate errors) can be enabled for a file through the use of a special comment. Typically this comment is placed at or near the top of a code file on its own line. ``` # pyright: strict ``` -Individual configuration settings can also be overridden on a per-file basis and combined with “strict” typing. For example, if you want to enable all type checks except for “reportPrivateUsage”, you could add the following comment: +Individual configuration settings can also be overridden on a per-file basis and combined with "strict" typing. For example, if you want to enable all type checks except for "reportPrivateUsage", you could add the following comment: ``` # pyright: strict, reportPrivateUsage=false @@ -29,4 +31,3 @@ Diagnostic levels are also supported. ``` # pyright: reportPrivateUsage=warning, reportOptionalCall=error ``` - diff --git a/docs/configuration.md b/docs/configuration.md index 300264046..0dd6ca581 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,24 +1,24 @@ # Pyright Configuration -Pyright offers flexible configuration options specified in a JSON-formatted text configuration. By default, the file is called “pyrightconfig.json” and is located within the root directory of your project. Multi-root workspaces (“Add Folder to Workspace…”) are supported, and each workspace root can have its own “pyrightconfig.json” file. +Pyright offers flexible configuration options specified in a JSON-formatted text configuration. By default, the file is called "pyrightconfig.json" and is located within the root directory of your project. Multi-root workspaces ("Add Folder to Workspace...") are supported, and each workspace root can have its own "pyrightconfig.json" file. -Relative paths specified within the config file are relative to the config file’s location. Paths with shell variables (including `~`) are not supported. +Relative paths specified within the config file are relative to the config file's location. Paths with shell variables (including `~`) are not supported. ## Master Pyright Config Options -**include** [array of paths, optional]: Paths of directories or files that should be included. If no paths are specified, pyright defaults to the directory that contains the config file. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). If no include paths are specified, the root path for the workspace is assumed. +**include** [array of paths, optional]: Paths of directories or files that should be included. If no paths are specified, pyright defaults to the directory that contains the config file. Paths may contain wildcard characters \*_ (a directory or multiple levels of directories), _ (a sequence of zero or more characters), or ? (a single character). If no include paths are specified, the root path for the workspace is assumed. -**exclude** [array of paths, optional]: Paths of directories or files that should not be included. These override the includes directories, allowing specific subdirectories to be ignored. Note that files in the exclude paths may still be included in the analysis if they are referenced (imported) by source files that are not excluded. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). If no exclude paths are specified, Pyright automatically excludes the following: `**/node_modules`, `**/__pycache__`, `.venv` and `.git`. +**exclude** [array of paths, optional]: Paths of directories or files that should not be included. These override the includes directories, allowing specific subdirectories to be ignored. Note that files in the exclude paths may still be included in the analysis if they are referenced (imported) by source files that are not excluded. Paths may contain wildcard characters ** (a directory or multiple levels of directories), \* (a sequence of zero or more characters), or ? (a single character). If no exclude paths are specified, Pyright automatically excludes the following: `**/node_modules`,`\*\*/**pycache**`,`.venv`and`.git`. -**ignore** [array of paths, optional]: Paths of directories or files whose diagnostic output (errors and warnings) should be suppressed even if they are an included file or within the transitive closure of an included file. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). +**ignore** [array of paths, optional]: Paths of directories or files whose diagnostic output (errors and warnings) should be suppressed even if they are an included file or within the transitive closure of an included file. Paths may contain wildcard characters `**` (a directory or multiple levels of directories), `*` (a sequence of zero or more characters), or `?` (a single character). -**strict** [array of paths, optional]: Paths of directories or files that should use “strict” analysis if they are included. This is the same as manually adding a “# pyright: strict” comment. In strict mode, all type-checking rules are enabled. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). +**strict** [array of paths, optional]: Paths of directories or files that should use "strict" analysis if they are included. This is the same as manually adding a "# pyright: strict" comment. In strict mode, all type-checking rules are enabled. Paths may contain wildcard characters `**` (a directory or multiple levels of directories), `*` (a sequence of zero or more characters), or `?` (a single character). **typeshedPath** [path, optional]: Path to a directory that contains typeshed type stub files. Pyright ships with an internal copy of some typeshed type stubs (those that cover the Python stdlib packages). If you want to use a full copy of the typeshed type stubs (including those for third-party packages), you can clone the [typeshed github repo](https://github.com/python/typeshed) to a local directory and reference the location with this path. **typingsPath** [path, optional]: Path to a directory that contains custom type stubs. Each package's type stub file(s) are expected to be in its own subdirectory. The default value of this setting is "./typings". -**venvPath** [path, optional]: Path to a directory containing one or more subdirectories, each of which contains a virtual environment. Each execution environment (see below for details) can refer to a different virtual environment. When used in conjunction with a **venv** setting (see below), pyright will search for imports in the virtual environment’s site-packages directory rather than the paths specified in PYTHONPATH. +**venvPath** [path, optional]: Path to a directory containing one or more subdirectories, each of which contains a virtual environment. Each execution environment (see below for details) can refer to a different virtual environment. When used in conjunction with a **venv** setting (see below), pyright will search for imports in the virtual environment's site-packages directory rather than the paths specified in PYTHONPATH. **venv** [string, optional]: Used in conjunction with the venvPath, specifies the virtual environment to use. Individual execution environments may override this setting. @@ -28,19 +28,19 @@ Relative paths specified within the config file are relative to the config file **executionEnvironments** [array of objects, optional]: Specifies a list of execution environments (see below). Execution environments are searched from start to finish by comparing the path of a source file with the root path specified in the execution environment. - ## Type Check Diagnostics Settings -The following settings control pyright’s diagnostic output (warnings or errors). Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, or `"error"` can be used to specify the diagnostic level. + +The following settings control pyright's diagnostic output (warnings or errors). Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, or `"error"` can be used to specify the diagnostic level. **strictListInference** [boolean]: When inferring the type of a list, use strict type assumptions. For example, the expression `[1, 'a', 3.4]` could be inferred to be of type `List[Any]` or `List[Union[int, str, float]]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is 'false'. -**strictDictionaryInference** [boolean]: When inferring the type of a dictionary’s keys and values, use strict type assumptions. For example, the expression `{'a': 1, 'b': 'a'}` could be inferred to be of type `Dict[str, Any]` or `Dict[str, Union[int, str]]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is 'false'. +**strictDictionaryInference** [boolean]: When inferring the type of a dictionary's keys and values, use strict type assumptions. For example, the expression `{'a': 1, 'b': 'a'}` could be inferred to be of type `Dict[str, Any]` or `Dict[str, Union[int, str]]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is 'false'. **strictParameterNoneValue** [boolean]: PEP 484 indicates that when a function parameter is assigned a default value of None, its type should implicitly be Optional even if the explicit type is not. When enabled, this rule requires that parameter type annotations use Optional explicitly in this case. The default value for this setting is 'false'. **enableTypeIgnoreComments** [boolean]: PEP 484 defines support for "# type: ignore" comments. This switch enables or disables support for these comments. The default value for this setting is 'true'. -**reportTypeshedErrors** [boolean or string, optional]: Generate or suppress diagnostics for typeshed type stub files. In general, these type stub files should be “clean” and generate no errors. The default value for this setting is 'none'. +**reportTypeshedErrors** [boolean or string, optional]: Generate or suppress diagnostics for typeshed type stub files. In general, these type stub files should be "clean" and generate no errors. The default value for this setting is 'none'. **reportMissingImports** [boolean or string, optional]: Generate or suppress diagnostics for imports that have no corresponding imported python file or type stub file. The default value for this setting is 'none', although pyright can do a much better job of static type checking if type stub files are provided for all imports. @@ -76,9 +76,9 @@ The following settings control pyright’s diagnostic output (warnings or errors **reportUntypedBaseClass** [boolean or string, optional]: Generate or suppress diagnostics for base classes whose type cannot be determined statically. These obscure the class type, defeating many type analysis features. The default value for this setting is 'none'. -**reportUntypedNamedTuple** [boolean or string, optional]: Generate or suppress diagnostics when “namedtuple” is used rather than “NamedTuple”. The former contains no type information, whereas the latter does. The default value for this setting is 'none'. +**reportUntypedNamedTuple** [boolean or string, optional]: Generate or suppress diagnostics when "namedtuple" is used rather than "NamedTuple". The former contains no type information, whereas the latter does. The default value for this setting is 'none'. -**reportPrivateUsage** [boolean or string, optional]: Generate or suppress diagnostics for incorrect usage of private or protected variables or functions. Protected class members begin with a single underscore (“_”) and can be accessed only by subclasses. Private class members begin with a double underscore but do not end in a double underscore and can be accessed only within the declaring class. Variables and functions declared outside of a class are considered private if their names start with either a single or double underscore, and they cannot be accessed outside of the declaring module. The default value for this setting is 'none'. +**reportPrivateUsage** [boolean or string, optional]: Generate or suppress diagnostics for incorrect usage of private or protected variables or functions. Protected class members begin with a single underscore ("\_") and can be accessed only by subclasses. Private class members begin with a double underscore but do not end in a double underscore and can be accessed only within the declaring class. Variables and functions declared outside of a class are considered private if their names start with either a single or double underscore, and they cannot be accessed outside of the declaring module. The default value for this setting is 'none'. **reportConstantRedefinition** [boolean or string, optional]: Generate or suppress diagnostics for attempts to redefine variables whose names are all-caps with underscores and numerals. The default value for this setting is 'none'. @@ -104,13 +104,13 @@ The following settings control pyright’s diagnostic output (warnings or errors **reportAssertAlwaysTrue** [boolean or string, optional]: Generate or suppress diagnostics for 'assert' statement that will provably always assert. This can be indicative of a programming error. The default value for this setting is 'warning'. -**reportSelfClsParameterName** [boolean or string, optional]: Generate or suppress diagnostics for a missing or misnamed “self” parameter in instance methods and “cls” parameter in class methods. Instance methods in metaclasses (classes that derive from “type”) are allowed to use “cls” for instance methods. The default value for this setting is 'warning'. +**reportSelfClsParameterName** [boolean or string, optional]: Generate or suppress diagnostics for a missing or misnamed "self" parameter in instance methods and "cls" parameter in class methods. Instance methods in metaclasses (classes that derive from "type") are allowed to use "cls" for instance methods. The default value for this setting is 'warning'. **reportImplicitStringConcatenation** [boolean or string, optional]: Generate or suppress diagnostics for two or more string literals that follow each other, indicating an implicit concatenation. This is considered a bad practice and often masks bugs such as missing commas. The default value for this setting is 'none'. - ## Execution Environment Options -Pyright allows multiple “execution environments” to be defined for different portions of your source tree. For example, a subtree may be designed to run with different import search paths or a different version of the python interpreter than the rest of the source base. + +Pyright allows multiple "execution environments" to be defined for different portions of your source tree. For example, a subtree may be designed to run with different import search paths or a different version of the python interpreter than the rest of the source base. The following settings can be specified for each execution environment. @@ -124,72 +124,56 @@ The following settings can be specified for each execution environment. **pythonPlatform** [string, optional]: Specifies the target platform that will be used for this execution environment. If not specified, the global `pythonPlatform` setting is used instead. - # VS Code Extension Settings + Pyright will import the following settings set through VS Code. These override the values provided in the configuration file. **python.venvPath**: Same as the **venvPath** setting described above. **python.analysis.typeshedPaths**: An array of typeshed paths to search. Pyright supports only one such path. If provided in the VS Code setting, the first entry overrides the **typeshedPath** configuration file entry described above. - ## Sample Config File + The following is an example of a pyright config file: + ```json { - "include": [ - "src" - ], - - "exclude": [ - "**/node_modules", - "**/__pycache__", - "src/experimental", - "src/web/node_modules", - "src/typestubs" - ], + "include": ["src"], - "ignore": [ - "src/oldstuff" - ], + "exclude": ["**/node_modules", "**/__pycache__", "src/experimental", "src/web/node_modules", "src/typestubs"], - "typingsPath": "src/typestubs", - "venvPath": "/home/foo/.venvs", + "ignore": ["src/oldstuff"], - "reportTypeshedErrors": false, - "reportMissingImports": true, - "reportMissingTypeStubs": false, + "typingsPath": "src/typestubs", + "venvPath": "/home/foo/.venvs", - "pythonVersion": "3.6", - "pythonPlatform": "Linux", + "reportTypeshedErrors": false, + "reportMissingImports": true, + "reportMissingTypeStubs": false, - "executionEnvironments": [ - { - "root": "src/web", - "pythonVersion": "3.5", - "pythonPlatform": "Windows", - "extraPaths": [ - "src/service_libs" - ] - }, - { - "root": "src/sdk", - "pythonVersion": "3.0", - "extraPaths": [ - "src/backend" - ], - "venv": "venv_bar" - }, - { - "root": "src/tests", - "extraPaths": [ - "src/tests/e2e", - "src/sdk" - ] - }, - { - "root": "src" - } - ] + "pythonVersion": "3.6", + "pythonPlatform": "Linux", + + "executionEnvironments": [ + { + "root": "src/web", + "pythonVersion": "3.5", + "pythonPlatform": "Windows", + "extraPaths": ["src/service_libs"] + }, + { + "root": "src/sdk", + "pythonVersion": "3.0", + "extraPaths": ["src/backend"], + "venv": "venv_bar" + }, + { + "root": "src/tests", + "extraPaths": ["src/tests/e2e", "src/sdk"] + }, + { + "root": "src" + } + ] } ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index f2313aecd..77dd857a9 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -3,12 +3,12 @@ A static type checker like pyright can add incremental value to your source code as more type information is provided. Here is a typical progression: + 1. Install pyright (either the VS Code extension or command-line tool). -2. Write a minimal `pyrightconfig.json` that defines `include` entries. Place the config file in your project’s top-level directory and commit it to your repo. +2. Write a minimal `pyrightconfig.json` that defines `include` entries. Place the config file in your project's top-level directory and commit it to your repo. 3. Run pyright over your source base with the default settings. Fix any errors and warnings that it emits. -4. Enable the `reportMissingTypeStubs` setting in the config file and add (minimal) type stub files for the imported files. You may wish to create a “typestubs” directory within your code base -- a common location for all of your custom type stub files. You may be able to find preexisting type stub files for some of your imports within the typeshed repo (in the [third-party directory](https://github.com/python/typeshed/tree/master/third_party)). -5. Check in your custom type stub files and configure pyright to run as part of your continuous integration (CI) environment to keep the project “type clean”. +4. Enable the `reportMissingTypeStubs` setting in the config file and add (minimal) type stub files for the imported files. You may wish to create a "typestubs" directory within your code base -- a common location for all of your custom type stub files. You may be able to find preexisting type stub files for some of your imports within the typeshed repo (in the [third-party directory](https://github.com/python/typeshed/tree/master/third_party)). +5. Check in your custom type stub files and configure pyright to run as part of your continuous integration (CI) environment to keep the project "type clean". 6. Incrementally add type annotations to your code files. The annotations that provide most value are on function input parameters, instance variables, and return parameters (in that order). Note that annotation of variables (instance, class and local) requires Python 3.6 or newer. 7. Enable stricter type checking options like "reportOptionalSubscript", "reportOptionalMemberAccess", "reportOptionalCall", and "reportUntypedFunctionDecorator". 8. On a file-by-file basis, enable all type checking options by adding the comment `# pyright: strict` somewhere in the file. - diff --git a/docs/import-resolution.md b/docs/import-resolution.md index dc3a0a5c8..0f2d54d3b 100644 --- a/docs/import-resolution.md +++ b/docs/import-resolution.md @@ -2,9 +2,8 @@ Pyright resolves external imports based on several configuration settings. If a venvPath and venv are specified, these are used to locate the `site-packages` directory within the virtual environment. -If no venvPath is specified, Pyright falls back to the paths found in the default python interpreter’s search paths (or the python interpreter pointed to by the “python.pythonPath” setting in VS Code). Only directory-based paths are supported (as opposed to zip files or other loader packages). +If no venvPath is specified, Pyright falls back to the paths found in the default python interpreter's search paths (or the python interpreter pointed to by the "python.pythonPath" setting in VS Code). Only directory-based paths are supported (as opposed to zip files or other loader packages). -The Pyright configuration file supports “execution environment” definitions, each of which can define additional paths. These are searched in addition to the venv or PYTHONPATH directories. - -If Pyright is reporting import resolution errors, additional diagnostic information may help you determine why. If you are using the command-line version, try adding the “--verbose” switch. If you are using the VS Code extension, look at the “Output” window (View -> Output) and choose the “Pyright” view from the popup menu. +The Pyright configuration file supports "execution environment" definitions, each of which can define additional paths. These are searched in addition to the venv or PYTHONPATH directories. +If Pyright is reporting import resolution errors, additional diagnostic information may help you determine why. If you are using the command-line version, try adding the "--verbose" switch. If you are using the VS Code extension, look at the "Output" window (View -> Output) and choose the "Pyright" view from the popup menu. diff --git a/docs/internals.md b/docs/internals.md index 32d83eed2..5d51d6ab6 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -2,15 +2,14 @@ ## Code Structure -* client/src/extension.ts: Language Server Protocol (LSP) client entry point for VS Code extension. -* client/typeshed-fallback/: Recent copy of Typeshed type stub files for Python stdlib -* server/src/pyright.ts: Main entry point for command-line tool -* server/src/server.ts: Main entry point for LSP server -* server/src/analyzer: Modules that perform analysis passes over Python parse tree -* server/src/common: Modules that are common to the parser and analyzer -* server/src/parser: Modules that perform tokenization and parsing of Python source -* server/src/tests: Tests for the parser and analyzer - +- client/src/extension.ts: Language Server Protocol (LSP) client entry point for VS Code extension. +- client/typeshed-fallback/: Recent copy of Typeshed type stub files for Python stdlib +- server/src/pyright.ts: Main entry point for command-line tool +- server/src/server.ts: Main entry point for LSP server +- server/src/analyzer: Modules that perform analysis passes over Python parse tree +- server/src/common: Modules that are common to the parser and analyzer +- server/src/parser: Modules that perform tokenization and parsing of Python source +- server/src/tests: Tests for the parser and analyzer ## Core Concepts @@ -22,12 +21,11 @@ The program tracks multiple [sourceFile](https://github.com/Microsoft/pyright/bl The program makes use of an [importResolver](https://github.com/Microsoft/pyright/blob/master/server/src/analyzer/importResolver.ts) to resolve the imported modules referenced within each source file. - ## Analysis Phases Pyright performs the following analysis phases for each source file. -The [tokenizer](https://github.com/Microsoft/pyright/blob/master/server/src/parser/tokenizer.ts) is responsible for converting the file’s string contents into a stream of tokens. White space, comments, and some end-of-line characters are ignored, as they are not needed by the parser. +The [tokenizer](https://github.com/Microsoft/pyright/blob/master/server/src/parser/tokenizer.ts) is responsible for converting the file's string contents into a stream of tokens. White space, comments, and some end-of-line characters are ignored, as they are not needed by the parser. The [parser](https://github.com/Microsoft/pyright/blob/master/server/src/parser/parser.ts) is responsible for converting the token stream into a parse tree. A generalized [parseTreeWalker](https://github.com/Microsoft/pyright/blob/master/server/src/analyzer/parseTreeWalker.ts) provides a convenient way to traverse the parse tree. All subsequent analysis phases utilize the parseTreeWalker. @@ -37,11 +35,12 @@ The [checker](https://github.com/Microsoft/pyright/blob/master/server/src/analyz ## Type Checking Concepts -Pyright uses an internal type called “Unknown” to represent types that are not annotated and cannot be inferred. Unknown is generally treated like the “Any” type in terms of type checking, but it provides a way for developers to know when type annotations are missing and could provide additional value. +Pyright uses an internal type called "Unknown" to represent types that are not annotated and cannot be inferred. Unknown is generally treated like the "Any" type in terms of type checking, but it provides a way for developers to know when type annotations are missing and could provide additional value. Pyright attempts to infer the types of global (module-level) variables, class variables, instance variables, and local variables. Return and yield types are also inferred. If type annotations are provided in these cases, the type annotation overrides any inferred types. -Pyright supports type constraints (sometimes called “path constraints” or "type guards") to track assumptions that apply within certain code flow paths. For example, consider the following code: +Pyright supports type constraints (sometimes called "path constraints" or "type guards") to track assumptions that apply within certain code flow paths. For example, consider the following code: + ```python def (a: Optional[Union[str, List[str]]): if isinstance(a, str): @@ -55,6 +54,7 @@ def (a: Optional[Union[str, List[str]]): In this example, the type evaluator knows that parameter a is either None, str, or List[str]. Within the first `if` clause, a is constrained to be a str. Within the `elif` clause, it is constrained to be a List[str], and within the `else` clause, it has to be None (by process of elimination). The type checker would therefore flag the final line as an error if the log method could not accept None as a parameter. If the type constraint logic exhausts all possible subtypes, it can be assumed that a code path will never be taken. For example, consider the following: + ```python def (a: Union[Foo, Bar]): if isinstance(a, Foo): @@ -68,15 +68,16 @@ def (a: Union[Foo, Bar]): a.do_something_3() ``` -In this case, the type of parameter “a” is initially “Union[Foo, Bar]”. Within the “if” clause, the type constraint logic will conclude that it must be of type “Foo”. Within the “elif” clause, it must be of type “Bar”. What type is it within the “else” clause? The type constraint system has eliminated all possible subtypes, so it gives it the type “Never”. This is generally indicates that there’s a logic error in the code because there’s way that code block will ever be executed. +In this case, the type of parameter "a" is initially "Union[Foo, Bar]". Within the "if" clause, the type constraint logic will conclude that it must be of type "Foo". Within the "elif" clause, it must be of type "Bar". What type is it within the "else" clause? The type constraint system has eliminated all possible subtypes, so it gives it the type "Never". This is generally indicates that there's a logic error in the code because there's way that code block will ever be executed. ## Type Inference -In cases where explicit type annotations are not provided, Pyright attempts to infer the types. The inferred return type of a function is determined from all of the return (and yield) statements within the function’s definition. The inferred type of a local variable is determined by the expression that is assigned to that variable. Likewise, the type of a member variable is inferred from all assignments to that member variable within its defining class. +In cases where explicit type annotations are not provided, Pyright attempts to infer the types. The inferred return type of a function is determined from all of the return (and yield) statements within the function's definition. The inferred type of a local variable is determined by the expression that is assigned to that variable. Likewise, the type of a member variable is inferred from all assignments to that member variable within its defining class. -The types of input parameters cannot be inferred, with the exception of the “self” or “cls” parameter for instance members and class members, respectively. +The types of input parameters cannot be inferred, with the exception of the "self" or "cls" parameter for instance members and class members, respectively. + +If an inferred return type is unknown or partially unknown because input parameter types are not annotated, Pyright may still be able to infer the return type based on the types of arguments at the call site. -If an inferred return type is unknown or partially unknown because input parameter types are not annotated, Pyright may still be able to infer the return type based on the types of arguments at the call site. ``` def add_values(a, b): return a + b diff --git a/docs/settings.md b/docs/settings.md index ecb96a6b2..ff81d506e 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -2,7 +2,7 @@ The Pyright VS Code extension honors the following settings. -**pyright.disableLanguageServices** [boolean]: Disables all language services except for “hover”. This includes type completion, signature completion, find definition, find references, and find symbols in file. This option is useful if you want to use pyright only as a type checker but want to run another Python language server for langue service features. +**pyright.disableLanguageServices** [boolean]: Disables all language services except for "hover". This includes type completion, signature completion, find definition, find references, and find symbols in file. This option is useful if you want to use pyright only as a type checker but want to run another Python language server for langue service features. **pyright.openFilesOnly** [boolean]: Determines whether pyright analyzes (and reports errors for) all files in the workspace, as indicated by the config file. If this option is set to true, pyright analyzes only open files. @@ -13,5 +13,3 @@ The Pyright VS Code extension honors the following settings. **python.pythonPath** [path]: Path to Python interpreter. **python.venvPath** [path]: Path to folder with subdirectories that contain virtual environments. - - diff --git a/docs/type-stubs.md b/docs/type-stubs.md index b3c5b733f..53150102e 100644 --- a/docs/type-stubs.md +++ b/docs/type-stubs.md @@ -1,28 +1,30 @@ # Type Stub Files -Type stubs are “.pyi” files that specify the public interface for a library. They use a variant of the Python syntax that allows for “...” to be used in place of any implementation details. Type stubs define the public contract for the library. +Type stubs are ".pyi" files that specify the public interface for a library. They use a variant of the Python syntax that allows for "..." to be used in place of any implementation details. Type stubs define the public contract for the library. ## Importance of Type Stub Files -Regardless of the search path, Pyright always attempts to resolve an import with a type stub (“.pyi”) file before falling back to a python source (“.py”) file. If a type stub cannot be located for an external import, that import will be treated as a “black box” for purposes of type analysis. Any symbols imported from these modules will be of type “Unknown”, and wildcard imports (of the form `from foo import *`) will not populate the module’s namespace with specific symbol names. +Regardless of the search path, Pyright always attempts to resolve an import with a type stub (".pyi") file before falling back to a python source (".py") file. If a type stub cannot be located for an external import, that import will be treated as a "black box" for purposes of type analysis. Any symbols imported from these modules will be of type "Unknown", and wildcard imports (of the form `from foo import *`) will not populate the module's namespace with specific symbol names. Why does Pyright not attempt (by default) to determine types from imported python sources? There are several reasons. 1. Imported libraries can be quite large, so analyzing them can require significant time and computation. 2. Some libraries are thin shims on top of native (C++) libraries. Little or no type information would be inferable in these cases. -3. Some libraries override Python’s default loader logic. Static analysis is not possible in these cases. +3. Some libraries override Python's default loader logic. Static analysis is not possible in these cases. 4. Type information inferred from source files is often of low value because many types cannot be inferred correctly. Even if concrete types can be inferred, generic type definitions cannot. -5. Type analysis would expose all symbols from an imported module, even those that are not meant to be exposed by the author. Unlike many other languages, Python offers no way of differentiating between a symbol that is meant to be exported and one that isn’t. +5. Type analysis would expose all symbols from an imported module, even those that are not meant to be exposed by the author. Unlike many other languages, Python offers no way of differentiating between a symbol that is meant to be exported and one that isn't. -If you’re serious about static type checking for your Python source base, it’s highly recommended that you use type stub files for all external imports. If you are unable to find a type stub for a particular library, the recommended approach is to create a custom type stub file that defines the portion of that module’s interface used by your code. More library authors have started to provide type stub files. +If you're serious about static type checking for your Python source base, it's highly recommended that you use type stub files for all external imports. If you are unable to find a type stub for a particular library, the recommended approach is to create a custom type stub file that defines the portion of that module's interface used by your code. More library authors have started to provide type stub files. ## Generating Type Stubs -If you use only a few classes, methods or functions within a library, writing a type stub file by hand is feasible. For large libraries, this can become tedious and error-prone. Pyright can generate “draft” versions of type stub files for you. -To generate a type stub file from within VS Code, enable the reportMissingTypeStubs” setting in your pyrightconfig.json file or by adding a comment `# pyright: reportMissingTypeStubs=true` to individual source files. Make sure you have the target library installed in the python environment that pyright is configured to use for import resolution. Optionally specify a “typingsPath” in your pyrightconfig.json file. This is where pyright will generate your type stub files. By default, the typingsPath is set to "./typings". +If you use only a few classes, methods or functions within a library, writing a type stub file by hand is feasible. For large libraries, this can become tedious and error-prone. Pyright can generate "draft" versions of type stub files for you. + +To generate a type stub file from within VS Code, enable the reportMissingTypeStubs" setting in your pyrightconfig.json file or by adding a comment `# pyright: reportMissingTypeStubs=true` to individual source files. Make sure you have the target library installed in the python environment that pyright is configured to use for import resolution. Optionally specify a "typingsPath" in your pyrightconfig.json file. This is where pyright will generate your type stub files. By default, the typingsPath is set to "./typings". ### Generating Type Stubs in VS Code -If “reportMissingTypeStubs” is enabled, pyright will highlight any imports that have no type stub. Hover over the error message, and you will see a “Quick Fix” link. Clicking on this link will reveal a popup menu item titled “Create Type Stub For XXX”. The example below shows a missing typestub for the `django` library. + +If "reportMissingTypeStubs" is enabled, pyright will highlight any imports that have no type stub. Hover over the error message, and you will see a "Quick Fix" link. Clicking on this link will reveal a popup menu item titled "Create Type Stub For XXX". The example below shows a missing typestub for the `django` library. ![Pyright](/docs/img/CreateTypeStub1.png) @@ -31,21 +33,23 @@ Click on the menu item to create the type stub. Depending on the size of the lib ![Pyright](/docs/img/CreateTypeStub2.png) ### Generating Type Stubs from Command Line + The command-line version of pyright can also be used to generate type stubs. As with the VS Code version, it must be run within the context of your configured project. Then type `pyright --createstub [import-name]`. For example: `pyright --createstub django` ### Cleaning Up Generated Type Stubs + Pyright can give you a head start by creating type stubs, but you will typically need to clean up the first draft, fixing various errors and omissions that pyright was not able to infer from the original library code. A few common situations that need to be cleaned up: -1. When generating a “.pyi” file, pyright removes any imports that are not referenced. Sometimes libraries import symbols that are meant to be simply re-exported from a module even though they are not referenced internally to that module. In such cases, you will need to manually add back these imports. Pyright does not perform this import culling in `__init__.pyi` files because this re-export technique is especially common in such files. +1. When generating a ".pyi" file, pyright removes any imports that are not referenced. Sometimes libraries import symbols that are meant to be simply re-exported from a module even though they are not referenced internally to that module. In such cases, you will need to manually add back these imports. Pyright does not perform this import culling in `__init__.pyi` files because this re-export technique is especially common in such files. -2. Some libraries attempt to import modules within a try statement. These constructs don’t work well in type stub files because they cannot be evaluated statically. Pyright omits any try statements when creating “.pyi” files, so you may need to add back in these import statements. +2. Some libraries attempt to import modules within a try statement. These constructs don't work well in type stub files because they cannot be evaluated statically. Pyright omits any try statements when creating ".pyi" files, so you may need to add back in these import statements. -3. Decorator functions are especially problematic for static type analyzers. Unless properly typed, they completely hide the signature of any class or function they are applied to. For this reason, it is highly recommended that you enable the “reportUntypedFunctionDecorator” and “reportUntypedClassDecorator” switches in pyrightconfig.json. Most decorators simply return the same function they are passed. Those can easily be annotated with a TypeVar like this: +3. Decorator functions are especially problematic for static type analyzers. Unless properly typed, they completely hide the signature of any class or function they are applied to. For this reason, it is highly recommended that you enable the "reportUntypedFunctionDecorator" and "reportUntypedClassDecorator" switches in pyrightconfig.json. Most decorators simply return the same function they are passed. Those can easily be annotated with a TypeVar like this: ``` from typings import Any, Callable, TypeVar @@ -54,4 +58,3 @@ _FuncT = TypeVar('_FuncT', bound=Callable[..., Any]) def my_decorator(*args, **kw) -> Callable[[_FuncT], _FuncT]: ... ``` - diff --git a/index.debug.js b/index.debug.js index 57257b0d6..1ab45197c 100644 --- a/index.debug.js +++ b/index.debug.js @@ -3,4 +3,4 @@ // Stash the base directory into a global variable. global.__rootDirectory = __dirname + '/dist/'; -require('./client/server/pyright') +require('./client/server/pyright'); diff --git a/index.js b/index.js index 0d0c0bd4b..d7a6639f6 100644 --- a/index.js +++ b/index.js @@ -3,4 +3,4 @@ // Stash the base directory into a global variable. global.__rootDirectory = __dirname + '/dist/'; -require('./dist/pyright') +require('./dist/pyright'); diff --git a/package-lock.json b/package-lock.json index 63aacea05..ed36699a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,9 +74,9 @@ } }, "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", "dev": true }, "@types/minimatch": { @@ -85,25 +85,19 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, - "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", - "dev": true - }, "@types/node": { - "version": "12.12.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.21.tgz", - "integrity": "sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==", + "version": "12.12.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.28.tgz", + "integrity": "sha512-g73GJYJDXgf0jqg+P9S8h2acWbDXNkoCX8DLtJVu7Fkn788pzQ/oJsrdJz/2JejRf/SjfZaAhsw+3nd1D5EWGg==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.12.0.tgz", - "integrity": "sha512-1t4r9rpLuEwl3hgt90jY18wJHSyb0E3orVL3DaqwmpiSDHmHiSspVsvsFF78BJ/3NNG3qmeso836jpuBWYziAA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.21.0.tgz", + "integrity": "sha512-b5jjjDMxzcjh/Sbjuo7WyhrQmVJg0WipTHQgXh5Xwx10uYm6nPWqN1WGOsaNq4HR3Zh4wUx4IRQdDkCHwyewyw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "2.12.0", + "@typescript-eslint/experimental-utils": "2.21.0", "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", @@ -111,39 +105,39 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.12.0.tgz", - "integrity": "sha512-jv4gYpw5N5BrWF3ntROvCuLe1IjRenLy5+U57J24NbPGwZFAjhnM45qpq0nDH1y/AZMb3Br25YiNVwyPbz6RkA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz", + "integrity": "sha512-olKw9JP/XUkav4lq0I7S1mhGgONJF9rHNhKFn9wJlpfRVjNo3PPjSvybxEldvCXnvD+WAshSzqH5cEjPp9CsBA==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.12.0", + "@typescript-eslint/typescript-estree": "2.21.0", "eslint-scope": "^5.0.0" } }, "@typescript-eslint/parser": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.12.0.tgz", - "integrity": "sha512-lPdkwpdzxEfjI8TyTzZqPatkrswLSVu4bqUgnB03fHSOwpC7KSerPgJRgIAf11UGNf7HKjJV6oaPZI4AghLU6g==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.21.0.tgz", + "integrity": "sha512-VrmbdrrrvvI6cPPOG7uOgGUFXNYTiSbnRq8ZMyuGa4+qmXJXVLEEz78hKuqupvkpwJQNk1Ucz1TenrRP90gmBg==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.12.0", - "@typescript-eslint/typescript-estree": "2.12.0", + "@typescript-eslint/experimental-utils": "2.21.0", + "@typescript-eslint/typescript-estree": "2.21.0", "eslint-visitor-keys": "^1.1.0" } }, "@typescript-eslint/typescript-estree": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.12.0.tgz", - "integrity": "sha512-rGehVfjHEn8Frh9UW02ZZIfJs6SIIxIu/K1bbci8rFfDE/1lQ8krIJy5OXOV3DVnNdDPtoiPOdEANkLMrwXbiQ==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz", + "integrity": "sha512-NC/nogZNb9IK2MEFQqyDBAciOT8Lp8O3KgAfvHx2Skx6WBo+KmDqlU3R9KxHONaijfTIKtojRe3SZQyMjr3wBw==", "dev": true, "requires": { "debug": "^4.1.1", "eslint-visitor-keys": "^1.1.0", "glob": "^7.1.6", "is-glob": "^4.0.1", - "lodash.unescape": "4.0.1", + "lodash": "^4.17.15", "semver": "^6.3.0", "tsutils": "^3.17.1" } @@ -544,6 +538,15 @@ } } }, + "eslint-config-prettier": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, "eslint-plugin-simple-import-sort": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-5.0.1.tgz", @@ -738,6 +741,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1021,12 +1030,6 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", - "dev": true - }, "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", @@ -1298,6 +1301,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -1702,9 +1711,9 @@ "dev": true }, "typescript": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", - "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", + "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index f4fd7db6f..05cda5243 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,15 @@ "package": "npm run install:all && npm run clean && npm run build:serverProd && npm run build:cli && cd client && npx vsce package && cd .." }, "devDependencies": { - "@types/mocha": "^5.2.7", - "@types/node": "^12.12.21", - "@typescript-eslint/eslint-plugin": "^2.12.0", - "@typescript-eslint/parser": "^2.12.0", + "@types/node": "^12.12.28", + "@typescript-eslint/eslint-plugin": "^2.21.0", + "@typescript-eslint/parser": "^2.21.0", "del-cli": "^3.0.0", "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.0", "eslint-plugin-simple-import-sort": "^5.0.1", - "typescript": "^3.7.3" + "prettier": "1.19.1", + "typescript": "^3.8.2" }, "main": "index.js", "bin": { diff --git a/server/.eslintignore b/server/.eslintignore index 1cb9e4788..2fe884953 100644 --- a/server/.eslintignore +++ b/server/.eslintignore @@ -1 +1,2 @@ -**/tests/fourslash/** \ No newline at end of file +**/client/server/** +**/tests/fourslash/** diff --git a/server/.vscode/settings.json b/server/.vscode/settings.json deleted file mode 100644 index 75472f4ea..000000000 --- a/server/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true, - "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true -} \ No newline at end of file diff --git a/server/copyTypeshedFallback.js b/server/copyTypeshedFallback.js index ea86a8246..be71cbe9a 100644 --- a/server/copyTypeshedFallback.js +++ b/server/copyTypeshedFallback.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +/* eslint-disable @typescript-eslint/no-var-requires */ // This script helps build the command-line version of pyright // by copying the typeshed-fallback directory to the dist directory. @@ -10,4 +11,3 @@ fsExtra.emptyDirSync('../dist'); fsExtra.mkdirSync('../dist/typeshed-fallback'); fsExtra.copySync('../client/typeshed-fallback', '../dist/typeshed-fallback'); - diff --git a/server/customInstallServerIntoExtension.js b/server/customInstallServerIntoExtension.js index 1bcc72226..3e219cf5b 100644 --- a/server/customInstallServerIntoExtension.js +++ b/server/customInstallServerIntoExtension.js @@ -1,75 +1,88 @@ #!/usr/bin/env node +/* eslint-disable @typescript-eslint/no-var-requires */ // This file is based on the "installServerIntoExtension" that ships with the // vscode-languagserver node package. We needed to modify it because the original // version does not copy the package-lock.json file, and it uses npm update // rather than npm install. -var path = require('path'); -var fs = require('fs'); -var cp = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const cp = require('child_process'); -var extensionDirectory = process.argv[2]; +let extensionDirectory = process.argv[2]; if (!extensionDirectory) { - console.error('No extension directory provided.'); - process.exit(1) + console.error('No extension directory provided.'); + process.exit(1); } -extensionDirectory = path.resolve(extensionDirectory) +extensionDirectory = path.resolve(extensionDirectory); if (!fs.existsSync(extensionDirectory)) { - console.error('Extension directory ' + extensionDirectory + ' doesn\'t exist on disk.'); - process.exit(1); + console.error('Extension directory ' + extensionDirectory + " doesn't exist on disk."); + process.exit(1); } -var packageFile = process.argv[3]; +let packageFile = process.argv[3]; if (!packageFile) { - console.error('No package.json file provided.'); - process.exit(1); + console.error('No package.json file provided.'); + process.exit(1); } packageFile = path.resolve(packageFile); if (!fs.existsSync(packageFile)) { - console.error('Package file ' + packageFile + ' doesn\'t exist on disk.'); - process.exit(1); + console.error('Package file ' + packageFile + " doesn't exist on disk."); + process.exit(1); } -var tsconfigFile = process.argv[4]; +let tsconfigFile = process.argv[4]; if (!tsconfigFile) { - console.error('No tsconfig.json file provided'); - process.exit(1); + console.error('No tsconfig.json file provided'); + process.exit(1); } tsconfigFile = path.resolve(tsconfigFile); if (!fs.existsSync(tsconfigFile)) { - console.error('tsconfig file ' + tsconfigFile + ' doesn\'t exist on disk.') - process.exit(1); + console.error('tsconfig file ' + tsconfigFile + " doesn't exist on disk."); + process.exit(1); } -var extensionServerDirectory = path.join(extensionDirectory, 'server') +const extensionServerDirectory = path.join(extensionDirectory, 'server'); -var json = require(tsconfigFile); -var compilerOptions = json.compilerOptions; +const json = require(tsconfigFile); +const compilerOptions = json.compilerOptions; if (compilerOptions) { - var outDir = compilerOptions.outDir; - if (!outDir || path.join(path.dirname(tsconfigFile), outDir) !== extensionServerDirectory) { - console.error('outDir in ' + process.argv[4] + ' must point to ' + extensionServerDirectory + ' but it points to ' + path.join(path.dirname(tsconfigFile), outDir)); - console.error('Please change outDir in ' + process.argv[4] + ' to ' + path.relative(path.dirname(tsconfigFile), extensionServerDirectory).replace(/\\/g, '/')); - process.exit(1); - } + const outDir = compilerOptions.outDir; + if (!outDir || path.join(path.dirname(tsconfigFile), outDir) !== extensionServerDirectory) { + console.error( + 'outDir in ' + + process.argv[4] + + ' must point to ' + + extensionServerDirectory + + ' but it points to ' + + path.join(path.dirname(tsconfigFile), outDir) + ); + console.error( + 'Please change outDir in ' + + process.argv[4] + + ' to ' + + path.relative(path.dirname(tsconfigFile), extensionServerDirectory).replace(/\\/g, '/') + ); + process.exit(1); + } } if (!fs.existsSync(extensionServerDirectory)) { - fs.mkdirSync(extensionServerDirectory); + fs.mkdirSync(extensionServerDirectory); } -var dest = path.join(extensionServerDirectory, 'package.json'); -console.log('Copying package.json to extension\'s server location...'); +const dest = path.join(extensionServerDirectory, 'package.json'); +console.log("Copying package.json to extension's server location..."); fs.writeFileSync(dest, fs.readFileSync(packageFile)); -var packageLockFile = process.argv[5]; +let packageLockFile = process.argv[5]; if (fs.existsSync(packageLockFile)) { - const packageLockFileDest = path.join(extensionServerDirectory, 'package-lock.json'); - packageLockFile = path.resolve(packageLockFile); - console.log('Copying package-lock.json to extension\'s server location...'); - fs.writeFileSync(packageLockFileDest, fs.readFileSync(packageLockFile)); + const packageLockFileDest = path.join(extensionServerDirectory, 'package-lock.json'); + packageLockFile = path.resolve(packageLockFile); + console.log("Copying package-lock.json to extension's server location..."); + fs.writeFileSync(packageLockFileDest, fs.readFileSync(packageLockFile)); } -console.log('Installing server npm modules into extension\'s server location...'); -process.chdir(extensionServerDirectory) +console.log("Installing server npm modules into extension's server location..."); +process.chdir(extensionServerDirectory); cp.execSync('npm install --production --prefix'); diff --git a/server/jest.config.js b/server/jest.config.js index 0c69f96cb..3d2622ecf 100644 --- a/server/jest.config.js +++ b/server/jest.config.js @@ -1,33 +1,25 @@ /* -* jest.config.js -* -* Configuration for jest tests. -*/ + * jest.config.js + * + * Configuration for jest tests. + */ module.exports = { - roots: [ - '/src/tests' - ], - transform: { - '^.+\\.tsx?$': 'ts-jest' - }, - testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', - moduleFileExtensions: [ - 'ts', - 'tsx', - 'js', - 'jsx' - ], - globals: { - 'ts-jest': { - tsConfig: { - "target": "es6", + roots: ['/src/tests'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + }, + testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + globals: { + 'ts-jest': { + tsConfig: { + target: 'es6', - // Needed because jest calls tsc in a way that doesn't - // inline const enums. - "preserveConstEnums": true - } + // Needed because jest calls tsc in a way that doesn't + // inline const enums. + preserveConstEnums: true + } + } } - } -} - +}; diff --git a/server/package-lock.json b/server/package-lock.json index 7d40975cf..9bc6a6491 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -456,9 +456,9 @@ "dev": true }, "@types/fs-extra": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.1.tgz", - "integrity": "sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", "dev": true, "requires": { "@types/node": "*" @@ -490,24 +490,24 @@ } }, "@types/jest": { - "version": "24.0.24", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.24.tgz", - "integrity": "sha512-vgaG968EDPSJPMunEDdZvZgvxYSmeH8wKqBlHSkBt1pV2XlLEVDzsj1ZhLuI4iG4Pv841tES61txSBF0obh4CQ==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.9.1.tgz", + "integrity": "sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q==", "dev": true, "requires": { "jest-diff": "^24.3.0" } }, "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", "dev": true }, "@types/node": { - "version": "12.12.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.21.tgz", - "integrity": "sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==", + "version": "12.12.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.28.tgz", + "integrity": "sha512-g73GJYJDXgf0jqg+P9S8h2acWbDXNkoCX8DLtJVu7Fkn788pzQ/oJsrdJz/2JejRf/SjfZaAhsw+3nd1D5EWGg==", "dev": true }, "@types/stack-utils": { @@ -532,12 +532,12 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.12.0.tgz", - "integrity": "sha512-1t4r9rpLuEwl3hgt90jY18wJHSyb0E3orVL3DaqwmpiSDHmHiSspVsvsFF78BJ/3NNG3qmeso836jpuBWYziAA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.21.0.tgz", + "integrity": "sha512-b5jjjDMxzcjh/Sbjuo7WyhrQmVJg0WipTHQgXh5Xwx10uYm6nPWqN1WGOsaNq4HR3Zh4wUx4IRQdDkCHwyewyw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "2.12.0", + "@typescript-eslint/experimental-utils": "2.21.0", "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", @@ -545,51 +545,39 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.12.0.tgz", - "integrity": "sha512-jv4gYpw5N5BrWF3ntROvCuLe1IjRenLy5+U57J24NbPGwZFAjhnM45qpq0nDH1y/AZMb3Br25YiNVwyPbz6RkA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz", + "integrity": "sha512-olKw9JP/XUkav4lq0I7S1mhGgONJF9rHNhKFn9wJlpfRVjNo3PPjSvybxEldvCXnvD+WAshSzqH5cEjPp9CsBA==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.12.0", + "@typescript-eslint/typescript-estree": "2.21.0", "eslint-scope": "^5.0.0" - }, - "dependencies": { - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - } } }, "@typescript-eslint/parser": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.12.0.tgz", - "integrity": "sha512-lPdkwpdzxEfjI8TyTzZqPatkrswLSVu4bqUgnB03fHSOwpC7KSerPgJRgIAf11UGNf7HKjJV6oaPZI4AghLU6g==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.21.0.tgz", + "integrity": "sha512-VrmbdrrrvvI6cPPOG7uOgGUFXNYTiSbnRq8ZMyuGa4+qmXJXVLEEz78hKuqupvkpwJQNk1Ucz1TenrRP90gmBg==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.12.0", - "@typescript-eslint/typescript-estree": "2.12.0", + "@typescript-eslint/experimental-utils": "2.21.0", + "@typescript-eslint/typescript-estree": "2.21.0", "eslint-visitor-keys": "^1.1.0" } }, "@typescript-eslint/typescript-estree": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.12.0.tgz", - "integrity": "sha512-rGehVfjHEn8Frh9UW02ZZIfJs6SIIxIu/K1bbci8rFfDE/1lQ8krIJy5OXOV3DVnNdDPtoiPOdEANkLMrwXbiQ==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz", + "integrity": "sha512-NC/nogZNb9IK2MEFQqyDBAciOT8Lp8O3KgAfvHx2Skx6WBo+KmDqlU3R9KxHONaijfTIKtojRe3SZQyMjr3wBw==", "dev": true, "requires": { "debug": "^4.1.1", "eslint-visitor-keys": "^1.1.0", "glob": "^7.1.6", "is-glob": "^4.0.1", - "lodash.unescape": "4.0.1", + "lodash": "^4.17.15", "semver": "^6.3.0", "tsutils": "^3.17.1" }, @@ -1531,9 +1519,9 @@ } }, "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "chrome-trace-event": { @@ -1660,9 +1648,9 @@ } }, "commander": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", - "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "commondir": { @@ -2109,6 +2097,7 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", + "dev": true, "requires": { "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", @@ -2126,6 +2115,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -2263,9 +2253,9 @@ } }, "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -2343,9 +2333,9 @@ "dev": true }, "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", "dev": true }, "evp_bytestokey": { @@ -3342,7 +3332,8 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -3487,6 +3478,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -3499,7 +3491,8 @@ "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true }, "has-value": { "version": "1.0.0", @@ -3843,7 +3836,8 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true }, "is-ci": { "version": "2.0.0", @@ -3877,7 +3871,8 @@ "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true }, "is-descriptor": { "version": "0.1.6", @@ -3935,11 +3930,11 @@ } }, "is-nan": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", - "integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", + "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==", "requires": { - "define-properties": "^1.1.1" + "define-properties": "^1.1.3" } }, "is-number": { @@ -3981,6 +3976,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, "requires": { "has": "^1.0.1" } @@ -3995,6 +3991,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, "requires": { "has-symbols": "^1.0.0" } @@ -4802,12 +4799,6 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", - "dev": true - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4845,9 +4836,9 @@ } }, "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "makeerror": { @@ -5322,12 +5313,13 @@ "object-inspect": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true }, "object-is": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==" }, "object-keys": { "version": "1.1.1", @@ -5343,17 +5335,6 @@ "isobject": "^3.0.0" } }, - "object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -5512,9 +5493,9 @@ "dev": true }, "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, "parallel-transform": { @@ -6654,6 +6635,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -6663,6 +6645,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -6737,9 +6720,9 @@ "dev": true }, "terser": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.3.tgz", - "integrity": "sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", + "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -6902,9 +6885,9 @@ } }, "ts-jest": { - "version": "24.2.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.2.0.tgz", - "integrity": "sha512-Yc+HLyldlIC9iIK8xEN7tV960Or56N49MDP7hubCZUeI7EbIOTsas6rXCMB4kQjLACJ7eDOF4xWEO5qumpKsag==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.3.0.tgz", + "integrity": "sha512-Hb94C/+QRIgjVZlJyiWwouYUF+siNJHJHknyspaOcZ+OQAIdFG/UrdQVXw/0B8Z3No34xkUXZJpOTy9alOWdVQ==", "dev": true, "requires": { "bs-logger": "0.x", @@ -7058,9 +7041,9 @@ "dev": true }, "typescript": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", - "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", + "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==", "dev": true }, "typescript-char": { @@ -7215,14 +7198,13 @@ "dev": true }, "util": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.1.tgz", - "integrity": "sha512-MREAtYOp+GTt9/+kwf00IYoHZyjM8VU4aVrkzUlejyqaIjd2GztVl5V9hGXKlvBKE3gENn/FMfHE5v6hElXGcQ==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.2.tgz", + "integrity": "sha512-XE+MkWQvglYa+IOfBt5UFG93EmncEMP23UqpgDvVZVFBPxwmkK10QRp6pgU4xICPnWRf/t0zPv4noYSUq9gqUQ==", "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", - "object.entries": "^1.1.0", "safe-buffer": "^5.1.2" }, "dependencies": { @@ -7289,9 +7271,9 @@ "dev": true }, "vscode-jsonrpc": { - "version": "5.0.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz", - "integrity": "sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", + "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" }, "vscode-languageserver": { "version": "5.3.0-next.10", @@ -7303,18 +7285,18 @@ } }, "vscode-languageserver-protocol": { - "version": "3.15.0-next.9", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz", - "integrity": "sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g==", + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", + "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", "requires": { - "vscode-jsonrpc": "^5.0.0-next.2", - "vscode-languageserver-types": "^3.15.0-next.5" + "vscode-jsonrpc": "^5.0.1", + "vscode-languageserver-types": "3.15.1" } }, "vscode-languageserver-types": { - "version": "3.15.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz", - "integrity": "sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw==" + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", + "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" }, "vscode-textbuffer": { "version": "1.0.0", @@ -7443,9 +7425,9 @@ "dev": true }, "webpack": { - "version": "4.41.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.3.tgz", - "integrity": "sha512-EcNzP9jGoxpQAXq1VOoTet0ik7/VVU1MovIfcUSAjLowc7GhcQku/sOXALvq5nPpSei2HF6VRhibeJSC3i/Law==", + "version": "4.41.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz", + "integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", @@ -7478,13 +7460,23 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } } } }, "webpack-cli": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.10.tgz", - "integrity": "sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", + "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", "dev": true, "requires": { "chalk": "2.4.2", diff --git a/server/package.json b/server/package.json index 8eb9be56f..bd30bf251 100644 --- a/server/package.json +++ b/server/package.json @@ -31,20 +31,20 @@ "@types/chalk": "^2.2.0", "@types/chokidar": "^2.1.3", "@types/command-line-args": "^5.0.0", - "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.24", - "@types/node": "^12.12.21", - "@typescript-eslint/eslint-plugin": "^2.12.0", - "@typescript-eslint/parser": "^2.12.0", + "@types/fs-extra": "^8.1.0", + "@types/jest": "^24.9.1", + "@types/node": "^12.12.28", + "@typescript-eslint/eslint-plugin": "^2.21.0", + "@typescript-eslint/parser": "^2.21.0", "eslint": "^6.8.0", "fs-extra": "^8.1.0", "jest": "^24.9.0", "node-loader": "^0.6.0", - "ts-jest": "^24.2.0", + "ts-jest": "^24.3.0", "ts-loader": "^6.2.1", - "typescript": "^3.7.3", - "webpack": "^4.41.3", - "webpack-cli": "^3.3.10" + "typescript": "^3.8.2", + "webpack": "^4.41.6", + "webpack-cli": "^3.3.11" }, "types": "out/main.d.ts", "main": "out/main.js" diff --git a/server/src/analyzer/analyzerFileInfo.ts b/server/src/analyzer/analyzerFileInfo.ts index 55c18c6bd..da75a553c 100644 --- a/server/src/analyzer/analyzerFileInfo.ts +++ b/server/src/analyzer/analyzerFileInfo.ts @@ -1,12 +1,12 @@ /* -* analyzerFileInfo.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Information associated with a source file that is used -* by the binder and checker. -*/ + * analyzerFileInfo.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Information associated with a source file that is used + * by the binder and checker. + */ import { DiagnosticSettings, ExecutionEnvironment } from '../common/configOptions'; import { TextRangeDiagnosticSink } from '../common/diagnosticSink'; diff --git a/server/src/analyzer/analyzerNodeInfo.ts b/server/src/analyzer/analyzerNodeInfo.ts index 8060e2633..04bf43669 100644 --- a/server/src/analyzer/analyzerNodeInfo.ts +++ b/server/src/analyzer/analyzerNodeInfo.ts @@ -1,17 +1,24 @@ /* -* analyzerNodeInfo.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Defines objects that hang off the parse nodes in the parse tree. -* It contains information collected during the binder phase that -* can be used for later analysis steps or for language services -* (e.g. hover information). -*/ + * analyzerNodeInfo.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Defines objects that hang off the parse nodes in the parse tree. + * It contains information collected during the binder phase that + * can be used for later analysis steps or for language services + * (e.g. hover information). + */ -import { ClassNode, ExecutionScopeNode, FunctionNode, LambdaNode, ListComprehensionNode, - ModuleNode, ParseNode } from '../parser/parseNodes'; +import { + ClassNode, + ExecutionScopeNode, + FunctionNode, + LambdaNode, + ListComprehensionNode, + ModuleNode, + ParseNode +} from '../parser/parseNodes'; import { AnalyzerFileInfo } from './analyzerFileInfo'; import { FlowFlags, FlowNode } from './codeFlow'; import { Declaration } from './declaration'; @@ -50,8 +57,7 @@ interface AnalyzerNodeInfo { codeFlowExpressions?: Map; } -export type ScopedNode = ModuleNode | ClassNode | FunctionNode | - LambdaNode | ListComprehensionNode; +export type ScopedNode = ModuleNode | ClassNode | FunctionNode | LambdaNode | ListComprehensionNode; // Cleans out all fields that are added by the analyzer phases // (after the post-parse walker). diff --git a/server/src/analyzer/binder.ts b/server/src/analyzer/binder.ts index 67af3ad0c..a1b228e21 100644 --- a/server/src/analyzer/binder.ts +++ b/server/src/analyzer/binder.ts @@ -1,20 +1,20 @@ /* -* binder.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* A parse tree walker that performs basic name binding (creation of -* scopes and associated symbol tables). -* The binder walks the parse tree by scopes starting at the module -* level. When a new scope is detected, it is pushed onto a list and -* walked separately at a later time. (The exception is a class scope, -* which is immediately walked.) Walking the tree in this manner -* simulates the order in which execution normally occurs in a Python -* file. The binder attempts to statically detect runtime errors that -* would be reported by the python interpreter when executing the code. -* This binder doesn't perform any static type checking. -*/ + * binder.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * A parse tree walker that performs basic name binding (creation of + * scopes and associated symbol tables). + * The binder walks the parse tree by scopes starting at the module + * level. When a new scope is detected, it is pushed onto a list and + * walked separately at a later time. (The exception is a class scope, + * which is immediately walked.) Walking the tree in this manner + * simulates the order in which execution normally occurs in a Python + * file. The binder attempts to statically detect runtime errors that + * would be reported by the python interpreter when executing the code. + * This binder doesn't perform any static type checking. + */ import { Commands } from '../commands/commands'; import { DiagnosticLevel } from '../common/configOptions'; @@ -25,23 +25,79 @@ import { convertOffsetsToRange } from '../common/positionUtils'; import { PythonVersion } from '../common/pythonVersion'; import { getEmptyRange } from '../common/textRange'; import { TextRange } from '../common/textRange'; -import { ArgumentCategory, AssertNode, AssignmentExpressionNode, AssignmentNode, - AugmentedAssignmentNode, AwaitNode, BinaryOperationNode, BreakNode, - CallNode, ClassNode, ContinueNode, DelNode, ExceptNode, ExpressionNode, ForNode, - FunctionNode, GlobalNode, IfNode, ImportAsNode, ImportFromNode, LambdaNode, - ListComprehensionNode, MemberAccessNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode, - ParseNode, ParseNodeType, RaiseNode, ReturnNode, StatementNode, StringListNode, - SuiteNode, TernaryNode, TryNode, TypeAnnotationNode, - UnaryOperationNode, WhileNode, WithNode, YieldFromNode, YieldNode } from '../parser/parseNodes'; +import { + ArgumentCategory, + AssertNode, + AssignmentExpressionNode, + AssignmentNode, + AugmentedAssignmentNode, + AwaitNode, + BinaryOperationNode, + BreakNode, + CallNode, + ClassNode, + ContinueNode, + DelNode, + ExceptNode, + ExpressionNode, + ForNode, + FunctionNode, + GlobalNode, + IfNode, + ImportAsNode, + ImportFromNode, + LambdaNode, + ListComprehensionNode, + MemberAccessNode, + ModuleNameNode, + ModuleNode, + NameNode, + NonlocalNode, + ParseNode, + ParseNodeType, + RaiseNode, + ReturnNode, + StatementNode, + StringListNode, + SuiteNode, + TernaryNode, + TryNode, + TypeAnnotationNode, + UnaryOperationNode, + WhileNode, + WithNode, + YieldFromNode, + YieldNode +} from '../parser/parseNodes'; import * as StringTokenUtils from '../parser/stringTokenUtils'; import { KeywordType, OperatorType } from '../parser/tokenizerTypes'; import { AnalyzerFileInfo, ImportLookupResult } from './analyzerFileInfo'; import * as AnalyzerNodeInfo from './analyzerNodeInfo'; -import { createKeyForReference, FlowAssignment, FlowAssignmentAlias, FlowCall, FlowCondition, - FlowFlags, FlowLabel, FlowNode, FlowPostFinally, FlowPreFinallyGate, FlowWildcardImport, - getUniqueFlowNodeId, isCodeFlowSupportedForReference } from './codeFlow'; -import { AliasDeclaration, ClassDeclaration, DeclarationType, FunctionDeclaration, - IntrinsicType, ModuleLoaderActions, ParameterDeclaration, VariableDeclaration } from './declaration'; +import { + createKeyForReference, + FlowAssignment, + FlowAssignmentAlias, + FlowCall, + FlowCondition, + FlowFlags, + FlowLabel, + FlowNode, + FlowPostFinally, + FlowPreFinallyGate, + FlowWildcardImport, + getUniqueFlowNodeId, + isCodeFlowSupportedForReference +} from './codeFlow'; +import { + AliasDeclaration, + ClassDeclaration, + DeclarationType, + FunctionDeclaration, + IntrinsicType, + ModuleLoaderActions, + ParameterDeclaration, + VariableDeclaration +} from './declaration'; import { ImplicitImport, ImportResult, ImportType } from './importResult'; import * as ParseTreeUtils from './parseTreeUtils'; import { ParseTreeWalker } from './parseTreeWalker'; @@ -147,29 +203,31 @@ export class Binder extends ParseTreeWalker { // binding the builtins module itself. const isBuiltInModule = this._fileInfo.builtinsScope === undefined; - this._createNewScope(isBuiltInModule ? ScopeType.Builtin : ScopeType.Module, - this._fileInfo.builtinsScope, () => { + this._createNewScope( + isBuiltInModule ? ScopeType.Builtin : ScopeType.Module, + this._fileInfo.builtinsScope, + () => { + AnalyzerNodeInfo.setScope(node, this._currentScope); - AnalyzerNodeInfo.setScope(node, this._currentScope); + // Bind implicit names. + // List taken from https://docs.python.org/3/reference/import.html#__name__ + this._addBuiltInSymbolToCurrentScope('__doc__', node, 'str'); + this._addBuiltInSymbolToCurrentScope('__name__', node, 'str'); + this._addBuiltInSymbolToCurrentScope('__loader__', node, 'Any'); + this._addBuiltInSymbolToCurrentScope('__package__', node, 'str'); + this._addBuiltInSymbolToCurrentScope('__spec__', node, 'Any'); + this._addBuiltInSymbolToCurrentScope('__path__', node, 'Iterable[str]'); + this._addBuiltInSymbolToCurrentScope('__file__', node, 'str'); + this._addBuiltInSymbolToCurrentScope('__cached__', node, 'str'); - // Bind implicit names. - // List taken from https://docs.python.org/3/reference/import.html#__name__ - this._addBuiltInSymbolToCurrentScope('__doc__', node, 'str'); - this._addBuiltInSymbolToCurrentScope('__name__', node, 'str'); - this._addBuiltInSymbolToCurrentScope('__loader__', node, 'Any'); - this._addBuiltInSymbolToCurrentScope('__package__', node, 'str'); - this._addBuiltInSymbolToCurrentScope('__spec__', node, 'Any'); - this._addBuiltInSymbolToCurrentScope('__path__', node, 'Iterable[str]'); - this._addBuiltInSymbolToCurrentScope('__file__', node, 'str'); - this._addBuiltInSymbolToCurrentScope('__cached__', node, 'str'); + // Create a start node for the module. + this._currentFlowNode = this._createStartFlowNode(); - // Create a start node for the module. - this._currentFlowNode = this._createStartFlowNode(); + this._walkStatementsAndReportUnreachable(node.statements); - this._walkStatementsAndReportUnreachable(node.statements); - - AnalyzerNodeInfo.setCodeFlowExpressions(node, this._currentExecutionScopeReferenceMap); - }); + AnalyzerNodeInfo.setCodeFlowExpressions(node, this._currentExecutionScopeReferenceMap); + } + ); // Perform all analysis that was deferred during the first pass. this._bindDeferred(); @@ -197,15 +255,20 @@ export class Binder extends ParseTreeWalker { if (importResult) { if (!importResult.isImportFound) { - this._addDiagnostic(this._fileInfo.diagnosticSettings.reportMissingImports, + this._addDiagnostic( + this._fileInfo.diagnosticSettings.reportMissingImports, DiagnosticRule.reportMissingImports, - `Import '${ importResult.importName }' could not be resolved`, node); + `Import '${importResult.importName}' could not be resolved`, + node + ); } else if (importResult.importType === ImportType.ThirdParty) { if (!importResult.isStubFile) { const diagnostic = this._addDiagnostic( this._fileInfo.diagnosticSettings.reportMissingTypeStubs, DiagnosticRule.reportMissingTypeStubs, - `Stub file not found for '${ importResult.importName }'`, node); + `Stub file not found for '${importResult.importName}'`, + node + ); if (diagnostic) { // Add a diagnostic action for resolving this diagnostic. const createTypeStubAction: CreateTypeStubFileAction = { @@ -228,8 +291,7 @@ export class Binder extends ParseTreeWalker { type: DeclarationType.Class, node, path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.name.start, - TextRange.getEnd(node.name), this._fileInfo.lines) + range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), this._fileInfo.lines) }; const symbol = this._bindNameToScope(this._currentScope, node.name.value); @@ -237,7 +299,7 @@ export class Binder extends ParseTreeWalker { symbol.addDeclaration(classDeclaration); } - // Stash the declaration in the parse node for later access. + // Stash the declaration in the parse node for later access. AnalyzerNodeInfo.setDeclaration(node, classDeclaration); this.walkMultiple(node.arguments); @@ -277,8 +339,7 @@ export class Binder extends ParseTreeWalker { isMethod: !!containingClassNode, isGenerator: false, path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), - this._fileInfo.lines) + range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), this._fileInfo.lines) }; if (symbol) { @@ -308,8 +369,10 @@ export class Binder extends ParseTreeWalker { // the scope of the containing function or module when they execute. let functionOrModuleNode: ParseNode | undefined = node.parent; while (functionOrModuleNode) { - if (functionOrModuleNode.nodeType === ParseNodeType.Module || - functionOrModuleNode.nodeType === ParseNodeType.Function) { + if ( + functionOrModuleNode.nodeType === ParseNodeType.Module || + functionOrModuleNode.nodeType === ParseNodeType.Function + ) { break; } @@ -358,8 +421,11 @@ export class Binder extends ParseTreeWalker { type: DeclarationType.Parameter, node: paramNode, path: this._fileInfo.filePath, - range: convertOffsetsToRange(paramNode.start, TextRange.getEnd(paramNode), - this._fileInfo.lines) + range: convertOffsetsToRange( + paramNode.start, + TextRange.getEnd(paramNode), + this._fileInfo.lines + ) }; symbol.addDeclaration(paramDeclaration); @@ -421,8 +487,11 @@ export class Binder extends ParseTreeWalker { type: DeclarationType.Parameter, node: paramNode, path: this._fileInfo.filePath, - range: convertOffsetsToRange(paramNode.start, TextRange.getEnd(paramNode), - this._fileInfo.lines) + range: convertOffsetsToRange( + paramNode.start, + TextRange.getEnd(paramNode), + this._fileInfo.lines + ) }; symbol.addDeclaration(paramDeclaration); @@ -478,9 +547,7 @@ export class Binder extends ParseTreeWalker { const evaluationNode = ParseTreeUtils.getEvaluationNodeForAssignmentExpression(node); if (!evaluationNode) { - this._addError( - 'Assignment expression must be within module, function or lambda', - node); + this._addError('Assignment expression must be within module, function or lambda', node); } else { // Bind the name to the containing scope. This special logic is required // because of the behavior defined in PEP 572. Targets of assignment @@ -496,9 +563,10 @@ export class Binder extends ParseTreeWalker { const localSymbol = curScope.lookUpSymbol(node.name.value); if (localSymbol) { this._addError( - `Assignment expression target '${ node.name.value }' ` + + `Assignment expression target '${node.name.value}' ` + `cannot use same name as comprehension for target`, - node.name); + node.name + ); break; } @@ -649,21 +717,23 @@ export class Binder extends ParseTreeWalker { // Determine if the test condition is always true or always false. If so, // we can treat either the then or the else clause as unconditional. const constExprValue = StaticExpressions.evaluateStaticBoolLikeExpression( - node.testExpression, this._fileInfo.executionEnvironment); + node.testExpression, + this._fileInfo.executionEnvironment + ); this._bindConditional(node.testExpression, thenLabel, elseLabel); // Handle the if clause. - this._currentFlowNode = constExprValue === false ? - Binder._unreachableFlowNode : this._finishFlowLabel(thenLabel); + this._currentFlowNode = + constExprValue === false ? Binder._unreachableFlowNode : this._finishFlowLabel(thenLabel); this.walk(node.ifSuite); this._addAntecedent(postIfLabel, this._currentFlowNode); // Now handle the else clause if it's present. If there // are chained "else if" statements, they'll be handled // recursively here. - this._currentFlowNode = constExprValue === true ? - Binder._unreachableFlowNode : this._finishFlowLabel(elseLabel); + this._currentFlowNode = + constExprValue === true ? Binder._unreachableFlowNode : this._finishFlowLabel(elseLabel); if (node.elseSuite) { this.walk(node.elseSuite); } @@ -681,7 +751,9 @@ export class Binder extends ParseTreeWalker { // Determine if the test condition is always true or always false. If so, // we can treat either the while or the else clause as unconditional. const constExprValue = StaticExpressions.evaluateStaticBoolLikeExpression( - node.testExpression, this._fileInfo.executionEnvironment); + node.testExpression, + this._fileInfo.executionEnvironment + ); const preLoopLabel = this._createLoopLabel(); this._addAntecedent(preLoopLabel, this._currentFlowNode); @@ -690,15 +762,15 @@ export class Binder extends ParseTreeWalker { this._bindConditional(node.testExpression, thenLabel, elseLabel); // Handle the while clause. - this._currentFlowNode = constExprValue === false ? - Binder._unreachableFlowNode : this._finishFlowLabel(thenLabel); + this._currentFlowNode = + constExprValue === false ? Binder._unreachableFlowNode : this._finishFlowLabel(thenLabel); this._bindLoopStatement(preLoopLabel, postWhileLabel, () => { this.walk(node.whileSuite); }); this._addAntecedent(preLoopLabel, this._currentFlowNode); - this._currentFlowNode = constExprValue === true ? - Binder._unreachableFlowNode : this._finishFlowLabel(elseLabel); + this._currentFlowNode = + constExprValue === true ? Binder._unreachableFlowNode : this._finishFlowLabel(elseLabel); if (node.elseSuite) { this.walk(node.elseSuite); } @@ -737,8 +809,7 @@ export class Binder extends ParseTreeWalker { node: node.name, isConstant: isConstantName(node.name.value), path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), - this._fileInfo.lines) + range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), this._fileInfo.lines) }; symbol.addDeclaration(declaration); } @@ -757,9 +828,7 @@ export class Binder extends ParseTreeWalker { visitRaise(node: RaiseNode): boolean { if (!node.typeExpression && this._nestedExceptDepth === 0) { - this._addError( - `Raise requires parameter(s) when used outside of except clause `, - node); + this._addError(`Raise requires parameter(s) when used outside of except clause `, node); } if (node.typeExpression) { @@ -893,8 +962,7 @@ export class Binder extends ParseTreeWalker { antecedent: this._currentFlowNode, preFinallyGate }; - this._currentFlowNode = isAfterElseAndExceptsReachable ? - postFinallyNode : Binder._unreachableFlowNode; + this._currentFlowNode = isAfterElseAndExceptsReachable ? postFinallyNode : Binder._unreachableFlowNode; } return false; @@ -916,33 +984,34 @@ export class Binder extends ParseTreeWalker { const unescapedResult = StringTokenUtils.getUnescapedString(stringNode.token); unescapedResult.unescapeErrors.forEach((error: StringTokenUtils.UnescapeError) => { - const start = stringNode.token.start + stringNode.token.prefixLength + - stringNode.token.quoteMarkLength + error.offset; + const start = + stringNode.token.start + + stringNode.token.prefixLength + + stringNode.token.quoteMarkLength + + error.offset; const textRange = { start, length: error.length }; if (error.errorType === StringTokenUtils.UnescapeErrorType.InvalidEscapeSequence) { this._addDiagnostic( this._fileInfo.diagnosticSettings.reportInvalidStringEscapeSequence, DiagnosticRule.reportInvalidStringEscapeSequence, - 'Unsupported escape sequence in string literal', textRange); - } else if (error.errorType === - StringTokenUtils.UnescapeErrorType.EscapeWithinFormatExpression) { - + 'Unsupported escape sequence in string literal', + textRange + ); + } else if (error.errorType === StringTokenUtils.UnescapeErrorType.EscapeWithinFormatExpression) { this._addError( 'Escape sequence (backslash) not allowed in expression portion of f-string', - textRange); - } else if (error.errorType === - StringTokenUtils.UnescapeErrorType.SingleCloseBraceWithinFormatLiteral) { - + textRange + ); + } else if ( + error.errorType === StringTokenUtils.UnescapeErrorType.SingleCloseBraceWithinFormatLiteral + ) { this._addError( 'Single close brace not allowed within f-string literal; use double close brace', - textRange); - } else if (error.errorType === - StringTokenUtils.UnescapeErrorType.UnterminatedFormatExpression) { - - this._addError( - 'Unterminated expression in f-string; missing close brace', - textRange); + textRange + ); + } else if (error.errorType === StringTokenUtils.UnescapeErrorType.UnterminatedFormatExpression) { + this._addError('Unterminated expression in f-string; missing close brace', textRange); } }); } @@ -959,14 +1028,14 @@ export class Binder extends ParseTreeWalker { // Is the binding inconsistent? if (this._notLocalBindings.get(nameValue) === NameBindingType.Nonlocal) { - this._addError(`'${ nameValue }' was already declared nonlocal`, name); + this._addError(`'${nameValue}' was already declared nonlocal`, name); } const valueWithScope = this._currentScope.lookUpSymbolRecursive(nameValue); // Was the name already assigned within this scope before it was declared global? if (valueWithScope && valueWithScope.scope === this._currentScope) { - this._addError(`'${ nameValue }' is assigned before global declaration`, name); + this._addError(`'${nameValue}' is assigned before global declaration`, name); } // Add it to the global scope if it's not already added. @@ -991,16 +1060,16 @@ export class Binder extends ParseTreeWalker { // Is the binding inconsistent? if (this._notLocalBindings.get(nameValue) === NameBindingType.Global) { - this._addError(`'${ nameValue }' was already declared global`, name); + this._addError(`'${nameValue}' was already declared global`, name); } const valueWithScope = this._currentScope.lookUpSymbolRecursive(nameValue); // Was the name already assigned within this scope before it was declared nonlocal? if (valueWithScope && valueWithScope.scope === this._currentScope) { - this._addError(`'${ nameValue }' is assigned before nonlocal declaration`, name); + this._addError(`'${nameValue}' is assigned before nonlocal declaration`, name); } else if (!valueWithScope || valueWithScope.scope === globalScope) { - this._addError(`No binding for nonlocal '${ nameValue }' found`, name); + this._addError(`No binding for nonlocal '${nameValue}' found`, name); } this._notLocalBindings.set(nameValue, NameBindingType.Nonlocal); @@ -1043,11 +1112,11 @@ export class Binder extends ParseTreeWalker { // we'll build a single declaration that describes the combined actions // of both import statements, thus reflecting the behavior of the // python module loader. - const existingDecl = symbol.getDeclarations().find( - decl => decl.type === DeclarationType.Alias && - decl.firstNamePart === firstNamePartValue); + const existingDecl = symbol + .getDeclarations() + .find(decl => decl.type === DeclarationType.Alias && decl.firstNamePart === firstNamePartValue); - const newDecl: AliasDeclaration = existingDecl as AliasDeclaration || { + const newDecl: AliasDeclaration = (existingDecl as AliasDeclaration) || { type: DeclarationType.Alias, node, path: '', @@ -1072,9 +1141,9 @@ export class Binder extends ParseTreeWalker { const namePartValue = node.module.nameParts[i].value; // Is there an existing loader action for this name? - let loaderActions = curLoaderActions.implicitImports ? - curLoaderActions.implicitImports.get(namePartValue) : - undefined; + let loaderActions = curLoaderActions.implicitImports + ? curLoaderActions.implicitImports.get(namePartValue) + : undefined; if (!loaderActions) { // Allocate a new loader action. loaderActions = { @@ -1367,8 +1436,7 @@ export class Binder extends ParseTreeWalker { // if it's the same name as a symbol in an outer scope. If so, we'll // create an alias node in the control flow graph. for (const addedSymbol of addedSymbols) { - const aliasSymbol = this._currentScope.parent!. - lookUpSymbol(addedSymbol[0]); + const aliasSymbol = this._currentScope.parent!.lookUpSymbol(addedSymbol[0]); if (aliasSymbol) { this._createAssignmentAliasFlowNode(addedSymbol[1].id, aliasSymbol.id); } @@ -1411,10 +1479,11 @@ export class Binder extends ParseTreeWalker { const expr = firstDecl.node.parent.rightExpression; if (expr.nodeType === ParseNodeType.List) { expr.entries.forEach(listEntryNode => { - if (listEntryNode.nodeType === ParseNodeType.StringList && - listEntryNode.strings.length === 1 && - listEntryNode.strings[0].nodeType === ParseNodeType.String) { - + if ( + listEntryNode.nodeType === ParseNodeType.StringList && + listEntryNode.strings.length === 1 && + listEntryNode.strings[0].nodeType === ParseNodeType.String + ) { const entryName = listEntryNode.strings[0].value; if (lookupInfo.symbolTable.get(entryName)) { namesToImport.push(entryName); @@ -1527,26 +1596,29 @@ export class Binder extends ParseTreeWalker { this._currentFalseTarget = savedFalseTarget; if (!this._isLogicalExpression(node)) { - this._addAntecedent(trueTarget, - this._createFlowConditional(FlowFlags.TrueCondition, - this._currentFlowNode, node)); - this._addAntecedent(falseTarget, - this._createFlowConditional(FlowFlags.FalseCondition, - this._currentFlowNode, node)); + this._addAntecedent( + trueTarget, + this._createFlowConditional(FlowFlags.TrueCondition, this._currentFlowNode, node) + ); + this._addAntecedent( + falseTarget, + this._createFlowConditional(FlowFlags.FalseCondition, this._currentFlowNode, node) + ); } } - private _createFlowConditional(flags: FlowFlags, antecedent: FlowNode, - expression: ExpressionNode): FlowNode { - + private _createFlowConditional(flags: FlowFlags, antecedent: FlowNode, expression: ExpressionNode): FlowNode { if (antecedent.flags & FlowFlags.Unreachable) { return antecedent; } const staticValue = StaticExpressions.evaluateStaticBoolLikeExpression( - expression, this._fileInfo.executionEnvironment); - if (staticValue === true && (flags & FlowFlags.FalseCondition) || - staticValue === false && (flags & FlowFlags.TrueCondition)) { - + expression, + this._fileInfo.executionEnvironment + ); + if ( + (staticValue === true && flags & FlowFlags.FalseCondition) || + (staticValue === false && flags & FlowFlags.TrueCondition) + ) { return Binder._unreachableFlowNode; } @@ -1580,17 +1652,14 @@ export class Binder extends ParseTreeWalker { } case ParseNodeType.BinaryOperation: { - return expression.operator === OperatorType.And || - expression.operator === OperatorType.Or; + return expression.operator === OperatorType.And || expression.operator === OperatorType.Or; } } return false; } - private _isNarrowingExpression(expression: ExpressionNode, - expressionList: NarrowingExpressionNode[]): boolean { - + private _isNarrowingExpression(expression: ExpressionNode, expressionList: NarrowingExpressionNode[]): boolean { switch (expression.nodeType) { case ParseNodeType.Name: case ParseNodeType.MemberAccess: { @@ -1612,30 +1681,34 @@ export class Binder extends ParseTreeWalker { } case ParseNodeType.BinaryOperation: { - const isOrIsNotOperator = expression.operator === OperatorType.Is || - expression.operator === OperatorType.IsNot; - const equalsOrNotEqualsOperator = expression.operator === OperatorType.Equals || - expression.operator === OperatorType.NotEquals; + const isOrIsNotOperator = + expression.operator === OperatorType.Is || expression.operator === OperatorType.IsNot; + const equalsOrNotEqualsOperator = + expression.operator === OperatorType.Equals || expression.operator === OperatorType.NotEquals; if (isOrIsNotOperator || equalsOrNotEqualsOperator) { // Look for "X is None", "X is not None", "X == None", "X != None". // These are commonly-used patterns used in control flow. - if (expression.rightExpression.nodeType === ParseNodeType.Constant && - expression.rightExpression.constType === KeywordType.None) { - + if ( + expression.rightExpression.nodeType === ParseNodeType.Constant && + expression.rightExpression.constType === KeywordType.None + ) { return this._isNarrowingExpression(expression.leftExpression, expressionList); } // Look for "type(X) is Y" or "type(X) is not Y". - if (isOrIsNotOperator && + if ( + isOrIsNotOperator && expression.leftExpression.nodeType === ParseNodeType.Call && expression.leftExpression.leftExpression.nodeType === ParseNodeType.Name && expression.leftExpression.leftExpression.value === 'type' && expression.leftExpression.arguments.length === 1 && - expression.leftExpression.arguments[0].argumentCategory === ArgumentCategory.Simple) { - + expression.leftExpression.arguments[0].argumentCategory === ArgumentCategory.Simple + ) { return this._isNarrowingExpression( - expression.leftExpression.arguments[0].valueExpression, expressionList); + expression.leftExpression.arguments[0].valueExpression, + expressionList + ); } } @@ -1643,8 +1716,10 @@ export class Binder extends ParseTreeWalker { } case ParseNodeType.UnaryOperation: { - return expression.operator === OperatorType.Not && - this._isNarrowingExpression(expression.expression, expressionList); + return ( + expression.operator === OperatorType.Not && + this._isNarrowingExpression(expression.expression, expressionList) + ); } case ParseNodeType.AugmentedAssignment: { @@ -1652,21 +1727,23 @@ export class Binder extends ParseTreeWalker { } case ParseNodeType.Call: { - if (expression.leftExpression.nodeType === ParseNodeType.Name && - (expression.leftExpression.value === 'isinstance' || - expression.leftExpression.value === 'issubclass') && - expression.arguments.length === 2) { - + if ( + expression.leftExpression.nodeType === ParseNodeType.Name && + (expression.leftExpression.value === 'isinstance' || + expression.leftExpression.value === 'issubclass') && + expression.arguments.length === 2 + ) { return this._isNarrowingExpression(expression.arguments[0].valueExpression, expressionList); } - - if (expression.leftExpression.nodeType === ParseNodeType.Name && - expression.leftExpression.value === 'callable' && - expression.arguments.length === 1) { + if ( + expression.leftExpression.nodeType === ParseNodeType.Name && + expression.leftExpression.value === 'callable' && + expression.arguments.length === 1 + ) { return this._isNarrowingExpression(expression.arguments[0].valueExpression, expressionList); } - } + } } return false; @@ -1805,13 +1882,13 @@ export class Binder extends ParseTreeWalker { } private _addExceptTargets(flowNode: FlowNode) { - // If there are any except targets, then we're in a try block, and we - // have to assume that an exception can be raised after every assignment. - if (this._currentExceptTargets) { - this._currentExceptTargets.forEach(label => { - this._addAntecedent(label, flowNode); - }); - } + // If there are any except targets, then we're in a try block, and we + // have to assume that an exception can be raised after every assignment. + if (this._currentExceptTargets) { + this._currentExceptTargets.forEach(label => { + this._addAntecedent(label, flowNode); + }); + } } private _bindLoopStatement(preLoopLabel: FlowLabel, postLoopLabel: FlowLabel, callback: () => void) { @@ -1840,8 +1917,7 @@ export class Binder extends ParseTreeWalker { // Don't overwrite an existing symbol. let symbol = scope.lookUpSymbol(name); if (!symbol) { - symbol = scope.addSymbol(name, - SymbolFlags.InitiallyUnbound | SymbolFlags.ClassMember); + symbol = scope.addSymbol(name, SymbolFlags.InitiallyUnbound | SymbolFlags.ClassMember); if (this._fileInfo.isStubFile && isPrivateOrProtectedName(name)) { symbol.setIsExternallyHidden(); @@ -1891,9 +1967,11 @@ export class Binder extends ParseTreeWalker { } } - private _addBuiltInSymbolToCurrentScope(nameValue: string, - node: ModuleNode | ClassNode | FunctionNode, type: IntrinsicType) { - + private _addBuiltInSymbolToCurrentScope( + nameValue: string, + node: ModuleNode | ClassNode | FunctionNode, + type: IntrinsicType + ) { const symbol = this._addSymbolToCurrentScope(nameValue, false); if (symbol) { symbol.addDeclaration({ @@ -1937,15 +2015,13 @@ export class Binder extends ParseTreeWalker { return symbol; } - private _createNewScope(scopeType: ScopeType, parentScope: Scope | undefined, - callback: () => void) { - + private _createNewScope(scopeType: ScopeType, parentScope: Scope | undefined, callback: () => void) { const prevScope = this._currentScope; this._currentScope = new Scope(scopeType, parentScope); // If this scope is an execution scope, allocate a new reference map. - const isExecutionScope = scopeType === ScopeType.Builtin || scopeType === ScopeType.Module || - scopeType === ScopeType.Function; + const isExecutionScope = + scopeType === ScopeType.Builtin || scopeType === ScopeType.Module || scopeType === ScopeType.Function; const prevReferenceMap = this._currentExecutionScopeReferenceMap; if (isExecutionScope) { @@ -1988,10 +2064,8 @@ export class Binder extends ParseTreeWalker { let symbol = memberAccessInfo.classScope.lookUpSymbol(name.value); if (!symbol) { - symbol = memberAccessInfo.classScope.addSymbol(name.value, - SymbolFlags.InitiallyUnbound); - const honorPrivateNaming = - this._fileInfo.diagnosticSettings.reportPrivateUsage !== 'none'; + symbol = memberAccessInfo.classScope.addSymbol(name.value, SymbolFlags.InitiallyUnbound); + const honorPrivateNaming = this._fileInfo.diagnosticSettings.reportPrivateUsage !== 'none'; if (isPrivateOrProtectedName(name.value) && honorPrivateNaming) { symbol.setIsPrivateMember(); } @@ -2009,9 +2083,11 @@ export class Binder extends ParseTreeWalker { isConstant: isConstantName(name.value), inferredTypeSource: source, path: this._fileInfo.filePath, - range: convertOffsetsToRange(target.memberName.start, + range: convertOffsetsToRange( + target.memberName.start, target.memberName.start + target.memberName.length, - this._fileInfo.lines) + this._fileInfo.lines + ) }; symbol.addDeclaration(declaration); } @@ -2059,8 +2135,7 @@ export class Binder extends ParseTreeWalker { isConstant: isConstantName(name.value), isFinal: finalInfo.isFinal, path: this._fileInfo.filePath, - typeAnnotationNode: finalInfo.isFinal ? - finalInfo.finalTypeNode : typeAnnotation, + typeAnnotationNode: finalInfo.isFinal ? finalInfo.finalTypeNode : typeAnnotation, range: convertOffsetsToRange(name.start, TextRange.getEnd(name), this._fileInfo.lines) }; symbolWithScope.symbol.addDeclaration(declaration); @@ -2085,10 +2160,8 @@ export class Binder extends ParseTreeWalker { let symbol = memberAccessInfo.classScope.lookUpSymbol(name.value); if (!symbol) { - symbol = memberAccessInfo.classScope.addSymbol(name.value, - SymbolFlags.InitiallyUnbound); - const honorPrivateNaming = - this._fileInfo.diagnosticSettings.reportPrivateUsage !== 'none'; + symbol = memberAccessInfo.classScope.addSymbol(name.value, SymbolFlags.InitiallyUnbound); + const honorPrivateNaming = this._fileInfo.diagnosticSettings.reportPrivateUsage !== 'none'; if (isPrivateOrProtectedName(name.value) && honorPrivateNaming) { symbol.setIsPrivateMember(); } @@ -2107,11 +2180,12 @@ export class Binder extends ParseTreeWalker { isConstant: isConstantName(name.value), isFinal: finalInfo.isFinal, path: this._fileInfo.filePath, - typeAnnotationNode: finalInfo.isFinal ? - finalInfo.finalTypeNode : typeAnnotation, - range: convertOffsetsToRange(target.memberName.start, + typeAnnotationNode: finalInfo.isFinal ? finalInfo.finalTypeNode : typeAnnotation, + range: convertOffsetsToRange( + target.memberName.start, target.memberName.start + target.memberName.length, - this._fileInfo.lines) + this._fileInfo.lines + ) }; symbol.addDeclaration(declaration); @@ -2122,9 +2196,7 @@ export class Binder extends ParseTreeWalker { } if (!declarationHandled) { - this._addError( - `Type annotation not supported for this type of expression`, - typeAnnotation); + this._addError(`Type annotation not supported for this type of expression`, typeAnnotation); } } @@ -2145,15 +2217,14 @@ export class Binder extends ParseTreeWalker { isFinal = true; } } else if (typeAnnotation.nodeType === ParseNodeType.MemberAccess) { - if (typeAnnotation.leftExpression.nodeType === ParseNodeType.Name && + if ( + typeAnnotation.leftExpression.nodeType === ParseNodeType.Name && typeAnnotation.leftExpression.value === 'typing' && - typeAnnotation.memberName.value === 'Final') { - + typeAnnotation.memberName.value === 'Final' + ) { isFinal = true; } - } else if (typeAnnotation.nodeType === ParseNodeType.Index && - typeAnnotation.items.items.length === 1) { - + } else if (typeAnnotation.nodeType === ParseNodeType.Index && typeAnnotation.items.items.length === 1) { // Recursively call to see if the base expression is "Final". const finalInfo = this._isAnnotationFinal(typeAnnotation.baseExpression); if (finalInfo.isFinal) { @@ -2249,9 +2320,9 @@ export class Binder extends ParseTreeWalker { private _addImplicitImportsToLoaderActions(importResult: ImportResult, loaderActions: ModuleLoaderActions) { importResult.implicitImports.forEach(implicitImport => { - const existingLoaderAction = loaderActions.implicitImports ? - loaderActions.implicitImports.get(implicitImport.name) : - undefined; + const existingLoaderAction = loaderActions.implicitImports + ? loaderActions.implicitImports.get(implicitImport.name) + : undefined; if (existingLoaderAction) { existingLoaderAction.path = implicitImport.path; } else { @@ -2291,15 +2362,15 @@ export class Binder extends ParseTreeWalker { const assignedNameNode = annotationNode.valueExpression; const specialTypes: { [name: string]: boolean } = { - 'Tuple': true, - 'Generic': true, - 'Protocol': true, - 'Callable': true, - 'Type': true, - 'ClassVar': true, - 'Final': true, - 'Literal': true, - 'TypedDict': true + Tuple: true, + Generic: true, + Protocol: true, + Callable: true, + Type: true, + ClassVar: true, + Final: true, + Literal: true, + TypedDict: true }; const assignedName = assignedNameNode.value; @@ -2314,8 +2385,11 @@ export class Binder extends ParseTreeWalker { type: DeclarationType.SpecialBuiltInClass, node: annotationNode, path: this._fileInfo.filePath, - range: convertOffsetsToRange(annotationNode.start, - TextRange.getEnd(annotationNode), this._fileInfo.lines) + range: convertOffsetsToRange( + annotationNode.start, + TextRange.getEnd(annotationNode), + this._fileInfo.lines + ) }); } return true; @@ -2348,13 +2422,11 @@ export class Binder extends ParseTreeWalker { const functionNode = ParseTreeUtils.getEnclosingFunction(node); if (!functionNode) { - this._addError( - `'yield' not allowed outside of a function`, node); + this._addError(`'yield' not allowed outside of a function`, node); } else if (functionNode.isAsync && node.nodeType === ParseNodeType.YieldFrom) { // PEP 525 indicates that 'yield from' is not allowed in an // async function. - this._addError( - `'yield from' not allowed in an async function`, node); + this._addError(`'yield from' not allowed in an async function`, node); } if (this._targetFunctionDeclaration) { @@ -2372,9 +2444,7 @@ export class Binder extends ParseTreeWalker { AnalyzerNodeInfo.setFlowNode(node, this._currentFlowNode); } - private _addDiagnostic(diagLevel: DiagnosticLevel, rule: string, - message: string, textRange: TextRange) { - + private _addDiagnostic(diagLevel: DiagnosticLevel, rule: string, message: string, textRange: TextRange) { if (diagLevel === 'error') { const diagnostic = this._addError(message, textRange); diagnostic.setRule(rule); @@ -2388,8 +2458,7 @@ export class Binder extends ParseTreeWalker { } private _addUnusedCode(textRange: TextRange) { - return this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange( - 'Code is unreachable', textRange); + return this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange('Code is unreachable', textRange); } private _addError(message: string, textRange: TextRange) { diff --git a/server/src/analyzer/checker.ts b/server/src/analyzer/checker.ts index c7c6ad443..cd66d1e06 100644 --- a/server/src/analyzer/checker.ts +++ b/server/src/analyzer/checker.ts @@ -1,29 +1,64 @@ /* -* checker.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* A parse tree walker that performs static type checking for -* a source file. Most of its work is performed by the type -* evaluator, but this module touches every node in the file -* to ensure that all statements and expressions are evaluated -* and checked. It also performs some additional checks that -* cannot (or should not be) performed lazily. -*/ + * checker.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * A parse tree walker that performs static type checking for + * a source file. Most of its work is performed by the type + * evaluator, but this module touches every node in the file + * to ensure that all statements and expressions are evaluated + * and checked. It also performs some additional checks that + * cannot (or should not be) performed lazily. + */ import { DiagnosticLevel } from '../common/configOptions'; import { assert } from '../common/debug'; import { Diagnostic, DiagnosticAddendum } from '../common/diagnostic'; import { DiagnosticRule } from '../common/diagnosticRules'; import { TextRange } from '../common/textRange'; -import { AssertNode, AssignmentExpressionNode, AssignmentNode, AugmentedAssignmentNode, - BinaryOperationNode, CallNode, ClassNode, DelNode, ErrorNode, ExceptNode, - FormatStringNode, ForNode, FunctionNode, IfNode, ImportAsNode, ImportFromAsNode, ImportFromNode, - IndexNode, LambdaNode, ListComprehensionNode, MemberAccessNode, ModuleNode, NameNode, - ParameterCategory, ParseNode, ParseNodeType, RaiseNode, ReturnNode, SliceNode, StringListNode, - SuiteNode, TernaryNode, TupleNode, TypeAnnotationNode, UnaryOperationNode, UnpackNode, - WhileNode, WithNode, YieldFromNode, YieldNode } from '../parser/parseNodes'; +import { + AssertNode, + AssignmentExpressionNode, + AssignmentNode, + AugmentedAssignmentNode, + BinaryOperationNode, + CallNode, + ClassNode, + DelNode, + ErrorNode, + ExceptNode, + FormatStringNode, + ForNode, + FunctionNode, + IfNode, + ImportAsNode, + ImportFromAsNode, + ImportFromNode, + IndexNode, + LambdaNode, + ListComprehensionNode, + MemberAccessNode, + ModuleNode, + NameNode, + ParameterCategory, + ParseNode, + ParseNodeType, + RaiseNode, + ReturnNode, + SliceNode, + StringListNode, + SuiteNode, + TernaryNode, + TupleNode, + TypeAnnotationNode, + UnaryOperationNode, + UnpackNode, + WhileNode, + WithNode, + YieldFromNode, + YieldNode +} from '../parser/parseNodes'; import { AnalyzerFileInfo } from './analyzerFileInfo'; import * as AnalyzerNodeInfo from './analyzerNodeInfo'; import { Declaration, DeclarationType } from './declaration'; @@ -36,11 +71,31 @@ import { Symbol } from './symbol'; import * as SymbolNameUtils from './symbolNameUtils'; import { getLastTypedDeclaredForSymbol, isFinalVariable } from './symbolUtils'; import { TypeEvaluator } from './typeEvaluator'; -import { ClassType, combineTypes, FunctionType, isAnyOrUnknown, isNoneOrNever, isTypeSame, - NoneType, ObjectType, Type, TypeCategory, UnknownType } from './types'; -import { containsUnknown, derivesFromClassRecursive, doForSubtypes, - getDeclaredGeneratorReturnType, getDeclaredGeneratorYieldType, getSymbolFromBaseClasses, - isNoReturnType, isProperty, specializeType, transformTypeObjectToClass } from './typeUtils'; +import { + ClassType, + combineTypes, + FunctionType, + isAnyOrUnknown, + isNoneOrNever, + isTypeSame, + NoneType, + ObjectType, + Type, + TypeCategory, + UnknownType +} from './types'; +import { + containsUnknown, + derivesFromClassRecursive, + doForSubtypes, + getDeclaredGeneratorReturnType, + getDeclaredGeneratorYieldType, + getSymbolFromBaseClasses, + isNoReturnType, + isProperty, + specializeType, + transformTypeObjectToClass +} from './typeUtils'; export class Checker extends ParseTreeWalker { private readonly _moduleNode: ModuleNode; @@ -52,7 +107,6 @@ export class Checker extends ParseTreeWalker { private _scopedNodes: AnalyzerNodeInfo.ScopedNode[] = []; constructor(node: ModuleNode, evaluator: TypeEvaluator) { - super(); this._moduleNode = node; @@ -87,9 +141,9 @@ export class Checker extends ParseTreeWalker { if (classTypeResult) { this._validateClassMethods(classTypeResult.classType); - + this._validateFinalMemberOverrides(classTypeResult.classType); - + if (ClassType.isTypedDictClass(classTypeResult.classType)) { this._validateTypedDictClassSuite(node.suite); } @@ -113,16 +167,18 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownParameterType, DiagnosticRule.reportUnknownParameterType, - `Type of parameter '${ param.name.value }' is unknown`, - param.name); + `Type of parameter '${param.name.value}' is unknown`, + param.name + ); } else if (containsUnknown(paramType)) { const diagAddendum = new DiagnosticAddendum(); - diagAddendum.addMessage(`Parameter type is '${ this._evaluator.printType(paramType) }'`); + diagAddendum.addMessage(`Parameter type is '${this._evaluator.printType(paramType)}'`); this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownParameterType, DiagnosticRule.reportUnknownParameterType, - `Type of parameter '${ param.name.value }' is partially unknown` + diagAddendum.getString(), - param.name); + `Type of parameter '${param.name.value}' is partially unknown` + diagAddendum.getString(), + param.name + ); } } }); @@ -180,15 +236,17 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownLambdaType, DiagnosticRule.reportUnknownLambdaType, - `Type of '${ param.name.value }' is unknown`, - param.name); + `Type of '${param.name.value}' is unknown`, + param.name + ); } else if (containsUnknown(paramType)) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownLambdaType, DiagnosticRule.reportUnknownLambdaType, - `Type of '${ param.name.value }', ` + - `'${ this._evaluator.printType(paramType) }', is partially unknown`, - param.name); + `Type of '${param.name.value}', ` + + `'${this._evaluator.printType(paramType)}', is partially unknown`, + param.name + ); } } } @@ -200,13 +258,16 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownLambdaType, DiagnosticRule.reportUnknownLambdaType, - `Type of lambda expression is unknown`, node.expression); + `Type of lambda expression is unknown`, + node.expression + ); } else if (containsUnknown(returnType)) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownLambdaType, DiagnosticRule.reportUnknownLambdaType, - `Type of lambda expression, '${ this._evaluator.printType(returnType) }', is partially unknown`, - node.expression); + `Type of lambda expression, '${this._evaluator.printType(returnType)}', is partially unknown`, + node.expression + ); } } @@ -225,7 +286,8 @@ export class Checker extends ParseTreeWalker { this._fileInfo.diagnosticSettings.reportCallInDefaultInitializer, DiagnosticRule.reportCallInDefaultInitializer, `Function calls within default value initializer are not permitted`, - node); + node + ); } return true; @@ -263,9 +325,9 @@ export class Checker extends ParseTreeWalker { let returnType: Type; const enclosingFunctionNode = ParseTreeUtils.getEnclosingFunction(node); - const declaredReturnType = enclosingFunctionNode ? - this._evaluator.getFunctionDeclaredReturnType(enclosingFunctionNode) : - undefined; + const declaredReturnType = enclosingFunctionNode + ? this._evaluator.getFunctionDeclaredReturnType(enclosingFunctionNode) + : undefined; if (node.returnExpression) { returnType = this._evaluator.getType(node.returnExpression) || UnknownType.create(); @@ -279,7 +341,8 @@ export class Checker extends ParseTreeWalker { if (isNoReturnType(declaredReturnType)) { this._evaluator.addError( `Function with declared return type 'NoReturn' cannot include a return statement`, - node); + node + ); } else { const diagAddendum = new DiagnosticAddendum(); @@ -288,10 +351,11 @@ export class Checker extends ParseTreeWalker { const specializedDeclaredType = specializeType(declaredReturnType, undefined); if (!this._evaluator.canAssignType(specializedDeclaredType, returnType, diagAddendum)) { this._evaluator.addError( - `Expression of type '${ this._evaluator.printType(returnType) }' cannot be assigned ` + - `to return type '${ this._evaluator.printType(specializedDeclaredType) }'` + + `Expression of type '${this._evaluator.printType(returnType)}' cannot be assigned ` + + `to return type '${this._evaluator.printType(specializedDeclaredType)}'` + diagAddendum.getString(), - node.returnExpression ? node.returnExpression : node); + node.returnExpression ? node.returnExpression : node + ); } } } @@ -300,13 +364,16 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownVariableType, DiagnosticRule.reportUnknownVariableType, - `Return type is unknown`, node.returnExpression!); + `Return type is unknown`, + node.returnExpression! + ); } else if (containsUnknown(returnType)) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownVariableType, DiagnosticRule.reportUnknownVariableType, - `Return type, '${ this._evaluator.printType(returnType) }', is partially unknown`, - node.returnExpression!); + `Return type, '${this._evaluator.printType(returnType)}', is partially unknown`, + node.returnExpression! + ); } } @@ -314,8 +381,7 @@ export class Checker extends ParseTreeWalker { } visitYield(node: YieldNode) { - const yieldType = node.expression ? - this._evaluator.getType(node.expression) : NoneType.create(); + const yieldType = node.expression ? this._evaluator.getType(node.expression) : NoneType.create(); // Wrap the yield type in an Iterator. let adjYieldType = yieldType; @@ -354,14 +420,20 @@ export class Checker extends ParseTreeWalker { if (!isAnyOrUnknown(subtype)) { if (subtype.category === TypeCategory.Class) { if (!derivesFromClassRecursive(subtype, baseExceptionType)) { - diagAddendum.addMessage(`'${ this._evaluator.printType(subtype) }' does not derive from BaseException`); + diagAddendum.addMessage( + `'${this._evaluator.printType(subtype)}' does not derive from BaseException` + ); } } else if (subtype.category === TypeCategory.Object) { if (!derivesFromClassRecursive(subtype.classType, baseExceptionType)) { - diagAddendum.addMessage(`'${ this._evaluator.printType(subtype) }' does not derive from BaseException`); + diagAddendum.addMessage( + `'${this._evaluator.printType(subtype)}' does not derive from BaseException` + ); } } else { - diagAddendum.addMessage(`'${ this._evaluator.printType(subtype) }' does not derive from BaseException`); + diagAddendum.addMessage( + `'${this._evaluator.printType(subtype)}' does not derive from BaseException` + ); } } @@ -371,7 +443,8 @@ export class Checker extends ParseTreeWalker { if (diagAddendum.getMessageCount() > 0) { this._evaluator.addError( `Expected exception class or object` + diagAddendum.getString(), - node.typeExpression); + node.typeExpression + ); } } } @@ -387,10 +460,14 @@ export class Checker extends ParseTreeWalker { if (!isAnyOrUnknown(subtype) && !isNoneOrNever(subtype)) { if (subtype.category === TypeCategory.Object) { if (!derivesFromClassRecursive(subtype.classType, baseExceptionType)) { - diagAddendum.addMessage(`'${ this._evaluator.printType(subtype) }' does not derive from BaseException`); + diagAddendum.addMessage( + `'${this._evaluator.printType(subtype)}' does not derive from BaseException` + ); } } else { - diagAddendum.addMessage(`'${ this._evaluator.printType(subtype) }' does not derive from BaseException`); + diagAddendum.addMessage( + `'${this._evaluator.printType(subtype)}' does not derive from BaseException` + ); } } @@ -400,7 +477,8 @@ export class Checker extends ParseTreeWalker { if (diagAddendum.getMessageCount() > 0) { this._evaluator.addError( `Expected exception object or None` + diagAddendum.getString(), - node.valueExpression); + node.valueExpression + ); } } } @@ -430,10 +508,13 @@ export class Checker extends ParseTreeWalker { if (type && type.category === TypeCategory.Object) { if (ClassType.isBuiltIn(type.classType, 'Tuple') && type.classType.typeArguments) { if (type.classType.typeArguments.length > 0) { - this._evaluator.addDiagnosticForTextRange(this._fileInfo, + this._evaluator.addDiagnosticForTextRange( + this._fileInfo, this._fileInfo.diagnosticSettings.reportAssertAlwaysTrue, DiagnosticRule.reportAssertAlwaysTrue, - `Assert expression always evaluates to true`, node.testExpression); + `Assert expression always evaluates to true`, + node.testExpression + ); } } } @@ -501,10 +582,13 @@ export class Checker extends ParseTreeWalker { } if (node.strings.length > 1) { - this._evaluator.addDiagnosticForTextRange(this._fileInfo, + this._evaluator.addDiagnosticForTextRange( + this._fileInfo, this._fileInfo.diagnosticSettings.reportImplicitStringConcatenation, DiagnosticRule.reportImplicitStringConcatenation, - `Implicit string concatenation not allowed`, node); + `Implicit string concatenation not allowed`, + node + ); } return true; @@ -591,12 +675,12 @@ export class Checker extends ParseTreeWalker { } else if (exceptionType.category === TypeCategory.Class) { if (!derivesFromBaseException(exceptionType)) { diagAddendum.addMessage( - `'${this._evaluator.printType(exceptionType)}' does not derive from BaseException`); + `'${this._evaluator.printType(exceptionType)}' does not derive from BaseException` + ); } resultingExceptionType = ObjectType.create(exceptionType); } else if (exceptionType.category === TypeCategory.Object) { - const iterableType = this._evaluator.getTypeFromIterable( - exceptionType, false, errorNode, false); + const iterableType = this._evaluator.getTypeFromIterable(exceptionType, false, errorNode, false); resultingExceptionType = doForSubtypes(iterableType, subtype => { if (isAnyOrUnknown(subtype)) { @@ -607,23 +691,25 @@ export class Checker extends ParseTreeWalker { if (transformedSubtype.category === TypeCategory.Class) { if (!derivesFromBaseException(transformedSubtype)) { diagAddendum.addMessage( - `'${this._evaluator.printType(exceptionType)}' does not derive from BaseException`); + `'${this._evaluator.printType(exceptionType)}' does not derive from BaseException` + ); } return ObjectType.create(transformedSubtype); } diagAddendum.addMessage( - `'${this._evaluator.printType(exceptionType)}' does not derive from BaseException`); + `'${this._evaluator.printType(exceptionType)}' does not derive from BaseException` + ); return UnknownType.create(); }); } if (diagAddendum.getMessageCount() > 0) { this._evaluator.addError( - `'${this._evaluator.printType(exceptionType)}' is not valid exception class` + - diagAddendum.getString(), - errorNode); + `'${this._evaluator.printType(exceptionType)}' is not valid exception class` + diagAddendum.getString(), + errorNode + ); } return resultingExceptionType || UnknownType.create(); @@ -660,20 +746,14 @@ export class Checker extends ParseTreeWalker { decls.forEach(decl => { if (isFinalVariableDeclaration(decl)) { if (sawFinal) { - this._evaluator.addError( - `'${ name }' was previously declared as Final`, - decl.node - ); + this._evaluator.addError(`'${name}' was previously declared as Final`, decl.node); } sawFinal = true; } if (decl.type === DeclarationType.Variable && decl.inferredTypeSource) { if (sawAssignment) { - this._evaluator.addError( - `'${ name }' is declared Final and can be assigned only once`, - decl.node - ); + this._evaluator.addError(`'${name}' is declared Final and can be assigned only once`, decl.node); } sawAssignment = true; } @@ -683,10 +763,7 @@ export class Checker extends ParseTreeWalker { if (!sawAssignment && !this._fileInfo.isStubFile) { const firstDecl = decls.find(decl => decl.type === DeclarationType.Variable && decl.isFinal); if (firstDecl) { - this._evaluator.addError( - `'${ name }' is declared Final, but value is not assigned`, - firstDecl.node - ); + this._evaluator.addError(`'${name}' is declared Final, but value is not assigned`, firstDecl.node); } } } @@ -729,9 +806,7 @@ export class Checker extends ParseTreeWalker { const addPrimaryDeclInfo = (diag?: Diagnostic) => { if (diag) { let primaryDeclNode: ParseNode | undefined; - if (primaryDecl.type === DeclarationType.Function || - primaryDecl.type === DeclarationType.Class) { - + if (primaryDecl.type === DeclarationType.Function || primaryDecl.type === DeclarationType.Class) { primaryDeclNode = primaryDecl.node.name; } else if (primaryDecl.type === DeclarationType.Variable) { if (primaryDecl.node.nodeType === ParseNodeType.Name) { @@ -744,8 +819,7 @@ export class Checker extends ParseTreeWalker { } if (primaryDeclNode) { - diag.addRelatedInfo(`See ${ primaryDeclType }declaration`, - primaryDecl.path, primaryDecl.range); + diag.addRelatedInfo(`See ${primaryDeclType}declaration`, primaryDecl.path, primaryDecl.range); } } }; @@ -753,14 +827,13 @@ export class Checker extends ParseTreeWalker { for (const otherDecl of otherDecls) { if (otherDecl.type === DeclarationType.Class) { const diag = this._evaluator.addError( - `Class declaration '${ name }' is obscured by a ${ primaryDeclType }` + - `declaration of the same name`, + `Class declaration '${name}' is obscured by a ${primaryDeclType}` + `declaration of the same name`, otherDecl.node.name ); addPrimaryDeclInfo(diag); } else if (otherDecl.type === DeclarationType.Function) { const diag = this._evaluator.addError( - `Function declaration '${ name }' is obscured by a ${ primaryDeclType }` + + `Function declaration '${name}' is obscured by a ${primaryDeclType}` + `declaration of the same name`, otherDecl.node.name ); @@ -768,8 +841,7 @@ export class Checker extends ParseTreeWalker { } else if (otherDecl.type === DeclarationType.Parameter) { if (otherDecl.node.name) { const diag = this._evaluator.addError( - `Parameter '${ name }' is obscured by a ${ primaryDeclType }` + - `declaration of the same name`, + `Parameter '${name}' is obscured by a ${primaryDeclType}` + `declaration of the same name`, otherDecl.node.name ); addPrimaryDeclInfo(diag); @@ -792,8 +864,8 @@ export class Checker extends ParseTreeWalker { if (!duplicateIsOk) { const diag = this._evaluator.addError( - `Declared type for '${ name }' is obscured by an ` + - `incompatible ${ primaryDeclType }declaration`, + `Declared type for '${name}' is obscured by an ` + + `incompatible ${primaryDeclType}declaration`, otherDecl.node ); addPrimaryDeclInfo(diag); @@ -802,7 +874,7 @@ export class Checker extends ParseTreeWalker { } else if (primaryType && !isProperty(primaryType)) { if (primaryDecl.type === DeclarationType.Function || primaryDecl.type === DeclarationType.Class) { const diag = this._evaluator.addError( - `Declared ${ primaryDeclType }already exists for '${ name }'`, + `Declared ${primaryDeclType}already exists for '${name}'`, otherDecl.node ); addPrimaryDeclInfo(diag); @@ -830,8 +902,7 @@ export class Checker extends ParseTreeWalker { const decls = symbol.getDeclarations(); decls.forEach(decl => { - this._conditionallyReportUnusedDeclaration(decl, - this._isSymbolPrivate(name, scopeType)); + this._conditionallyReportUnusedDeclaration(decl, this._isSymbolPrivate(name, scopeType)); }); } @@ -856,12 +927,17 @@ export class Checker extends ParseTreeWalker { const textRange: TextRange = { start: nameParts[0].start, length: nameParts[0].length }; TextRange.extend(textRange, nameParts[nameParts.length - 1]); this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange( - `'${ multipartName }' is not accessed`, textRange); + `'${multipartName}' is not accessed`, + textRange + ); - this._evaluator.addDiagnosticForTextRange(this._fileInfo, + this._evaluator.addDiagnosticForTextRange( + this._fileInfo, this._fileInfo.diagnosticSettings.reportUnusedImport, DiagnosticRule.reportUnusedImport, - `Import '${ multipartName }' is not accessed`, textRange); + `Import '${multipartName}' is not accessed`, + textRange + ); return; } } @@ -870,16 +946,17 @@ export class Checker extends ParseTreeWalker { // unused imports. Don't report these because they're in generated // files that shouldn't be edited. const importFrom = decl.node.parent as ImportFromNode; - if (importFrom.module.nameParts.length === 0 || - importFrom.module.nameParts[0].value !== '__future__' && - !this._fileInfo.filePath.endsWith('_pb2.py')) { - + if ( + importFrom.module.nameParts.length === 0 || + (importFrom.module.nameParts[0].value !== '__future__' && + !this._fileInfo.filePath.endsWith('_pb2.py')) + ) { nameNode = decl.node.alias || decl.node.name; } } if (nameNode) { - message = `Import '${ nameNode.value }' is not accessed`; + message = `Import '${nameNode.value}' is not accessed`; } break; @@ -892,7 +969,7 @@ export class Checker extends ParseTreeWalker { if (decl.node.nodeType === ParseNodeType.Name) { nameNode = decl.node; rule = DiagnosticRule.reportUnusedVariable; - message = `Variable '${ nameNode.value }' is not accessed`; + message = `Variable '${nameNode.value}' is not accessed`; } break; @@ -903,7 +980,7 @@ export class Checker extends ParseTreeWalker { diagnosticLevel = this._fileInfo.diagnosticSettings.reportUnusedClass; nameNode = decl.node.name; rule = DiagnosticRule.reportUnusedClass; - message = `Class '${ nameNode.value }' is not accessed`; + message = `Class '${nameNode.value}' is not accessed`; break; case DeclarationType.Function: @@ -913,7 +990,7 @@ export class Checker extends ParseTreeWalker { diagnosticLevel = this._fileInfo.diagnosticSettings.reportUnusedFunction; nameNode = decl.node.name; rule = DiagnosticRule.reportUnusedFunction; - message = `Function '${ nameNode.value }' is not accessed`; + message = `Function '${nameNode.value}' is not accessed`; break; default: @@ -921,20 +998,19 @@ export class Checker extends ParseTreeWalker { } if (nameNode && rule !== undefined && message) { - this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange( - `'${ nameNode.value }' is not accessed`, nameNode); - this._evaluator.addDiagnostic( - diagnosticLevel, rule, message, nameNode); + this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange(`'${nameNode.value}' is not accessed`, nameNode); + this._evaluator.addDiagnostic(diagnosticLevel, rule, message, nameNode); } } // Validates that a call to isinstance or issubclass are necessary. This is a // common source of programming errors. private _validateIsInstanceCallNecessary(node: CallNode) { - if (node.leftExpression.nodeType !== ParseNodeType.Name || - (node.leftExpression.value !== 'isinstance' && - node.leftExpression.value !== 'issubclass') || - node.arguments.length !== 2) { + if ( + node.leftExpression.nodeType !== ParseNodeType.Name || + (node.leftExpression.value !== 'isinstance' && node.leftExpression.value !== 'issubclass') || + node.arguments.length !== 2 + ) { return; } @@ -990,15 +1066,17 @@ export class Checker extends ParseTreeWalker { // According to PEP 544, protocol classes cannot be used as the right-hand // argument to isinstance or issubclass. if (classTypeList.some(type => ClassType.isProtocol(type))) { - this._evaluator.addError(`Protocol class cannot be used in ${ callName } call`, - node.arguments[1].valueExpression); + this._evaluator.addError( + `Protocol class cannot be used in ${callName} call`, + node.arguments[1].valueExpression + ); } const finalizeFilteredTypeList = (types: Type[]): Type => { return combineTypes(types); }; - const filterType = (varType: ClassType): (ObjectType[] | ClassType[]) => { + const filterType = (varType: ClassType): ObjectType[] | ClassType[] => { const filteredTypes: ClassType[] = []; for (const filterType of classTypeList) { @@ -1067,16 +1145,18 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnnecessaryIsInstance, DiagnosticRule.reportUnnecessaryIsInstance, - `Unnecessary ${ callName } call: '${ this._evaluator.printType(arg0Type) }' ` + - `is never ${ callType } of '${ this._evaluator.printType(getTestType()) }'`, - node); + `Unnecessary ${callName} call: '${this._evaluator.printType(arg0Type)}' ` + + `is never ${callType} of '${this._evaluator.printType(getTestType())}'`, + node + ); } else if (isTypeSame(filteredType, arg0Type)) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnnecessaryIsInstance, DiagnosticRule.reportUnnecessaryIsInstance, - `Unnecessary ${ callName } call: '${ this._evaluator.printType(arg0Type) }' ` + - `is always ${ callType } of '${ this._evaluator.printType(getTestType()) }'`, - node); + `Unnecessary ${callName} call: '${this._evaluator.printType(arg0Type)}' ` + + `is always ${callType} of '${this._evaluator.printType(getTestType())}'`, + node + ); } } @@ -1123,8 +1203,8 @@ export class Checker extends ParseTreeWalker { const declarations = this._evaluator.getDeclarationsForNameNode(node); - let primaryDeclaration = declarations && declarations.length > 0 ? - declarations[declarations.length - 1] : undefined; + let primaryDeclaration = + declarations && declarations.length > 0 ? declarations[declarations.length - 1] : undefined; if (!primaryDeclaration || primaryDeclaration.node === node) { return; } @@ -1136,17 +1216,17 @@ export class Checker extends ParseTreeWalker { let classOrModuleNode: ClassNode | ModuleNode | undefined; if (primaryDeclaration.node) { - classOrModuleNode = ParseTreeUtils.getEnclosingClassOrModule( - primaryDeclaration.node); + classOrModuleNode = ParseTreeUtils.getEnclosingClassOrModule(primaryDeclaration.node); } // If this is the name of a class, find the module or class that contains it rather // than constraining the use of the class name within the class itself. - if (primaryDeclaration.node && - primaryDeclaration.node.parent && - primaryDeclaration.node.parent === classOrModuleNode && - classOrModuleNode.nodeType === ParseNodeType.Class) { - + if ( + primaryDeclaration.node && + primaryDeclaration.node.parent && + primaryDeclaration.node.parent === classOrModuleNode && + classOrModuleNode.nodeType === ParseNodeType.Class + ) { classOrModuleNode = ParseTreeUtils.getEnclosingClassOrModule(classOrModuleNode); } @@ -1166,12 +1246,16 @@ export class Checker extends ParseTreeWalker { // If the referencing class is a subclass of the declaring class, it's // allowed to access a protected name. - if (enclosingClassTypeInfo && - enclosingClassTypeInfo.decoratedType.category === TypeCategory.Class) { - - if (derivesFromClassRecursive(enclosingClassTypeInfo.decoratedType, - declClassTypeInfo.decoratedType)) { - + if ( + enclosingClassTypeInfo && + enclosingClassTypeInfo.decoratedType.category === TypeCategory.Class + ) { + if ( + derivesFromClassRecursive( + enclosingClassTypeInfo.decoratedType, + declClassTypeInfo.decoratedType + ) + ) { return; } } @@ -1185,17 +1269,18 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportPrivateUsage, DiagnosticRule.reportPrivateUsage, - `'${ nameValue }' is protected and used outside of a derived class`, - node); + `'${nameValue}' is protected and used outside of a derived class`, + node + ); } else { - const scopeName = classOrModuleNode.nodeType === ParseNodeType.Class ? - 'class' : 'module'; + const scopeName = classOrModuleNode.nodeType === ParseNodeType.Class ? 'class' : 'module'; this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportPrivateUsage, DiagnosticRule.reportPrivateUsage, - `'${ nameValue }' is private and used outside of the ${ scopeName } in which it is declared`, - node); + `'${nameValue}' is private and used outside of the ${scopeName} in which it is declared`, + node + ); } } } @@ -1205,20 +1290,19 @@ export class Checker extends ParseTreeWalker { // strings, and "pass" statements or ellipses. private _validateTypedDictClassSuite(suiteNode: SuiteNode) { const emitBadStatementError = (node: ParseNode) => { - this._evaluator.addError( - `TypedDict classes can contain only type annotations`, - node); + this._evaluator.addError(`TypedDict classes can contain only type annotations`, node); }; suiteNode.statements.forEach(statement => { if (!AnalyzerNodeInfo.isCodeUnreachable(statement)) { if (statement.nodeType === ParseNodeType.StatementList) { for (const substatement of statement.statements) { - if (substatement.nodeType !== ParseNodeType.TypeAnnotation && - substatement.nodeType !== ParseNodeType.Ellipsis && - substatement.nodeType !== ParseNodeType.StringList && - substatement.nodeType !== ParseNodeType.Pass) { - + if ( + substatement.nodeType !== ParseNodeType.TypeAnnotation && + substatement.nodeType !== ParseNodeType.Ellipsis && + substatement.nodeType !== ParseNodeType.StringList && + substatement.nodeType !== ParseNodeType.Pass + ) { emitBadStatementError(substatement); } } @@ -1247,13 +1331,18 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownVariableType, DiagnosticRule.reportUnknownVariableType, - `Declared return type is unknown`, node.returnTypeAnnotation); + `Declared return type is unknown`, + node.returnTypeAnnotation + ); } else if (containsUnknown(declaredReturnType)) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownVariableType, DiagnosticRule.reportUnknownVariableType, - `Declared return type, '${ this._evaluator.printType(declaredReturnType) }', is partially unknown`, - node.returnTypeAnnotation); + `Declared return type, '${this._evaluator.printType( + declaredReturnType + )}', is partially unknown`, + node.returnTypeAnnotation + ); } } @@ -1273,7 +1362,8 @@ export class Checker extends ParseTreeWalker { if (!ParseTreeUtils.isSuiteEmpty(node.suite)) { this._evaluator.addError( `Function with declared type of 'NoReturn' cannot return 'None'`, - node.returnTypeAnnotation); + node.returnTypeAnnotation + ); } } else if (!FunctionType.isAbstractMethod(functionType)) { // Make sure that the function doesn't implicitly return None if the declared @@ -1287,9 +1377,11 @@ export class Checker extends ParseTreeWalker { // the return type matches. if (!ParseTreeUtils.isSuiteEmpty(node.suite)) { this._evaluator.addError( - `Function with declared type of '${ this._evaluator.printType(declaredReturnType) }'` + - ` must return value` + diagAddendum.getString(), - node.returnTypeAnnotation); + `Function with declared type of '${this._evaluator.printType(declaredReturnType)}'` + + ` must return value` + + diagAddendum.getString(), + node.returnTypeAnnotation + ); } } } @@ -1300,13 +1392,16 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownParameterType, DiagnosticRule.reportUnknownParameterType, - `Inferred return type is unknown`, node.name); + `Inferred return type is unknown`, + node.name + ); } else if (containsUnknown(inferredReturnType)) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportUnknownParameterType, DiagnosticRule.reportUnknownParameterType, - `Return type '${ this._evaluator.printType(inferredReturnType) }' is partially unknown`, - node.name); + `Return type '${this._evaluator.printType(inferredReturnType)}' is partially unknown`, + node.name + ); } } } @@ -1319,9 +1414,10 @@ export class Checker extends ParseTreeWalker { if (parentSymbol && isFinalVariable(parentSymbol.symbol)) { const decl = localSymbol.getDeclarations()[0]; this._evaluator.addError( - `'${ name }' cannot be redeclared because parent class ` + - `'${ parentSymbol.class.details.name }' declares it as Final`, - decl.node); + `'${name}' cannot be redeclared because parent class ` + + `'${parentSymbol.class.details.name}' declares it as Final`, + decl.node + ); } }); } @@ -1346,21 +1442,22 @@ export class Checker extends ParseTreeWalker { const baseClassAndSymbol = getSymbolFromBaseClasses(classType, name); if (baseClassAndSymbol) { const typeOfBaseClassMethod = this._evaluator.getEffectiveTypeOfSymbol( - baseClassAndSymbol.symbol); + baseClassAndSymbol.symbol + ); const diagAddendum = new DiagnosticAddendum(); - if (!this._evaluator.canOverrideMethod(typeOfBaseClassMethod, typeOfSymbol, - diagAddendum)) { - + if (!this._evaluator.canOverrideMethod(typeOfBaseClassMethod, typeOfSymbol, diagAddendum)) { const decl = getLastTypedDeclaredForSymbol(symbol); if (decl && decl.type === DeclarationType.Function) { const diag = this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportIncompatibleMethodOverride, DiagnosticRule.reportIncompatibleMethodOverride, - `Method '${ name }' overrides class '${ baseClassAndSymbol.class.details.name }' ` + - `in an incompatible manner` + diagAddendum.getString(), decl.node.name); + `Method '${name}' overrides class '${baseClassAndSymbol.class.details.name}' ` + + `in an incompatible manner` + + diagAddendum.getString(), + decl.node.name + ); - const origDecl = getLastTypedDeclaredForSymbol( - baseClassAndSymbol.symbol); + const origDecl = getLastTypedDeclaredForSymbol(baseClassAndSymbol.symbol); if (diag && origDecl) { diag.addRelatedInfo('Overridden method', origDecl.path, origDecl.range); } @@ -1372,12 +1469,12 @@ export class Checker extends ParseTreeWalker { const decl = getLastTypedDeclaredForSymbol(symbol); if (decl && decl.type === DeclarationType.Function) { const diag = this._evaluator.addError( - `Method '${ name }' cannot override final method defined ` + - `in class '${ baseClassAndSymbol.class.details.name }'`, - decl.node.name); + `Method '${name}' cannot override final method defined ` + + `in class '${baseClassAndSymbol.class.details.name}'`, + decl.node.name + ); - const origDecl = getLastTypedDeclaredForSymbol( - baseClassAndSymbol.symbol); + const origDecl = getLastTypedDeclaredForSymbol(baseClassAndSymbol.symbol); if (diag && origDecl) { diag.addRelatedInfo('Final method', origDecl.path, origDecl.range); } @@ -1395,24 +1492,27 @@ export class Checker extends ParseTreeWalker { private _validateMethod(node: FunctionNode, functionType: FunctionType, classNode: ClassNode) { if (node.name && node.name.value === '__new__') { // __new__ overrides should have a "cls" parameter. - if (node.parameters.length === 0 || !node.parameters[0].name || - (node.parameters[0].name.value !== 'cls' && - node.parameters[0].name.value !== 'mcs')) { + if ( + node.parameters.length === 0 || + !node.parameters[0].name || + (node.parameters[0].name.value !== 'cls' && node.parameters[0].name.value !== 'mcs') + ) { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportSelfClsParameterName, DiagnosticRule.reportSelfClsParameterName, `The __new__ override should take a 'cls' parameter`, - node.parameters.length > 0 ? node.parameters[0] : node.name); + node.parameters.length > 0 ? node.parameters[0] : node.name + ); } } else if (node.name && node.name.value === '__init_subclass__') { // __init_subclass__ overrides should have a "cls" parameter. - if (node.parameters.length === 0 || !node.parameters[0].name || - node.parameters[0].name.value !== 'cls') { + if (node.parameters.length === 0 || !node.parameters[0].name || node.parameters[0].name.value !== 'cls') { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportSelfClsParameterName, DiagnosticRule.reportSelfClsParameterName, `The __init_subclass__ override should take a 'cls' parameter`, - node.parameters.length > 0 ? node.parameters[0] : node.name); + node.parameters.length > 0 ? node.parameters[0] : node.name + ); } } else if (FunctionType.isStaticMethod(functionType)) { // Static methods should not have "self" or "cls" parameters. @@ -1423,7 +1523,8 @@ export class Checker extends ParseTreeWalker { this._fileInfo.diagnosticSettings.reportSelfClsParameterName, DiagnosticRule.reportSelfClsParameterName, `Static methods should not take a 'self' or 'cls' parameter`, - node.parameters[0].name); + node.parameters[0].name + ); } } } else if (FunctionType.isClassMethod(functionType)) { @@ -1440,7 +1541,8 @@ export class Checker extends ParseTreeWalker { this._fileInfo.diagnosticSettings.reportSelfClsParameterName, DiagnosticRule.reportSelfClsParameterName, `Class methods should take a 'cls' parameter`, - node.parameters.length > 0 ? node.parameters[0] : node.name); + node.parameters.length > 0 ? node.parameters[0] : node.name + ); } } } else { @@ -1466,9 +1568,12 @@ export class Checker extends ParseTreeWalker { if (paramName === 'cls') { const classTypeInfo = this._evaluator.getTypeOfClass(classNode); const typeType = this._evaluator.getBuiltInType(classNode, 'type'); - if (typeType && typeType.category === TypeCategory.Class && - classTypeInfo && classTypeInfo.classType.category === TypeCategory.Class) { - + if ( + typeType && + typeType.category === TypeCategory.Class && + classTypeInfo && + classTypeInfo.classType.category === TypeCategory.Class + ) { if (derivesFromClassRecursive(classTypeInfo.classType, typeType)) { isLegalMetaclassName = true; } @@ -1480,7 +1585,8 @@ export class Checker extends ParseTreeWalker { this._fileInfo.diagnosticSettings.reportSelfClsParameterName, DiagnosticRule.reportSelfClsParameterName, `Instance methods should take a 'self' parameter`, - node.parameters.length > 0 ? node.parameters[0] : node.name); + node.parameters.length > 0 ? node.parameters[0] : node.name + ); } } } @@ -1505,14 +1611,17 @@ export class Checker extends ParseTreeWalker { if (isNoReturnType(declaredYieldType)) { this._evaluator.addError( `Function with declared return type 'NoReturn' cannot include a yield statement`, - node); + node + ); } else { const diagAddendum = new DiagnosticAddendum(); if (!this._evaluator.canAssignType(declaredYieldType, adjustedYieldType, diagAddendum)) { this._evaluator.addError( - `Expression of type '${ this._evaluator.printType(adjustedYieldType) }' cannot be assigned ` + - `to yield type '${ this._evaluator.printType(declaredYieldType) }'` + diagAddendum.getString(), - node.expression || node); + `Expression of type '${this._evaluator.printType(adjustedYieldType)}' cannot be assigned ` + + `to yield type '${this._evaluator.printType(declaredYieldType)}'` + + diagAddendum.getString(), + node.expression || node + ); } } } @@ -1536,8 +1645,9 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportDuplicateImport, DiagnosticRule.reportDuplicateImport, - `'${ importFromAs.name.value }' is imported more than once`, - importFromAs.name); + `'${importFromAs.name.value}' is imported more than once`, + importFromAs.name + ); } else { symbolMap.set(importFromAs.name.value, importFromAs); } @@ -1551,8 +1661,9 @@ export class Checker extends ParseTreeWalker { this._evaluator.addDiagnostic( this._fileInfo.diagnosticSettings.reportDuplicateImport, DiagnosticRule.reportDuplicateImport, - `'${ importStatement.moduleName }' is imported more than once`, - importStatement.subnode); + `'${importStatement.moduleName}' is imported more than once`, + importStatement.subnode + ); } else { importModuleMap.set(importStatement.moduleName, importStatement.subnode); } diff --git a/server/src/analyzer/circularDependency.ts b/server/src/analyzer/circularDependency.ts index ee272367e..0655c24da 100644 --- a/server/src/analyzer/circularDependency.ts +++ b/server/src/analyzer/circularDependency.ts @@ -1,14 +1,14 @@ /* -* circularDependency.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* A list of file paths that are part of a circular dependency -* chain (i.e. a chain of imports). Since these are circular, there -* no defined "start", but this module helps normalize the start -* by picking the alphabetically-first module in the cycle. -*/ + * circularDependency.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * A list of file paths that are part of a circular dependency + * chain (i.e. a chain of imports). Since these are circular, there + * no defined "start", but this module helps normalize the start + * by picking the alphabetically-first module in the cycle. + */ export class CircularDependency { private _paths: string[] = []; @@ -32,8 +32,7 @@ export class CircularDependency { }); if (firstIndex !== 0) { - this._paths = this._paths.slice(firstIndex).concat( - this._paths.slice(0, firstIndex)); + this._paths = this._paths.slice(firstIndex).concat(this._paths.slice(0, firstIndex)); } } diff --git a/server/src/analyzer/codeFlow.ts b/server/src/analyzer/codeFlow.ts index 6dbdd5ab4..02226353e 100644 --- a/server/src/analyzer/codeFlow.ts +++ b/server/src/analyzer/codeFlow.ts @@ -1,36 +1,42 @@ /* -* codeFlow.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Data structures that track the code flow (or more accurately, -* the inverse of code flow) starting with return statements and -* working back to the entry. This allows us to work out the -* types at each point of the code flow. -* -* This is largely based on the code flow engine in the -* TypeScript compiler. -*/ + * codeFlow.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Data structures that track the code flow (or more accurately, + * the inverse of code flow) starting with return statements and + * working back to the entry. This allows us to work out the + * types at each point of the code flow. + * + * This is largely based on the code flow engine in the + * TypeScript compiler. + */ import { assert } from '../common/debug'; -import { CallNode, ExpressionNode, ImportFromNode, MemberAccessNode, NameNode, - ParseNodeType } from '../parser/parseNodes'; +import { + CallNode, + ExpressionNode, + ImportFromNode, + MemberAccessNode, + NameNode, + ParseNodeType +} from '../parser/parseNodes'; export enum FlowFlags { - Unreachable = 1 << 0, // Unreachable code - Start = 1 << 1, // Entry point - BranchLabel = 1 << 2, // Junction for forward control flow - LoopLabel = 1 << 3, // Junction for backward control flow - Assignment = 1 << 4, // Assignment statement - Unbind = 1 << 5, // Used with assignment to indicate target should be unbound - WildcardImport = 1 << 6, // For "from X import *" statements - TrueCondition = 1 << 7, // Condition known to be true - FalseCondition = 1 << 9, // Condition known to be false - Call = 1 << 10, // Call node - PreFinallyGate = 1 << 11, // Injected edge that links pre-finally label and pre-try flow - PostFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph - AssignmentAlias = 1 << 13 // Assigned symbol is aliased to another symbol with the same name + Unreachable = 1 << 0, // Unreachable code + Start = 1 << 1, // Entry point + BranchLabel = 1 << 2, // Junction for forward control flow + LoopLabel = 1 << 3, // Junction for backward control flow + Assignment = 1 << 4, // Assignment statement + Unbind = 1 << 5, // Used with assignment to indicate target should be unbound + WildcardImport = 1 << 6, // For "from X import *" statements + TrueCondition = 1 << 7, // Condition known to be true + FalseCondition = 1 << 9, // Condition known to be false + Call = 1 << 10, // Call node + PreFinallyGate = 1 << 11, // Injected edge that links pre-finally label and pre-try flow + PostFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph + AssignmentAlias = 1 << 13 // Assigned symbol is aliased to another symbol with the same name } let _nextFlowNodeId = 1; @@ -123,11 +129,11 @@ export function createKeyForReference(reference: NameNode | MemberAccessNode): s key = reference.memberName.value; let leftNode = reference.leftExpression; while (leftNode.nodeType === ParseNodeType.MemberAccess) { - key = leftNode.memberName.value + `.${ key }`; + key = leftNode.memberName.value + `.${key}`; leftNode = leftNode.leftExpression; } assert(leftNode.nodeType === ParseNodeType.Name); - key = (leftNode as NameNode).value + `.${ key }`; + key = (leftNode as NameNode).value + `.${key}`; } return key; diff --git a/server/src/analyzer/commentUtils.ts b/server/src/analyzer/commentUtils.ts index 79b4d6cc1..ab5b68f57 100644 --- a/server/src/analyzer/commentUtils.ts +++ b/server/src/analyzer/commentUtils.ts @@ -1,21 +1,29 @@ /* -* commentUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Utility functions that parse comments and extract commands -* or other directives from them. -*/ + * commentUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Utility functions that parse comments and extract commands + * or other directives from them. + */ -import { cloneDiagnosticSettings, DiagnosticLevel, DiagnosticSettings, - getBooleanDiagnosticSettings, getDiagLevelSettings, getStrictDiagnosticSettings } from '../common/configOptions'; +import { + cloneDiagnosticSettings, + DiagnosticLevel, + DiagnosticSettings, + getBooleanDiagnosticSettings, + getDiagLevelSettings, + getStrictDiagnosticSettings +} from '../common/configOptions'; import { TextRangeCollection } from '../common/textRangeCollection'; import { Token } from '../parser/tokenizerTypes'; -export function getFileLevelDirectives(tokens: TextRangeCollection, - defaultSettings: DiagnosticSettings, useStrict: boolean): DiagnosticSettings { - +export function getFileLevelDirectives( + tokens: TextRangeCollection, + defaultSettings: DiagnosticSettings, + useStrict: boolean +): DiagnosticSettings { let settings = cloneDiagnosticSettings(defaultSettings); if (useStrict) { diff --git a/server/src/analyzer/declaration.ts b/server/src/analyzer/declaration.ts index e8bd20f0b..c10876a32 100644 --- a/server/src/analyzer/declaration.ts +++ b/server/src/analyzer/declaration.ts @@ -1,19 +1,32 @@ /* -* declaration.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Tracks the location within the code where a named entity -* is declared and its associated declared type (if the type -* is explicitly declared). -*/ + * declaration.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Tracks the location within the code where a named entity + * is declared and its associated declared type (if the type + * is explicitly declared). + */ import { Range } from '../common/textRange'; -import { ClassNode, ExpressionNode, FunctionNode, ImportAsNode, - ImportFromAsNode, ImportFromNode, ModuleNode, NameNode, ParameterNode, - ParseNode, ReturnNode, StringListNode, TypeAnnotationNode, YieldFromNode, - YieldNode } from '../parser/parseNodes'; +import { + ClassNode, + ExpressionNode, + FunctionNode, + ImportAsNode, + ImportFromAsNode, + ImportFromNode, + ModuleNode, + NameNode, + ParameterNode, + ParseNode, + ReturnNode, + StringListNode, + TypeAnnotationNode, + YieldFromNode, + YieldNode +} from '../parser/parseNodes'; export const enum DeclarationType { Intrinsic, @@ -132,6 +145,11 @@ export interface ModuleLoaderActions { implicitImports?: Map; } -export type Declaration = IntrinsicDeclaration | ClassDeclaration | - SpecialBuiltInClassDeclaration | FunctionDeclaration | ParameterDeclaration | - VariableDeclaration | AliasDeclaration; +export type Declaration = + | IntrinsicDeclaration + | ClassDeclaration + | SpecialBuiltInClassDeclaration + | FunctionDeclaration + | ParameterDeclaration + | VariableDeclaration + | AliasDeclaration; diff --git a/server/src/analyzer/declarationUtils.ts b/server/src/analyzer/declarationUtils.ts index ba93b704c..ede5e013f 100644 --- a/server/src/analyzer/declarationUtils.ts +++ b/server/src/analyzer/declarationUtils.ts @@ -1,11 +1,11 @@ /* -* declarationUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Collection of static methods that operate on declarations. -*/ + * declarationUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Collection of static methods that operate on declarations. + */ import { Declaration, DeclarationType } from './declaration'; @@ -37,8 +37,10 @@ export function areDeclarationsSame(decl1: Declaration, decl2: Declaration): boo return false; } - if (decl1.range.start.line !== decl2.range.start.line || - decl1.range.start.character !== decl2.range.start.character) { + if ( + decl1.range.start.line !== decl2.range.start.line || + decl1.range.start.character !== decl2.range.start.character + ) { return false; } @@ -47,4 +49,4 @@ export function areDeclarationsSame(decl1: Declaration, decl2: Declaration): boo export function isFinalVariableDeclaration(decl: Declaration) { return decl.type === DeclarationType.Variable && !!decl.isFinal; -} \ No newline at end of file +} diff --git a/server/src/analyzer/docStringUtils.ts b/server/src/analyzer/docStringUtils.ts index 5b1de73fe..4fa1df199 100644 --- a/server/src/analyzer/docStringUtils.ts +++ b/server/src/analyzer/docStringUtils.ts @@ -1,13 +1,13 @@ /* -* docStringUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Static methods that format and parse doc strings based on -* the rules specified in PEP 257 -* (https://www.python.org/dev/peps/pep-0257/). -*/ + * docStringUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Static methods that format and parse doc strings based on + * the rules specified in PEP 257 + * (https://www.python.org/dev/peps/pep-0257/). + */ export function decodeDocString(rawString: string): string { // Remove carriage returns and replace tabs. diff --git a/server/src/analyzer/importResolver.ts b/server/src/analyzer/importResolver.ts index 19e33064e..dd9453db6 100644 --- a/server/src/analyzer/importResolver.ts +++ b/server/src/analyzer/importResolver.ts @@ -1,18 +1,25 @@ /* -* importResolver.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Provides the logic for resolving imports according to the -* runtime rules of Python. -*/ + * importResolver.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Provides the logic for resolving imports according to the + * runtime rules of Python. + */ import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions'; import { - combinePaths, ensureTrailingDirectorySeparator, getDirectoryPath, - getFileExtension, getFileSystemEntries, getPathComponents, isDirectory, - isFile, stripFileExtension, stripTrailingDirectorySeparator + combinePaths, + ensureTrailingDirectorySeparator, + getDirectoryPath, + getFileExtension, + getFileSystemEntries, + getPathComponents, + isDirectory, + isFile, + stripFileExtension, + stripTrailingDirectorySeparator } from '../common/pathUtils'; import { versionToString } from '../common/pythonVersion'; import * as StringUtils from '../common/stringUtils'; @@ -56,16 +63,22 @@ export class ImportResolver { // Resolves the import and returns the path if it exists, otherwise // returns undefined. - resolveImport(sourceFilePath: string, execEnv: ExecutionEnvironment, - moduleDescriptor: ImportedModuleDescriptor): ImportResult { - + resolveImport( + sourceFilePath: string, + execEnv: ExecutionEnvironment, + moduleDescriptor: ImportedModuleDescriptor + ): ImportResult { const importName = this._formatImportName(moduleDescriptor); const importFailureInfo: string[] = []; // Is it a relative import? if (moduleDescriptor.leadingDots > 0) { - const relativeImport = this._resolveRelativeImport(sourceFilePath, - moduleDescriptor, importName, importFailureInfo); + const relativeImport = this._resolveRelativeImport( + sourceFilePath, + moduleDescriptor, + importName, + importFailureInfo + ); if (relativeImport) { relativeImport.isRelative = true; @@ -73,73 +86,91 @@ export class ImportResolver { } } else { // Is it already cached? - const cachedResults = this._lookUpResultsInCache(execEnv, importName, - moduleDescriptor.importedSymbols); + const cachedResults = this._lookUpResultsInCache(execEnv, importName, moduleDescriptor.importedSymbols); if (cachedResults) { return cachedResults; } // First check for a typeshed file. if (moduleDescriptor.nameParts.length > 0) { - const builtInImport = this._findTypeshedPath(execEnv, moduleDescriptor, - importName, true, importFailureInfo); + const builtInImport = this._findTypeshedPath( + execEnv, + moduleDescriptor, + importName, + true, + importFailureInfo + ); if (builtInImport) { builtInImport.isTypeshedFile = true; - return this._addResultsToCache(execEnv, importName, builtInImport, - moduleDescriptor.importedSymbols); + return this._addResultsToCache( + execEnv, + importName, + builtInImport, + moduleDescriptor.importedSymbols + ); } } let bestResultSoFar: ImportResult | undefined; // Look for it in the root directory of the execution environment. - importFailureInfo.push(`Looking in root directory of execution environment ` + - `'${ execEnv.root }'`); - let localImport = this.resolveAbsoluteImport( - execEnv.root, moduleDescriptor, importName, importFailureInfo); + importFailureInfo.push(`Looking in root directory of execution environment ` + `'${execEnv.root}'`); + let localImport = this.resolveAbsoluteImport(execEnv.root, moduleDescriptor, importName, importFailureInfo); if (localImport && localImport.isImportFound) { - return this._addResultsToCache(execEnv, importName, localImport, - moduleDescriptor.importedSymbols); + return this._addResultsToCache(execEnv, importName, localImport, moduleDescriptor.importedSymbols); } bestResultSoFar = localImport; for (const extraPath of execEnv.extraPaths) { - importFailureInfo.push(`Looking in extraPath '${ extraPath }'`); - localImport = this.resolveAbsoluteImport(extraPath, moduleDescriptor, - importName, importFailureInfo); + importFailureInfo.push(`Looking in extraPath '${extraPath}'`); + localImport = this.resolveAbsoluteImport(extraPath, moduleDescriptor, importName, importFailureInfo); if (localImport && localImport.isImportFound) { - return this._addResultsToCache(execEnv, importName, localImport, - moduleDescriptor.importedSymbols); + return this._addResultsToCache(execEnv, importName, localImport, moduleDescriptor.importedSymbols); } - if (localImport && (bestResultSoFar === undefined || - localImport.resolvedPaths.length > bestResultSoFar.resolvedPaths.length)) { + if ( + localImport && + (bestResultSoFar === undefined || + localImport.resolvedPaths.length > bestResultSoFar.resolvedPaths.length) + ) { bestResultSoFar = localImport; } } // Check for a typings file. if (this._configOptions.typingsPath) { - importFailureInfo.push(`Looking in typingsPath '${ this._configOptions.typingsPath }'`); + importFailureInfo.push(`Looking in typingsPath '${this._configOptions.typingsPath}'`); const typingsImport = this.resolveAbsoluteImport( - this._configOptions.typingsPath, moduleDescriptor, importName, importFailureInfo); + this._configOptions.typingsPath, + moduleDescriptor, + importName, + importFailureInfo + ); if (typingsImport && typingsImport.isImportFound) { // We will treat typings files as "local" rather than "third party". typingsImport.importType = ImportType.Local; typingsImport.isLocalTypingsFile = true; - return this._addResultsToCache(execEnv, importName, typingsImport, - moduleDescriptor.importedSymbols); + return this._addResultsToCache( + execEnv, + importName, + typingsImport, + moduleDescriptor.importedSymbols + ); } } // Check for a typeshed file. importFailureInfo.push(`Looking for typeshed path`); - const typeshedImport = this._findTypeshedPath(execEnv, moduleDescriptor, - importName, false, importFailureInfo); + const typeshedImport = this._findTypeshedPath( + execEnv, + moduleDescriptor, + importName, + false, + importFailureInfo + ); if (typeshedImport) { typeshedImport.isTypeshedFile = true; - return this._addResultsToCache(execEnv, importName, typeshedImport, - moduleDescriptor.importedSymbols); + return this._addResultsToCache(execEnv, importName, typeshedImport, moduleDescriptor.importedSymbols); } // Look for the import in the list of third-party packages. @@ -148,23 +179,35 @@ export class ImportResolver { for (const searchPath of pythonSearchPaths) { // Allow partial resolution because some third-party packages // use tricks to populate their package namespaces. - importFailureInfo.push(`Looking in python search path '${ searchPath }'`); + importFailureInfo.push(`Looking in python search path '${searchPath}'`); const thirdPartyImport = this.resolveAbsoluteImport( - searchPath, moduleDescriptor, importName, importFailureInfo, - true, true, true); + searchPath, + moduleDescriptor, + importName, + importFailureInfo, + true, + true, + true + ); if (thirdPartyImport) { thirdPartyImport.importType = ImportType.ThirdParty; if (thirdPartyImport.isImportFound && thirdPartyImport.isStubFile) { - return this._addResultsToCache(execEnv, importName, - thirdPartyImport, moduleDescriptor.importedSymbols); + return this._addResultsToCache( + execEnv, + importName, + thirdPartyImport, + moduleDescriptor.importedSymbols + ); } // We did not find it, or we did and it's not from a // stub, so give chance for resolveImportEx to find // one from a stub. - if (bestResultSoFar === undefined || - thirdPartyImport.resolvedPaths.length > bestResultSoFar.resolvedPaths.length) { + if ( + bestResultSoFar === undefined || + thirdPartyImport.resolvedPaths.length > bestResultSoFar.resolvedPaths.length + ) { bestResultSoFar = thirdPartyImport; } } @@ -173,17 +216,21 @@ export class ImportResolver { importFailureInfo.push('No python interpreter search path'); } - const extraResults = this.resolveImportEx(sourceFilePath, execEnv, moduleDescriptor, importName, importFailureInfo); + const extraResults = this.resolveImportEx( + sourceFilePath, + execEnv, + moduleDescriptor, + importName, + importFailureInfo + ); if (extraResults !== undefined) { - return this._addResultsToCache(execEnv, importName, extraResults, - moduleDescriptor.importedSymbols); + return this._addResultsToCache(execEnv, importName, extraResults, moduleDescriptor.importedSymbols); } // We weren't able to find an exact match, so return the best // partial match. if (bestResultSoFar) { - return this._addResultsToCache(execEnv, importName, bestResultSoFar, - moduleDescriptor.importedSymbols); + return this._addResultsToCache(execEnv, importName, bestResultSoFar, moduleDescriptor.importedSymbols); } } @@ -205,53 +252,64 @@ export class ImportResolver { // Intended to be overridden by subclasses to provide additional stub // resolving capabilities. Return undefined if no stubs were found for // this import. - protected resolveImportEx(sourceFilePath: string, execEnv: ExecutionEnvironment, - moduleDescriptor: ImportedModuleDescriptor, importName: string, - importFailureInfo: string[] = []): ImportResult | undefined { + protected resolveImportEx( + sourceFilePath: string, + execEnv: ExecutionEnvironment, + moduleDescriptor: ImportedModuleDescriptor, + importName: string, + importFailureInfo: string[] = [] + ): ImportResult | undefined { return undefined; } - getCompletionSuggestions(sourceFilePath: string, execEnv: ExecutionEnvironment, - moduleDescriptor: ImportedModuleDescriptor, similarityLimit: number): string[] { - + getCompletionSuggestions( + sourceFilePath: string, + execEnv: ExecutionEnvironment, + moduleDescriptor: ImportedModuleDescriptor, + similarityLimit: number + ): string[] { const importFailureInfo: string[] = []; const suggestions: string[] = []; // Is it a relative import? if (moduleDescriptor.leadingDots > 0) { - this._getCompletionSuggestionsRelative(sourceFilePath, - moduleDescriptor, suggestions, similarityLimit); + this._getCompletionSuggestionsRelative(sourceFilePath, moduleDescriptor, suggestions, similarityLimit); } else { // First check for a typeshed file. if (moduleDescriptor.nameParts.length > 0) { - this._getCompletionSuggestionsTypeshedPath(execEnv, moduleDescriptor, - true, suggestions, similarityLimit); + this._getCompletionSuggestionsTypeshedPath( + execEnv, + moduleDescriptor, + true, + suggestions, + similarityLimit + ); } // Look for it in the root directory of the execution environment. - this._getCompletionSuggestionsAbsolute(execEnv.root, - moduleDescriptor, suggestions, similarityLimit); + this._getCompletionSuggestionsAbsolute(execEnv.root, moduleDescriptor, suggestions, similarityLimit); for (const extraPath of execEnv.extraPaths) { - this._getCompletionSuggestionsAbsolute(extraPath, moduleDescriptor, - suggestions, similarityLimit); + this._getCompletionSuggestionsAbsolute(extraPath, moduleDescriptor, suggestions, similarityLimit); } // Check for a typings file. if (this._configOptions.typingsPath) { - this._getCompletionSuggestionsAbsolute(this._configOptions.typingsPath, - moduleDescriptor, suggestions, similarityLimit); + this._getCompletionSuggestionsAbsolute( + this._configOptions.typingsPath, + moduleDescriptor, + suggestions, + similarityLimit + ); } // Check for a typeshed file. - this._getCompletionSuggestionsTypeshedPath(execEnv, moduleDescriptor, - false, suggestions, similarityLimit); + this._getCompletionSuggestionsTypeshedPath(execEnv, moduleDescriptor, false, suggestions, similarityLimit); // Look for the import in the list of third-party packages. const pythonSearchPaths = this._getPythonSearchPaths(execEnv, importFailureInfo); for (const searchPath of pythonSearchPaths) { - this._getCompletionSuggestionsAbsolute(searchPath, - moduleDescriptor, suggestions, similarityLimit); + this._getCompletionSuggestionsAbsolute(searchPath, moduleDescriptor, suggestions, similarityLimit); } } @@ -292,8 +350,7 @@ export class ImportResolver { // Check for a typings file. if (this._configOptions.typingsPath) { - const candidateModuleName = this._getModuleNameFromPath( - this._configOptions.typingsPath, filePath); + const candidateModuleName = this._getModuleNameFromPath(this._configOptions.typingsPath, filePath); // Does this candidate look better than the previous best module name? // We'll always try to use the shortest version. @@ -337,9 +394,11 @@ export class ImportResolver { return { moduleName: '', importType: ImportType.Local }; } - private _lookUpResultsInCache(execEnv: ExecutionEnvironment, importName: string, - importedSymbols: string[] | undefined) { - + private _lookUpResultsInCache( + execEnv: ExecutionEnvironment, + importName: string, + importedSymbols: string[] | undefined + ) { const cacheForExecEnv = this._cachedImportResults.get(execEnv.root); if (!cacheForExecEnv) { return undefined; @@ -353,9 +412,12 @@ export class ImportResolver { return this._filterImplicitImports(cachedEntry, importedSymbols); } - private _addResultsToCache(execEnv: ExecutionEnvironment, importName: string, - importResult: ImportResult, importedSymbols: string[] | undefined) { - + private _addResultsToCache( + execEnv: ExecutionEnvironment, + importName: string, + importResult: ImportResult, + importedSymbols: string[] | undefined + ) { let cacheForExecEnv = this._cachedImportResults.get(execEnv.root); if (!cacheForExecEnv) { cacheForExecEnv = new Map(); @@ -367,9 +429,11 @@ export class ImportResolver { return this._filterImplicitImports(importResult, importedSymbols); } - private _getModuleNameFromPath(containerPath: string, filePath: string, - stripTopContainerDir = false): string | undefined { - + private _getModuleNameFromPath( + containerPath: string, + filePath: string, + stripTopContainerDir = false + ): string | undefined { containerPath = ensureTrailingDirectorySeparator(containerPath); let filePathWithoutExtension = stripFileExtension(filePath); @@ -399,24 +463,33 @@ export class ImportResolver { return parts.join('.'); } - private _getPythonSearchPaths(execEnv: ExecutionEnvironment, - importFailureInfo: string[]) { - + private _getPythonSearchPaths(execEnv: ExecutionEnvironment, importFailureInfo: string[]) { const cacheKey = execEnv.venv ? execEnv.venv : ''; // Find the site packages for the configured virtual environment. if (!this._cachedPythonSearchPaths.has(cacheKey)) { - this._cachedPythonSearchPaths.set(cacheKey, PythonPathUtils.findPythonSearchPaths( - this.fileSystem, this._configOptions, execEnv.venv, importFailureInfo) || []); + this._cachedPythonSearchPaths.set( + cacheKey, + PythonPathUtils.findPythonSearchPaths( + this.fileSystem, + this._configOptions, + execEnv.venv, + importFailureInfo + ) || [] + ); } return this._cachedPythonSearchPaths.get(cacheKey)!; } - private _findTypeshedPath(execEnv: ExecutionEnvironment, moduleDescriptor: ImportedModuleDescriptor, - importName: string, isStdLib: boolean, importFailureInfo: string[]): ImportResult | undefined { - - importFailureInfo.push(`Looking for typeshed ${ isStdLib ? 'stdlib' : 'third_party' } path`); + private _findTypeshedPath( + execEnv: ExecutionEnvironment, + moduleDescriptor: ImportedModuleDescriptor, + importName: string, + isStdLib: boolean, + importFailureInfo: string[] + ): ImportResult | undefined { + importFailureInfo.push(`Looking for typeshed ${isStdLib ? 'stdlib' : 'third_party'} path`); const typeshedPath = this._getTypeshedPath(isStdLib, execEnv, importFailureInfo); if (!typeshedPath) { @@ -424,16 +497,20 @@ export class ImportResolver { } const pythonVersion = execEnv.pythonVersion; - let minorVersion = pythonVersion & 0xFF; + let minorVersion = pythonVersion & 0xff; // Search for module starting at "3.x" down to "3.1", then "3", then "2and3". while (true) { - const pythonVersionString = minorVersion > 0 ? versionToString(0x300 + minorVersion) : - minorVersion === 0 ? '3' : '2and3'; + const pythonVersionString = + minorVersion > 0 ? versionToString(0x300 + minorVersion) : minorVersion === 0 ? '3' : '2and3'; const testPath = combinePaths(typeshedPath, pythonVersionString); if (this.fileSystem.existsSync(testPath)) { - const importInfo = this.resolveAbsoluteImport(testPath, moduleDescriptor, - importName, importFailureInfo); + const importInfo = this.resolveAbsoluteImport( + testPath, + moduleDescriptor, + importName, + importFailureInfo + ); if (importInfo && importInfo.isImportFound) { importInfo.importType = isStdLib ? ImportType.BuiltIn : ImportType.ThirdParty; return importInfo; @@ -451,10 +528,13 @@ export class ImportResolver { return undefined; } - private _getCompletionSuggestionsTypeshedPath(execEnv: ExecutionEnvironment, - moduleDescriptor: ImportedModuleDescriptor, isStdLib: boolean, - suggestions: string[], similarityLimit: number) { - + private _getCompletionSuggestionsTypeshedPath( + execEnv: ExecutionEnvironment, + moduleDescriptor: ImportedModuleDescriptor, + isStdLib: boolean, + suggestions: string[], + similarityLimit: number + ) { const importFailureInfo: string[] = []; const typeshedPath = this._getTypeshedPath(isStdLib, execEnv, importFailureInfo); if (!typeshedPath) { @@ -462,16 +542,15 @@ export class ImportResolver { } const pythonVersion = execEnv.pythonVersion; - let minorVersion = pythonVersion & 0xFF; + let minorVersion = pythonVersion & 0xff; // Search for module starting at "3.x" down to "3.1", then "3", then "2and3". while (true) { - const pythonVersionString = minorVersion > 0 ? versionToString(0x300 + minorVersion) : - minorVersion === 0 ? '3' : '2and3'; + const pythonVersionString = + minorVersion > 0 ? versionToString(0x300 + minorVersion) : minorVersion === 0 ? '3' : '2and3'; const testPath = combinePaths(typeshedPath, pythonVersionString); if (this.fileSystem.existsSync(testPath)) { - this._getCompletionSuggestionsAbsolute(testPath, moduleDescriptor, - suggestions, similarityLimit); + this._getCompletionSuggestionsAbsolute(testPath, moduleDescriptor, suggestions, similarityLimit); } // We use -1 to indicate "2and3", which is searched after "3.0". @@ -482,9 +561,7 @@ export class ImportResolver { } } - private _getTypeshedPath(isStdLib: boolean, execEnv: ExecutionEnvironment, - importFailureInfo: string[]) { - + private _getTypeshedPath(isStdLib: boolean, execEnv: ExecutionEnvironment, importFailureInfo: string[]) { // See if we have it cached. if (isStdLib) { if (this._cachedTypeshedStdLibPath !== undefined) { @@ -502,14 +579,20 @@ export class ImportResolver { // python search paths, then in the typeshed-fallback directory. if (this._configOptions.typeshedPath) { const possibleTypeshedPath = this._configOptions.typeshedPath; - if (this.fileSystem.existsSync(possibleTypeshedPath) && isDirectory(this.fileSystem, possibleTypeshedPath)) { + if ( + this.fileSystem.existsSync(possibleTypeshedPath) && + isDirectory(this.fileSystem, possibleTypeshedPath) + ) { typeshedPath = possibleTypeshedPath; } } else { const pythonSearchPaths = this._getPythonSearchPaths(execEnv, importFailureInfo); for (const searchPath of pythonSearchPaths) { const possibleTypeshedPath = combinePaths(searchPath, 'typeshed'); - if (this.fileSystem.existsSync(possibleTypeshedPath) && isDirectory(this.fileSystem, possibleTypeshedPath)) { + if ( + this.fileSystem.existsSync(possibleTypeshedPath) && + isDirectory(this.fileSystem, possibleTypeshedPath) + ) { typeshedPath = possibleTypeshedPath; break; } @@ -537,25 +620,26 @@ export class ImportResolver { return typeshedPath; } - private _resolveRelativeImport(sourceFilePath: string, - moduleDescriptor: ImportedModuleDescriptor, importName: string, - importFailureInfo: string[]): ImportResult | undefined { - + private _resolveRelativeImport( + sourceFilePath: string, + moduleDescriptor: ImportedModuleDescriptor, + importName: string, + importFailureInfo: string[] + ): ImportResult | undefined { importFailureInfo.push('Attempting to resolve relative import'); // Determine which search path this file is part of. let curDir = getDirectoryPath(sourceFilePath); for (let i = 1; i < moduleDescriptor.leadingDots; i++) { if (curDir === '') { - importFailureInfo.push(`Invalid relative path '${ importName }'`); + importFailureInfo.push(`Invalid relative path '${importName}'`); return undefined; } curDir = getDirectoryPath(curDir); } // Now try to match the module parts from the current directory location. - const absImport = this.resolveAbsoluteImport(curDir, moduleDescriptor, - importName, importFailureInfo); + const absImport = this.resolveAbsoluteImport(curDir, moduleDescriptor, importName, importFailureInfo); if (!absImport) { return undefined; } @@ -563,10 +647,12 @@ export class ImportResolver { return this._filterImplicitImports(absImport, moduleDescriptor.importedSymbols); } - private _getCompletionSuggestionsRelative(sourceFilePath: string, - moduleDescriptor: ImportedModuleDescriptor, suggestions: string[], - similarityLimit: number) { - + private _getCompletionSuggestionsRelative( + sourceFilePath: string, + moduleDescriptor: ImportedModuleDescriptor, + suggestions: string[], + similarityLimit: number + ) { // Determine which search path this file is part of. let curDir = getDirectoryPath(sourceFilePath); for (let i = 1; i < moduleDescriptor.leadingDots; i++) { @@ -577,17 +663,21 @@ export class ImportResolver { } // Now try to match the module parts from the current directory location. - this._getCompletionSuggestionsAbsolute(curDir, moduleDescriptor, - suggestions, similarityLimit); + this._getCompletionSuggestionsAbsolute(curDir, moduleDescriptor, suggestions, similarityLimit); } // Follows import resolution algorithm defined in PEP-420: // https://www.python.org/dev/peps/pep-0420/ - protected resolveAbsoluteImport(rootPath: string, moduleDescriptor: ImportedModuleDescriptor, - importName: string, importFailureInfo: string[], allowPartial = false, - allowPydFile = false, allowStubsFolder = false): ImportResult | undefined { - - importFailureInfo.push(`Attempting to resolve using root path '${ rootPath }'`); + protected resolveAbsoluteImport( + rootPath: string, + moduleDescriptor: ImportedModuleDescriptor, + importName: string, + importFailureInfo: string[], + allowPartial = false, + allowPydFile = false, + allowStubsFolder = false + ): ImportResult | undefined { + importFailureInfo.push(`Attempting to resolve using root path '${rootPath}'`); // Starting at the specified path, walk the file system to find the // specified module. @@ -605,18 +695,22 @@ export class ImportResolver { const pydFilePath = pyFilePath + 'd'; if (this.fileSystem.existsSync(pyiFilePath) && isFile(this.fileSystem, pyiFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pyiFilePath }'`); + importFailureInfo.push(`Resolved import with file '${pyiFilePath}'`); resolvedPaths.push(pyiFilePath); isStubFile = true; } else if (this.fileSystem.existsSync(pyFilePath) && isFile(this.fileSystem, pyFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pyFilePath }'`); + importFailureInfo.push(`Resolved import with file '${pyFilePath}'`); resolvedPaths.push(pyFilePath); - } else if (allowPydFile && this.fileSystem.existsSync(pydFilePath) && isFile(this.fileSystem, pydFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pydFilePath }'`); + } else if ( + allowPydFile && + this.fileSystem.existsSync(pydFilePath) && + isFile(this.fileSystem, pydFilePath) + ) { + importFailureInfo.push(`Resolved import with file '${pydFilePath}'`); resolvedPaths.push(pydFilePath); isPydFile = true; } else { - importFailureInfo.push(`Partially resolved import with directory '${ dirPath }'`); + importFailureInfo.push(`Partially resolved import with directory '${dirPath}'`); resolvedPaths.push(''); isNamespacePackage = true; } @@ -634,7 +728,8 @@ export class ImportResolver { // the string '-stubs' to its top-level directory name. We'll // look there first. const stubsDirPath = dirPath + '-stubs'; - foundDirectory = this.fileSystem.existsSync(stubsDirPath) && isDirectory(this.fileSystem, stubsDirPath); + foundDirectory = + this.fileSystem.existsSync(stubsDirPath) && isDirectory(this.fileSystem, stubsDirPath); if (foundDirectory) { dirPath = stubsDirPath; } @@ -658,14 +753,14 @@ export class ImportResolver { let foundInit = false; if (this.fileSystem.existsSync(pyiFilePath) && isFile(this.fileSystem, pyiFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pyiFilePath }'`); + importFailureInfo.push(`Resolved import with file '${pyiFilePath}'`); resolvedPaths.push(pyiFilePath); if (isLastPart) { isStubFile = true; } foundInit = true; } else if (this.fileSystem.existsSync(pyFilePath) && isFile(this.fileSystem, pyFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pyFilePath }'`); + importFailureInfo.push(`Resolved import with file '${pyFilePath}'`); resolvedPaths.push(pyFilePath); foundInit = true; } @@ -684,30 +779,35 @@ export class ImportResolver { const pydFilePath = pyFilePath + 'd'; if (this.fileSystem.existsSync(pyiFilePath) && isFile(this.fileSystem, pyiFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pyiFilePath }'`); + importFailureInfo.push(`Resolved import with file '${pyiFilePath}'`); resolvedPaths.push(pyiFilePath); if (isLastPart) { isStubFile = true; } } else if (this.fileSystem.existsSync(pyFilePath) && isFile(this.fileSystem, pyFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pyFilePath }'`); + importFailureInfo.push(`Resolved import with file '${pyFilePath}'`); resolvedPaths.push(pyFilePath); - } else if (allowPydFile && this.fileSystem.existsSync(pydFilePath) && isFile(this.fileSystem, pydFilePath)) { - importFailureInfo.push(`Resolved import with file '${ pydFilePath }'`); + } else if ( + allowPydFile && + this.fileSystem.existsSync(pydFilePath) && + isFile(this.fileSystem, pydFilePath) + ) { + importFailureInfo.push(`Resolved import with file '${pydFilePath}'`); resolvedPaths.push(pydFilePath); if (isLastPart) { isPydFile = true; } } else if (foundDirectory) { - importFailureInfo.push(`Partially resolved import with directory '${ dirPath }'`); + importFailureInfo.push(`Partially resolved import with directory '${dirPath}'`); resolvedPaths.push(''); if (isLastPart) { implicitImports = this._findImplicitImports(dirPath, [pyFilePath, pyiFilePath]); isNamespacePackage = true; } } else { - importFailureInfo.push(`Did not find file '${ pyiFilePath }',` + - ` '${ pyFilePath }' or '${ pydFilePath }'`); + importFailureInfo.push( + `Did not find file '${pyiFilePath}',` + ` '${pyFilePath}' or '${pydFilePath}'` + ); } break; } @@ -739,10 +839,12 @@ export class ImportResolver { }; } - private _getCompletionSuggestionsAbsolute(rootPath: string, - moduleDescriptor: ImportedModuleDescriptor, suggestions: string[], - similarityLimit: number) { - + private _getCompletionSuggestionsAbsolute( + rootPath: string, + moduleDescriptor: ImportedModuleDescriptor, + suggestions: string[], + similarityLimit: number + ) { // Starting at the specified path, walk the file system to find the // specified module. let dirPath = rootPath; @@ -763,8 +865,7 @@ export class ImportResolver { // Provide completions only if we're on the last part // of the name. if (i === nameParts.length - 1) { - this._addFilteredSuggestions(dirPath, - nameParts[i], suggestions, similarityLimit); + this._addFilteredSuggestions(dirPath, nameParts[i], suggestions, similarityLimit); } dirPath = combinePaths(dirPath, nameParts[i]); @@ -775,9 +876,7 @@ export class ImportResolver { } } - private _addFilteredSuggestions(dirPath: string, filter: string, suggestions: string[], - similarityLimit: number) { - + private _addFilteredSuggestions(dirPath: string, filter: string, suggestions: string[], similarityLimit: number) { const entries = getFileSystemEntries(this.fileSystem, dirPath); entries.files.forEach(file => { @@ -786,9 +885,10 @@ export class ImportResolver { if (fileExtension === '.py' || fileExtension === '.pyi' || fileExtension === '.pyd') { if (fileWithoutExtension !== '__init__') { - if (!filter || StringUtils.computeCompletionSimilarity( - filter, fileWithoutExtension) >= similarityLimit) { - + if ( + !filter || + StringUtils.computeCompletionSimilarity(filter, fileWithoutExtension) >= similarityLimit + ) { this._addUniqueSuggestion(fileWithoutExtension, suggestions); } } diff --git a/server/src/analyzer/importResult.ts b/server/src/analyzer/importResult.ts index 7f668840b..75d1cf1f4 100644 --- a/server/src/analyzer/importResult.ts +++ b/server/src/analyzer/importResult.ts @@ -1,11 +1,11 @@ /* -* importResult.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Interface that describes the output of the import resolver. -*/ + * importResult.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Interface that describes the output of the import resolver. + */ export const enum ImportType { BuiltIn, diff --git a/server/src/analyzer/importStatementUtils.ts b/server/src/analyzer/importStatementUtils.ts index a64284ac4..e4dbad59d 100644 --- a/server/src/analyzer/importStatementUtils.ts +++ b/server/src/analyzer/importStatementUtils.ts @@ -1,19 +1,26 @@ /* -* importStatementUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Utility routines for summarizing and manipulating -* import statements in a python source file. -*/ + * importStatementUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Utility routines for summarizing and manipulating + * import statements in a python source file. + */ import { TextEditAction } from '../common/editAction'; import { convertOffsetToPosition } from '../common/positionUtils'; import { Position } from '../common/textRange'; import { TextRange } from '../common/textRange'; -import { ImportAsNode, ImportFromAsNode, ImportFromNode, ImportNode, - ModuleNameNode, ModuleNode, ParseNodeType } from '../parser/parseNodes'; +import { + ImportAsNode, + ImportFromAsNode, + ImportFromNode, + ImportNode, + ModuleNameNode, + ModuleNode, + ParseNodeType +} from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; import * as AnalyzerNodeInfo from './analyzerNodeInfo'; import { ImportResult, ImportType } from './importResult'; @@ -67,9 +74,11 @@ export function getTopLevelImports(parseTree: ModuleNode): ImportStatements { return localImports; } -export function getTextEditsForAutoImportSymbolAddition(symbolName: string, - importStatement: ImportStatement, parseResults: ParseResults) { - +export function getTextEditsForAutoImportSymbolAddition( + symbolName: string, + importStatement: ImportStatement, + parseResults: ParseResults +) { const textEditList: TextEditAction[] = []; // Scan through the import symbols to find the right insertion point, @@ -88,15 +97,16 @@ export function getTextEditsForAutoImportSymbolAddition(symbolName: string, priorImport = curImport; } - const insertionOffset = priorImport ? TextRange.getEnd(priorImport) : - (importStatement.node.imports.length > 0 ? - importStatement.node.imports[0].start : - importStatement.node.start + importStatement.node.length); + const insertionOffset = priorImport + ? TextRange.getEnd(priorImport) + : importStatement.node.imports.length > 0 + ? importStatement.node.imports[0].start + : importStatement.node.start + importStatement.node.length; const insertionPosition = convertOffsetToPosition(insertionOffset, parseResults.tokenizerOutput.lines); textEditList.push({ range: { start: insertionPosition, end: insertionPosition }, - replacementText: priorImport ? (', ' + symbolName) : (symbolName + ', ') + replacementText: priorImport ? ', ' + symbolName : symbolName + ', ' }); } } @@ -104,13 +114,17 @@ export function getTextEditsForAutoImportSymbolAddition(symbolName: string, return textEditList; } -export function getTextEditsForAutoImportInsertion(symbolName: string, importStatements: ImportStatements, - moduleName: string, importType: ImportType, parseResults: ParseResults): TextEditAction[] { - +export function getTextEditsForAutoImportInsertion( + symbolName: string, + importStatements: ImportStatements, + moduleName: string, + importType: ImportType, + parseResults: ParseResults +): TextEditAction[] { const textEditList: TextEditAction[] = []; // We need to emit a new 'from import' statement. - let newImportStatement = `from ${ moduleName } import ${ symbolName }`; + let newImportStatement = `from ${moduleName} import ${symbolName}`; let insertionPosition: Position; if (importStatements.orderedImports.length > 0) { let insertBefore = true; @@ -124,8 +138,9 @@ export function getTextEditsForAutoImportInsertion(symbolName: string, importSta // If the import was resolved, use its import type. If it wasn't // resolved, assume that it's the same import type as the previous // one. - const curImportType: ImportType = curImport.importResult ? - curImport.importResult.importType : prevImportType; + const curImportType: ImportType = curImport.importResult + ? curImport.importResult.importType + : prevImportType; if (importType < curImportType) { if (!insertBefore && prevImportType < importType) { @@ -151,7 +166,6 @@ export function getTextEditsForAutoImportInsertion(symbolName: string, importSta // If this is the last import, see if we need to create a new group. if (curImport === importStatements.orderedImports[importStatements.orderedImports.length - 1]) { - if (importType > curImportType) { // Add an extra line to create a new group. newImportStatement = parseResults.tokenizerOutput.predominantEndOfLineSequence + newImportStatement; @@ -178,7 +192,8 @@ export function getTextEditsForAutoImportInsertion(symbolName: string, importSta insertionPosition = convertOffsetToPosition( insertBefore ? insertionImport.node.start : TextRange.getEnd(insertionImport.node), - parseResults.tokenizerOutput.lines); + parseResults.tokenizerOutput.lines + ); } else { insertionPosition = { line: 0, character: 0 }; } @@ -207,19 +222,20 @@ export function getTextEditsForAutoImportInsertion(symbolName: string, importSta } if (stopHere) { - insertionPosition = convertOffsetToPosition(statement.start, - parseResults.tokenizerOutput.lines); + insertionPosition = convertOffsetToPosition(statement.start, parseResults.tokenizerOutput.lines); addNewLineBefore = false; break; } else { insertionPosition = convertOffsetToPosition( statement.start + statement.length, - parseResults.tokenizerOutput.lines); + parseResults.tokenizerOutput.lines + ); addNewLineBefore = true; } } - newImportStatement += parseResults.tokenizerOutput.predominantEndOfLineSequence + + newImportStatement += + parseResults.tokenizerOutput.predominantEndOfLineSequence + parseResults.tokenizerOutput.predominantEndOfLineSequence; if (addNewLineBefore) { @@ -237,9 +253,7 @@ export function getTextEditsForAutoImportInsertion(symbolName: string, importSta return textEditList; } -function _processImportNode(node: ImportNode, localImports: ImportStatements, - followsNonImportStatement: boolean) { - +function _processImportNode(node: ImportNode, localImports: ImportStatements, followsNonImportStatement: boolean) { node.list.forEach(importAsNode => { const importResult = AnalyzerNodeInfo.getImportInfo(importAsNode.module); let resolvedPath: string | undefined; @@ -271,9 +285,11 @@ function _processImportNode(node: ImportNode, localImports: ImportStatements, }); } -function _processImportFromNode(node: ImportFromNode, localImports: ImportStatements, - followsNonImportStatement: boolean) { - +function _processImportFromNode( + node: ImportFromNode, + localImports: ImportStatements, + followsNonImportStatement: boolean +) { const importResult = AnalyzerNodeInfo.getImportInfo(node.module); let resolvedPath: string | undefined; @@ -297,9 +313,11 @@ function _processImportFromNode(node: ImportFromNode, localImports: ImportStatem // Overwrite existing import statements because we always want to prefer // 'import from' over 'import'. Also, overwrite existing 'import from' if // the module name is shorter. - if (!prevEntry || prevEntry.node.nodeType === ParseNodeType.Import || - prevEntry.moduleName.length > localImport.moduleName.length) { - + if ( + !prevEntry || + prevEntry.node.nodeType === ParseNodeType.Import || + prevEntry.moduleName.length > localImport.moduleName.length + ) { localImports.mapByFilePath.set(resolvedPath, localImport); } } diff --git a/server/src/analyzer/parseTreeCleaner.ts b/server/src/analyzer/parseTreeCleaner.ts index 58bc99fb6..1dd1f7901 100644 --- a/server/src/analyzer/parseTreeCleaner.ts +++ b/server/src/analyzer/parseTreeCleaner.ts @@ -1,15 +1,15 @@ /* -* parseTreeCleaner.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* A parse tree walker that's used to clean any analysis -* information hanging off the parse tree. It's used when -* dependent files have been modified and the file requires -* reanalysis. Without this, we'd need to generate a fresh -* parse tree from scratch. -*/ + * parseTreeCleaner.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * A parse tree walker that's used to clean any analysis + * information hanging off the parse tree. It's used when + * dependent files have been modified and the file requires + * reanalysis. Without this, we'd need to generate a fresh + * parse tree from scratch. + */ import { ModuleNode, ParseNode } from '../parser/parseNodes'; import * as AnalyzerNodeInfo from './analyzerNodeInfo'; @@ -30,6 +30,6 @@ export class ParseTreeCleanerWalker extends ParseTreeWalker { visitNode(node: ParseNode) { AnalyzerNodeInfo.cleanNodeAnalysisInfo(node); - return super.visitNode(node); + return super.visitNode(node); } } diff --git a/server/src/analyzer/parseTreeUtils.ts b/server/src/analyzer/parseTreeUtils.ts index 06e6f462c..03a6a95f9 100644 --- a/server/src/analyzer/parseTreeUtils.ts +++ b/server/src/analyzer/parseTreeUtils.ts @@ -1,20 +1,34 @@ /* -* parseTreeUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Utility routines for traversing a parse tree. -*/ + * parseTreeUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Utility routines for traversing a parse tree. + */ import { fail } from '../common/debug'; import { convertPositionToOffset } from '../common/positionUtils'; import { Position } from '../common/textRange'; import { TextRange } from '../common/textRange'; import { TextRangeCollection } from '../common/textRangeCollection'; -import { ArgumentCategory, AssignmentExpressionNode, ClassNode, EvaluationScopeNode, - ExecutionScopeNode, ExpressionNode, FunctionNode, isExpressionNode, LambdaNode, ModuleNode, - ParameterCategory, ParseNode, ParseNodeType, StatementNode, SuiteNode } from '../parser/parseNodes'; +import { + ArgumentCategory, + AssignmentExpressionNode, + ClassNode, + EvaluationScopeNode, + ExecutionScopeNode, + ExpressionNode, + FunctionNode, + isExpressionNode, + LambdaNode, + ModuleNode, + ParameterCategory, + ParseNode, + ParseNodeType, + StatementNode, + SuiteNode +} from '../parser/parseNodes'; import { KeywordType, OperatorType, StringTokenFlags } from '../parser/tokenizerTypes'; import { decodeDocString } from './docStringUtils'; import { ParseTreeWalker } from './parseTreeWalker'; @@ -39,9 +53,11 @@ export function getNodeDepth(node: ParseNode): number { } // Returns the deepest node that contains the specified position. -export function findNodeByPosition(node: ParseNode, position: Position, - lines: TextRangeCollection): ParseNode | undefined { - +export function findNodeByPosition( + node: ParseNode, + position: Position, + lines: TextRangeCollection +): ParseNode | undefined { const offset = convertPositionToOffset(position, lines); if (offset === undefined) { return undefined; @@ -80,43 +96,53 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.MemberAccess: { - return printExpression(node.leftExpression, flags) + '.' + - node.memberName.value; + return printExpression(node.leftExpression, flags) + '.' + node.memberName.value; } case ParseNodeType.Call: { - return printExpression(node.leftExpression, flags) + '(' + - node.arguments.map(arg => { - let argStr = ''; - if (arg.argumentCategory === ArgumentCategory.UnpackedList) { - argStr = '*'; - } else if (arg.argumentCategory === ArgumentCategory.UnpackedDictionary) { - argStr = '**'; - } - if (arg.name) { - argStr += arg.name.value + '='; - } - argStr += printExpression(arg.valueExpression, flags); - return argStr; - }).join(', ') + - ')'; + return ( + printExpression(node.leftExpression, flags) + + '(' + + node.arguments + .map(arg => { + let argStr = ''; + if (arg.argumentCategory === ArgumentCategory.UnpackedList) { + argStr = '*'; + } else if (arg.argumentCategory === ArgumentCategory.UnpackedDictionary) { + argStr = '**'; + } + if (arg.name) { + argStr += arg.name.value + '='; + } + argStr += printExpression(arg.valueExpression, flags); + return argStr; + }) + .join(', ') + + ')' + ); } case ParseNodeType.Index: { - return printExpression(node.baseExpression, flags) + '[' + + return ( + printExpression(node.baseExpression, flags) + + '[' + node.items.items.map(item => printExpression(item, flags)).join(', ') + - ']'; + ']' + ); } case ParseNodeType.UnaryOperation: { - return printOperator(node.operator) + ' ' + - printExpression(node.expression, flags); + return printOperator(node.operator) + ' ' + printExpression(node.expression, flags); } case ParseNodeType.BinaryOperation: { - return printExpression(node.leftExpression, flags) + ' ' + - printOperator(node.operator) + ' ' + - printExpression(node.rightExpression, flags); + return ( + printExpression(node.leftExpression, flags) + + ' ' + + printOperator(node.operator) + + ' ' + + printExpression(node.rightExpression, flags) + ); } case ParseNodeType.Number: { @@ -128,12 +154,14 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.StringList: { - if ((flags & PrintExpressionFlags.ForwardDeclarations) && node.typeAnnotation) { + if (flags & PrintExpressionFlags.ForwardDeclarations && node.typeAnnotation) { return printExpression(node.typeAnnotation, flags); } else { - return node.strings.map(str => { - return printExpression(str, flags); - }).join(' '); + return node.strings + .map(str => { + return printExpression(str, flags); + }) + .join(' '); } } @@ -157,15 +185,15 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla if (node.token.flags & StringTokenFlags.Triplicate) { if (node.token.flags & StringTokenFlags.SingleQuote) { - exprString += `'''${ node.token.escapedValue }'''`; + exprString += `'''${node.token.escapedValue}'''`; } else { - exprString += `"""${ node.token.escapedValue }"""`; + exprString += `"""${node.token.escapedValue}"""`; } } else { if (node.token.flags & StringTokenFlags.SingleQuote) { - exprString += `'${ node.token.escapedValue }'`; + exprString += `'${node.token.escapedValue}'`; } else { - exprString += `"${ node.token.escapedValue }"`; + exprString += `"${node.token.escapedValue}"`; } } @@ -173,24 +201,25 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.Assignment: { - return printExpression(node.leftExpression, flags) + ' = ' + - printExpression(node.rightExpression, flags); + return printExpression(node.leftExpression, flags) + ' = ' + printExpression(node.rightExpression, flags); } case ParseNodeType.AssignmentExpression: { - return printExpression(node.name, flags) + ' := ' + - printExpression(node.rightExpression, flags); + return printExpression(node.name, flags) + ' := ' + printExpression(node.rightExpression, flags); } case ParseNodeType.TypeAnnotation: { - return printExpression(node.valueExpression, flags) + ': ' + - printExpression(node.typeAnnotation, flags); + return printExpression(node.valueExpression, flags) + ': ' + printExpression(node.typeAnnotation, flags); } case ParseNodeType.AugmentedAssignment: { - return printExpression(node.leftExpression, flags) + ' ' + - printOperator(node.operator) + ' ' + - printExpression(node.rightExpression, flags); + return ( + printExpression(node.leftExpression, flags) + + ' ' + + printOperator(node.operator) + + ' ' + + printExpression(node.rightExpression, flags) + ); } case ParseNodeType.Await: { @@ -198,16 +227,20 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.Ternary: { - return printExpression(node.ifExpression, flags) + ' if ' + - printExpression(node.testExpression, flags) + ' else ' + - printExpression(node.elseExpression, flags); + return ( + printExpression(node.ifExpression, flags) + + ' if ' + + printExpression(node.testExpression, flags) + + ' else ' + + printExpression(node.elseExpression, flags) + ); } case ParseNodeType.List: { const expressions = node.entries.map(expr => { return printExpression(expr, flags); }); - return `[${ expressions.join(', ') }]`; + return `[${expressions.join(', ')}]`; } case ParseNodeType.Unpack: { @@ -219,9 +252,9 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla return printExpression(expr, flags); }); if (expressions.length === 1) { - return `(${ expressions[0] }, )`; + return `(${expressions[0]}, )`; } - return `(${ expressions.join(', ') })`; + return `(${expressions.join(', ')})`; } case ParseNodeType.Yield: { @@ -248,19 +281,26 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } else if (node.expression.nodeType === ParseNodeType.DictionaryKeyEntry) { const keyStr = printExpression(node.expression.keyExpression, flags); const valueStr = printExpression(node.expression.valueExpression, flags); - listStr = `${ keyStr }: ${ valueStr }`; + listStr = `${keyStr}: ${valueStr}`; } - return listStr + ' ' + - node.comprehensions.map(expr => { - if (expr.nodeType === ParseNodeType.ListComprehensionFor) { - return `${ expr.isAsync ? 'async ' : '' }for ` + - printExpression(expr.targetExpression, flags) + - ` in ${ printExpression(expr.iterableExpression, flags) }`; - } else { - return `if ${ printExpression(expr.testExpression, flags) }`; - } - }).join(' '); + return ( + listStr + + ' ' + + node.comprehensions + .map(expr => { + if (expr.nodeType === ParseNodeType.ListComprehensionFor) { + return ( + `${expr.isAsync ? 'async ' : ''}for ` + + printExpression(expr.targetExpression, flags) + + ` in ${printExpression(expr.iterableExpression, flags)}` + ); + } else { + return `if ${printExpression(expr.testExpression, flags)}`; + } + }) + .join(' ') + ); } case ParseNodeType.Slice: { @@ -278,24 +318,31 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.Lambda: { - return 'lambda ' + node.parameters.map(param => { - let paramStr = ''; + return ( + 'lambda ' + + node.parameters + .map(param => { + let paramStr = ''; - if (param.category === ParameterCategory.VarArgList) { - paramStr += '*'; - } else if (param.category === ParameterCategory.VarArgDictionary) { - paramStr += '**'; - } + if (param.category === ParameterCategory.VarArgList) { + paramStr += '*'; + } else if (param.category === ParameterCategory.VarArgDictionary) { + paramStr += '**'; + } - if (param.name) { - paramStr += param.name.value; - } + if (param.name) { + paramStr += param.name.value; + } - if (param.defaultValue) { - paramStr += ' = ' + printExpression(param.defaultValue, flags); - } - return paramStr; - }).join(', ') + ': ' + printExpression(node.expression, flags); + if (param.defaultValue) { + paramStr += ' = ' + printExpression(param.defaultValue, flags); + } + return paramStr; + }) + .join(', ') + + ': ' + + printExpression(node.expression, flags) + ); } case ParseNodeType.Constant: { @@ -312,10 +359,12 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.Dictionary: { - return `{ ${ node.entries.map(entry => { + return `{ ${node.entries.map(entry => { if (entry.nodeType === ParseNodeType.DictionaryKeyEntry) { - return `${ printExpression(entry.keyExpression, flags) }: ` + - `${ printExpression(entry.valueExpression, flags) }`; + return ( + `${printExpression(entry.keyExpression, flags)}: ` + + `${printExpression(entry.valueExpression, flags)}` + ); } else { return printExpression(entry, flags); } @@ -323,7 +372,7 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla } case ParseNodeType.DictionaryExpandEntry: { - return `**${ printExpression(node.expandExpression, flags) }`; + return `**${printExpression(node.expandExpression, flags)}`; } case ParseNodeType.Set: { @@ -423,9 +472,7 @@ export function getEnclosingModule(node: ParseNode): ModuleNode { return undefined!; } -export function getEnclosingClassOrModule(node: ParseNode, - stopAtFunction = false): ClassNode | ModuleNode | undefined { - +export function getEnclosingClassOrModule(node: ParseNode, stopAtFunction = false): ClassNode | ModuleNode | undefined { let curNode = node.parent; while (curNode) { if (curNode.nodeType === ParseNodeType.Class) { @@ -465,9 +512,9 @@ export function getEnclosingFunction(node: ParseNode): FunctionNode | undefined return undefined; } -export function getEvaluationNodeForAssignmentExpression(node: AssignmentExpressionNode): - LambdaNode | FunctionNode | ModuleNode | undefined { - +export function getEvaluationNodeForAssignmentExpression( + node: AssignmentExpressionNode +): LambdaNode | FunctionNode | ModuleNode | undefined { // PEP 572 indicates that the evaluation node for an assignment expression // target is the containing lambda, function or module, but not a class. let curNode: ParseNode | undefined = getEvaluationScopeNode(node); @@ -547,9 +594,10 @@ export function getExecutionScopeNode(node: ParseNode): ExecutionScopeNode { // Classes are not considered execution scope because they are executed // within the context of their containing module or function. Likewise, list // comprehensions are executed within their container. - while (evaluationScope.nodeType === ParseNodeType.Class || - evaluationScope.nodeType === ParseNodeType.ListComprehension) { - + while ( + evaluationScope.nodeType === ParseNodeType.Class || + evaluationScope.nodeType === ParseNodeType.ListComprehension + ) { evaluationScope = getEvaluationScopeNode(evaluationScope.parent!); } @@ -592,9 +640,14 @@ export function isSuiteEmpty(node: SuiteNode): boolean { export function isMatchingExpression(expression1: ExpressionNode, expression2: ExpressionNode): boolean { if (expression1.nodeType === ParseNodeType.Name && expression2.nodeType === ParseNodeType.Name) { return expression1.value === expression2.value; - } else if (expression1.nodeType === ParseNodeType.MemberAccess && expression2.nodeType === ParseNodeType.MemberAccess) { - return isMatchingExpression(expression1.leftExpression, expression2.leftExpression) && - expression1.memberName.value === expression2.memberName.value; + } else if ( + expression1.nodeType === ParseNodeType.MemberAccess && + expression2.nodeType === ParseNodeType.MemberAccess + ) { + return ( + isMatchingExpression(expression1.leftExpression, expression2.leftExpression) && + expression1.memberName.value === expression2.memberName.value + ); } return false; @@ -609,10 +662,12 @@ export function isWithinDefaultParamInitializer(node: ParseNode) { return true; } - if (curNode.nodeType === ParseNodeType.Lambda || - curNode.nodeType === ParseNodeType.Function || - curNode.nodeType === ParseNodeType.Class || - curNode.nodeType === ParseNodeType.Module) { + if ( + curNode.nodeType === ParseNodeType.Lambda || + curNode.nodeType === ParseNodeType.Function || + curNode.nodeType === ParseNodeType.Class || + curNode.nodeType === ParseNodeType.Module + ) { return false; } @@ -636,8 +691,7 @@ export function getDocString(statements: StatementNode[]): string | undefined { // If the first statement in the suite isn't a StringNode, // assume there is no docString. const statementList = statements[0]; - if (statementList.statements.length === 0 || - statementList.statements[0].nodeType !== ParseNodeType.StringList) { + if (statementList.statements.length === 0 || statementList.statements[0].nodeType !== ParseNodeType.StringList) { return undefined; } @@ -658,22 +712,27 @@ export function getDocString(statements: StatementNode[]): string | undefined { // This pattern is commonly used to set the default values that are // not specified in the original list. export function isAssignmentToDefaultsFollowingNamedTuple(callNode: ParseNode): boolean { - if (callNode.nodeType !== ParseNodeType.Call || !callNode.parent || - callNode.parent.nodeType !== ParseNodeType.Assignment || - callNode.parent.leftExpression.nodeType !== ParseNodeType.Name || - !callNode.parent.parent || - callNode.parent.parent.nodeType !== ParseNodeType.StatementList) { - + if ( + callNode.nodeType !== ParseNodeType.Call || + !callNode.parent || + callNode.parent.nodeType !== ParseNodeType.Assignment || + callNode.parent.leftExpression.nodeType !== ParseNodeType.Name || + !callNode.parent.parent || + callNode.parent.parent.nodeType !== ParseNodeType.StatementList + ) { return false; } const namedTupleAssignedName = callNode.parent.leftExpression.value; const statementList = callNode.parent.parent; - if (statementList.statements[0] !== callNode.parent || - !statementList.parent || - !(statementList.parent.nodeType === ParseNodeType.Module || - statementList.parent.nodeType === ParseNodeType.Suite)) { - + if ( + statementList.statements[0] !== callNode.parent || + !statementList.parent || + !( + statementList.parent.nodeType === ParseNodeType.Module || + statementList.parent.nodeType === ParseNodeType.Suite + ) + ) { return false; } @@ -699,15 +758,17 @@ export function isAssignmentToDefaultsFollowingNamedTuple(callNode: ParseNode): if (nextStatement.statements[0].nodeType === ParseNodeType.Assignment) { const assignNode = nextStatement.statements[0]; - if (assignNode.leftExpression.nodeType === ParseNodeType.MemberAccess && - assignNode.leftExpression.memberName.value === '__defaults__') { - + if ( + assignNode.leftExpression.nodeType === ParseNodeType.MemberAccess && + assignNode.leftExpression.memberName.value === '__defaults__' + ) { const defaultTarget = assignNode.leftExpression.leftExpression; - if (defaultTarget.nodeType === ParseNodeType.MemberAccess && - defaultTarget.memberName.value === '__new__' && - defaultTarget.leftExpression.nodeType === ParseNodeType.Name && - defaultTarget.leftExpression.value === namedTupleAssignedName) { - + if ( + defaultTarget.nodeType === ParseNodeType.MemberAccess && + defaultTarget.memberName.value === '__new__' && + defaultTarget.leftExpression.nodeType === ParseNodeType.Name && + defaultTarget.leftExpression.value === namedTupleAssignedName + ) { return true; } } diff --git a/server/src/analyzer/parseTreeWalker.ts b/server/src/analyzer/parseTreeWalker.ts index e913ac0af..cbf32af6d 100644 --- a/server/src/analyzer/parseTreeWalker.ts +++ b/server/src/analyzer/parseTreeWalker.ts @@ -1,26 +1,81 @@ /* -* parseTreeWalker.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Class that traverses a parse tree. -*/ + * parseTreeWalker.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Class that traverses a parse tree. + */ import { fail } from '../common/debug'; -import { ArgumentNode, AssertNode, AssignmentExpressionNode, AssignmentNode, - AugmentedAssignmentNode, AwaitNode, BinaryOperationNode, BreakNode, CallNode, - ClassNode, ConstantNode, ContinueNode, DecoratorNode, DelNode, - DictionaryExpandEntryNode, DictionaryKeyEntryNode, DictionaryNode, EllipsisNode, - ErrorNode, ExceptNode, FormatStringNode, ForNode, FunctionNode, GlobalNode, IfNode, - ImportAsNode, ImportFromAsNode, ImportFromNode, ImportNode, IndexItemsNode, - IndexNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, - ListComprehensionNode, ListNode, MemberAccessNode, ModuleNameNode, ModuleNode, NameNode, - NonlocalNode, NumberNode, ParameterNode, ParseNode, ParseNodeArray, ParseNodeType, - PassNode, RaiseNode, ReturnNode, SetNode, SliceNode, StatementListNode, - StringListNode, StringNode, SuiteNode, TernaryNode, TryNode, - TupleNode, TypeAnnotationNode, UnaryOperationNode, UnpackNode, - WhileNode, WithItemNode, WithNode, YieldFromNode, YieldNode } from '../parser/parseNodes'; +import { + ArgumentNode, + AssertNode, + AssignmentExpressionNode, + AssignmentNode, + AugmentedAssignmentNode, + AwaitNode, + BinaryOperationNode, + BreakNode, + CallNode, + ClassNode, + ConstantNode, + ContinueNode, + DecoratorNode, + DelNode, + DictionaryExpandEntryNode, + DictionaryKeyEntryNode, + DictionaryNode, + EllipsisNode, + ErrorNode, + ExceptNode, + FormatStringNode, + ForNode, + FunctionNode, + GlobalNode, + IfNode, + ImportAsNode, + ImportFromAsNode, + ImportFromNode, + ImportNode, + IndexItemsNode, + IndexNode, + LambdaNode, + ListComprehensionForNode, + ListComprehensionIfNode, + ListComprehensionNode, + ListNode, + MemberAccessNode, + ModuleNameNode, + ModuleNode, + NameNode, + NonlocalNode, + NumberNode, + ParameterNode, + ParseNode, + ParseNodeArray, + ParseNodeType, + PassNode, + RaiseNode, + ReturnNode, + SetNode, + SliceNode, + StatementListNode, + StringListNode, + StringNode, + SuiteNode, + TernaryNode, + TryNode, + TupleNode, + TypeAnnotationNode, + UnaryOperationNode, + UnpackNode, + WhileNode, + WithItemNode, + WithNode, + YieldFromNode, + YieldNode +} from '../parser/parseNodes'; // To use this class, create a subclass and override the // visitXXX methods that you want to handle. @@ -229,8 +284,7 @@ export class ParseTreeWalker { case ParseNodeType.Function: if (this.visitFunction(node)) { - return [...node.decorators, node.name, ...node.parameters, - node.returnTypeAnnotation, node.suite]; + return [...node.decorators, node.name, ...node.parameters, node.returnTypeAnnotation, node.suite]; } break; diff --git a/server/src/analyzer/program.ts b/server/src/analyzer/program.ts index b63337eb5..788fbc1ef 100644 --- a/server/src/analyzer/program.ts +++ b/server/src/analyzer/program.ts @@ -1,12 +1,12 @@ /* -* program.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* An object that tracks all of the source files being analyzed -* and all of their recursive imports. -*/ + * program.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * An object that tracks all of the source files being analyzed + * and all of their recursive imports. + */ import { CompletionItem, CompletionList, DocumentSymbol, SymbolInformation } from 'vscode-languageserver'; @@ -17,8 +17,12 @@ import { Diagnostic } from '../common/diagnostic'; import { FileDiagnostics } from '../common/diagnosticSink'; import { FileEditAction, TextEditAction } from '../common/editAction'; import { - combinePaths, getDirectoryPath, getRelativePath, makeDirectories, - normalizePath, stripFileExtension + combinePaths, + getDirectoryPath, + getRelativePath, + makeDirectories, + normalizePath, + stripFileExtension } from '../common/pathUtils'; import { DocumentRange, doRangesOverlap, Position, Range } from '../common/textRange'; import { Duration, timingStats } from '../common/timing'; @@ -82,8 +86,11 @@ export class Program { private _configOptions: ConfigOptions; private _importResolver: ImportResolver; - constructor(initialImportResolver: ImportResolver, initialConfigOptions: ConfigOptions, - console?: ConsoleInterface) { + constructor( + initialImportResolver: ImportResolver, + initialConfigOptions: ConfigOptions, + console?: ConsoleInterface + ) { this._console = console || new StandardConsole(); this._evaluator = createTypeEvaluator(this._lookUpImport); this._importResolver = initialImportResolver; @@ -140,10 +147,11 @@ export class Program { let sourceFileCount = 0; this._sourceFileList.forEach(fileInfo => { - if (fileInfo.sourceFile.isParseRequired() || + if ( + fileInfo.sourceFile.isParseRequired() || fileInfo.sourceFile.isBindingRequired() || - fileInfo.sourceFile.isCheckingRequired()) { - + fileInfo.sourceFile.isCheckingRequired() + ) { if ((!this._configOptions.checkOnlyOpenFiles && fileInfo.isTracked) || fileInfo.isOpenByClient) { sourceFileCount++; } @@ -272,13 +280,10 @@ export class Program { analyze(maxTime?: MaxAnalysisTime, interactiveMode?: boolean): boolean { const elapsedTime = new Duration(); - const openFiles = this._sourceFileList.filter( - sf => sf.isOpenByClient && sf.sourceFile.isCheckingRequired() - ); + const openFiles = this._sourceFileList.filter(sf => sf.isOpenByClient && sf.sourceFile.isCheckingRequired()); if (openFiles.length > 0) { - const effectiveMaxTime = maxTime ? - maxTime.openFilesTimeInMs : Number.MAX_VALUE; + const effectiveMaxTime = maxTime ? maxTime.openFilesTimeInMs : Number.MAX_VALUE; // Check the open files. for (const sourceFileInfo of openFiles) { @@ -300,9 +305,11 @@ export class Program { if (!this._configOptions.checkOnlyOpenFiles) { // Do type analysis of remaining files. const allFiles = this._sourceFileList; - const effectiveMaxTime = maxTime ? - (interactiveMode ? maxTime.openFilesTimeInMs : maxTime.noOpenFilesTimeInMs) : - Number.MAX_VALUE; + const effectiveMaxTime = maxTime + ? interactiveMode + ? maxTime.openFilesTimeInMs + : maxTime.noOpenFilesTimeInMs + : Number.MAX_VALUE; // Now do type parsing and analysis of the remaining. for (const sourceFileInfo of allFiles) { @@ -320,9 +327,11 @@ export class Program { // Prints import dependency information for each of the files in // the program, skipping any typeshed files. printDependencies(projectRootDir: string, verbose: boolean) { - const sortedFiles = this._sourceFileList.filter(s => !s.isTypeshedFile).sort((a, b) => { - return (a.sourceFile.getFilePath() < b.sourceFile.getFilePath()) ? 1 : -1; - }); + const sortedFiles = this._sourceFileList + .filter(s => !s.isTypeshedFile) + .sort((a, b) => { + return a.sourceFile.getFilePath() < b.sourceFile.getFilePath() ? 1 : -1; + }); const zeroImportFiles: SourceFile[] = []; @@ -334,21 +343,23 @@ export class Program { filePath = relPath; } - this._console.log(`${ filePath }`); + this._console.log(`${filePath}`); - this._console.log(` Imports ${ sfInfo.imports.length } ` + - `file${ sfInfo.imports.length === 1 ? '' : 's' }`); + this._console.log( + ` Imports ${sfInfo.imports.length} ` + `file${sfInfo.imports.length === 1 ? '' : 's'}` + ); if (verbose) { sfInfo.imports.forEach(importInfo => { - this._console.log(` ${ importInfo.sourceFile.getFilePath() }`); + this._console.log(` ${importInfo.sourceFile.getFilePath()}`); }); } - this._console.log(` Imported by ${ sfInfo.importedBy.length } ` + - `file${ sfInfo.importedBy.length === 1 ? '' : 's' }`); + this._console.log( + ` Imported by ${sfInfo.importedBy.length} ` + `file${sfInfo.importedBy.length === 1 ? '' : 's'}` + ); if (verbose) { sfInfo.importedBy.forEach(importInfo => { - this._console.log(` ${ importInfo.sourceFile.getFilePath() }`); + this._console.log(` ${importInfo.sourceFile.getFilePath()}`); }); } @@ -359,10 +370,11 @@ export class Program { if (zeroImportFiles.length > 0) { this._console.log(''); - this._console.log(`${ zeroImportFiles.length } file${ zeroImportFiles.length === 1 ? '' : 's' }` + - ` not explicitly imported`); + this._console.log( + `${zeroImportFiles.length} file${zeroImportFiles.length === 1 ? '' : 's'}` + ` not explicitly imported` + ); zeroImportFiles.forEach(importFile => { - this._console.log(` ${ importFile.getFilePath() }`); + this._console.log(` ${importFile.getFilePath()}`); }); } } @@ -391,7 +403,7 @@ export class Program { try { makeDirectories(this._fs, typeStubDir, typingsPath); } catch (e) { - const errMsg = `Could not create directory for '${ typeStubDir }'`; + const errMsg = `Could not create directory for '${typeStubDir}'`; throw new Error(errMsg); } @@ -482,7 +494,7 @@ export class Program { symbolTable, docString }; - } + }; // Build a map of all modules within this program and the module- // level scope that contains the symbol table for the module. @@ -553,9 +565,11 @@ export class Program { // it imports (recursively) and ensures that all such files. If any of these files // have already been checked (they and their recursive imports have completed the // check phase), they are not included in the results. - private _getImportsRecursive(file: SourceFileInfo, closureMap: Map, - recursionCount: number) { - + private _getImportsRecursive( + file: SourceFileInfo, + closureMap: Map, + recursionCount: number + ) { // If the file is already in the closure map, we found a cyclical // dependency. Don't recur further. const filePath = file.sourceFile.getFilePath(); @@ -579,10 +593,11 @@ export class Program { } } - private _detectAndReportImportCycles(sourceFileInfo: SourceFileInfo, + private _detectAndReportImportCycles( + sourceFileInfo: SourceFileInfo, dependencyChain: SourceFileInfo[] = [], - dependencyMap = new Map()): void { - + dependencyMap = new Map() + ): void { // Don't bother checking for typestub files or third-party files. if (sourceFileInfo.sourceFile.isStubFile() || sourceFileInfo.isThirdPartyImport) { return; @@ -634,9 +649,7 @@ export class Program { firstSourceFile.sourceFile.addCircularDependency(circDep); } - private _markFileDirtyRecursive(sourceFileInfo: SourceFileInfo, - markMap: Map) { - + private _markFileDirtyRecursive(sourceFileInfo: SourceFileInfo, markMap: Map) { const filePath = sourceFileInfo.sourceFile.getFilePath(); // Don't mark it again if it's already been visited. @@ -656,7 +669,9 @@ export class Program { this._sourceFileList.forEach(sourceFileInfo => { if ((!options.checkOnlyOpenFiles && sourceFileInfo.isTracked) || sourceFileInfo.isOpenByClient) { const diagnostics = sourceFileInfo.sourceFile.getDiagnostics( - options, sourceFileInfo.diagnosticsVersion); + options, + sourceFileInfo.diagnosticsVersion + ); if (diagnostics !== undefined) { fileDiagnostics.push({ filePath: sourceFileInfo.sourceFile.getFilePath(), @@ -665,8 +680,7 @@ export class Program { // Update the cached diagnosticsVersion so we can determine // whether there are any updates next time we call getDiagnostics. - sourceFileInfo.diagnosticsVersion = - sourceFileInfo.sourceFile.getDiagnosticVersion(); + sourceFileInfo.diagnosticsVersion = sourceFileInfo.sourceFile.getDiagnosticVersion(); } } }); @@ -690,9 +704,7 @@ export class Program { }); } - getDefinitionsForPosition(filePath: string, position: Position): - DocumentRange[] | undefined { - + getDefinitionsForPosition(filePath: string, position: Position): DocumentRange[] | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; @@ -703,9 +715,11 @@ export class Program { return sourceFileInfo.sourceFile.getDefinitionsForPosition(position, this._evaluator); } - getReferencesForPosition(filePath: string, position: Position, - includeDeclaration: boolean): DocumentRange[] | undefined { - + getReferencesForPosition( + filePath: string, + position: Position, + includeDeclaration: boolean + ): DocumentRange[] | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; @@ -714,7 +728,10 @@ export class Program { this._bindFile(sourceFileInfo); const referencesResult = sourceFileInfo.sourceFile.getReferencesForPosition( - position, includeDeclaration, this._evaluator); + position, + includeDeclaration, + this._evaluator + ); if (!referencesResult) { return undefined; @@ -726,8 +743,7 @@ export class Program { if (curSourceFileInfo !== sourceFileInfo) { this._bindFile(curSourceFileInfo); - curSourceFileInfo.sourceFile.addReferences(referencesResult, - includeDeclaration, this._evaluator); + curSourceFileInfo.sourceFile.addReferences(referencesResult, includeDeclaration, this._evaluator); } } } @@ -740,8 +756,7 @@ export class Program { if (sourceFileInfo) { this._bindFile(sourceFileInfo); - sourceFileInfo.sourceFile.addHierarchicalSymbolsForDocument( - symbolList, this._evaluator); + sourceFileInfo.sourceFile.addHierarchicalSymbolsForDocument(symbolList, this._evaluator); } } @@ -755,14 +770,11 @@ export class Program { for (const sourceFileInfo of this._sourceFileList) { this._bindFile(sourceFileInfo); - sourceFileInfo.sourceFile.addSymbolsForDocument( - symbolList, this._evaluator, query); + sourceFileInfo.sourceFile.addSymbolsForDocument(symbolList, this._evaluator, query); } } - getHoverForPosition(filePath: string, position: Position): - HoverResults | undefined { - + getHoverForPosition(filePath: string, position: Position): HoverResults | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; @@ -773,9 +785,7 @@ export class Program { return sourceFileInfo.sourceFile.getHoverForPosition(position, this._evaluator); } - getSignatureHelpForPosition(filePath: string, position: Position): - SignatureHelpResults | undefined { - + getSignatureHelpForPosition(filePath: string, position: Position): SignatureHelpResults | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; @@ -783,13 +793,10 @@ export class Program { this._bindFile(sourceFileInfo); - return sourceFileInfo.sourceFile.getSignatureHelpForPosition( - position, this._lookUpImport, this._evaluator); + return sourceFileInfo.sourceFile.getSignatureHelpForPosition(position, this._lookUpImport, this._evaluator); } - getCompletionsForPosition(filePath: string, position: Position, - workspacePath: string): CompletionList | undefined { - + getCompletionsForPosition(filePath: string, position: Position, workspacePath: string): CompletionList | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; @@ -798,9 +805,14 @@ export class Program { this._bindFile(sourceFileInfo); return sourceFileInfo.sourceFile.getCompletionsForPosition( - position, workspacePath, this._configOptions, - this._importResolver, this._lookUpImport, this._evaluator, - () => this._buildModuleSymbolsMap(sourceFileInfo)); + position, + workspacePath, + this._configOptions, + this._importResolver, + this._lookUpImport, + this._evaluator, + () => this._buildModuleSymbolsMap(sourceFileInfo) + ); } resolveCompletionItem(filePath: string, completionItem: CompletionItem) { @@ -812,13 +824,16 @@ export class Program { this._bindFile(sourceFileInfo); sourceFileInfo.sourceFile.resolveCompletionItem( - this._configOptions, this._importResolver, this._lookUpImport, this._evaluator, - () => this._buildModuleSymbolsMap(sourceFileInfo), completionItem); + this._configOptions, + this._importResolver, + this._lookUpImport, + this._evaluator, + () => this._buildModuleSymbolsMap(sourceFileInfo), + completionItem + ); } - performQuickAction(filePath: string, command: string, - args: any[]): TextEditAction[] | undefined { - + performQuickAction(filePath: string, command: string, args: any[]): TextEditAction[] | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; @@ -826,20 +841,16 @@ export class Program { this._bindFile(sourceFileInfo); - return sourceFileInfo.sourceFile.performQuickAction( - command, args); + return sourceFileInfo.sourceFile.performQuickAction(command, args); } - renameSymbolAtPosition(filePath: string, position: Position, - newName: string): FileEditAction[] | undefined { - + renameSymbolAtPosition(filePath: string, position: Position, newName: string): FileEditAction[] | undefined { const sourceFileInfo = this._sourceFileMap.get(filePath); if (!sourceFileInfo) { return undefined; } - const referencesResult = sourceFileInfo.sourceFile.getReferencesForPosition( - position, true, this._evaluator); + const referencesResult = sourceFileInfo.sourceFile.getReferencesForPosition(position, true, this._evaluator); if (!referencesResult) { return undefined; @@ -851,8 +862,7 @@ export class Program { if (curSourceFileInfo !== sourceFileInfo) { this._bindFile(curSourceFileInfo); - curSourceFileInfo.sourceFile.addReferences(referencesResult, - true, this._evaluator); + curSourceFileInfo.sourceFile.addReferences(referencesResult, true, this._evaluator); } } } @@ -878,7 +888,7 @@ export class Program { // If a file is no longer tracked or opened, it can // be removed from the program. - for (let i = 0; i < this._sourceFileList.length;) { + for (let i = 0; i < this._sourceFileList.length; ) { const fileInfo = this._sourceFileList[i]; if (!this._isFileNeeded(fileInfo)) { fileDiagnostics.push({ @@ -975,33 +985,34 @@ export class Program { return false; } - private _isImportAllowed(importer: SourceFileInfo, importResult: ImportResult, - isImportStubFile: boolean): boolean { - + private _isImportAllowed(importer: SourceFileInfo, importResult: ImportResult, isImportStubFile: boolean): boolean { let thirdPartyImportAllowed = this._configOptions.useLibraryCodeForTypes; - if (importResult.importType === ImportType.ThirdParty || - (importer.isThirdPartyImport && importResult.importType === ImportType.Local)) { - + if ( + importResult.importType === ImportType.ThirdParty || + (importer.isThirdPartyImport && importResult.importType === ImportType.Local) + ) { if (this._allowedThirdPartyImports) { if (importResult.isRelative) { // If it's a relative import, we'll allow it because the // importer was already deemed to be allowed. thirdPartyImportAllowed = true; - } else if (this._allowedThirdPartyImports.some((importName: string) => { - // If this import name is the one that was explicitly - // allowed or is a child of that import name, - // it's considered allowed. - if (importResult.importName === importName) { - return true; - } + } else if ( + this._allowedThirdPartyImports.some((importName: string) => { + // If this import name is the one that was explicitly + // allowed or is a child of that import name, + // it's considered allowed. + if (importResult.importName === importName) { + return true; + } - if (importResult.importName.startsWith(importName + '.')) { - return true; - } + if (importResult.importName.startsWith(importName + '.')) { + return true; + } - return false; - })) { + return false; + }) + ) { thirdPartyImportAllowed = true; } } @@ -1018,9 +1029,7 @@ export class Program { return true; } - private _updateSourceFileImports(sourceFileInfo: SourceFileInfo, - options: ConfigOptions): SourceFileInfo[] { - + private _updateSourceFileImports(sourceFileInfo: SourceFileInfo, options: ConfigOptions): SourceFileInfo[] { const filesAdded: SourceFileInfo[] = []; // Get the new list of imports and see if it changed from the last @@ -1033,12 +1042,12 @@ export class Program { if (importResult.isImportFound) { if (this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) { if (importResult.resolvedPaths.length > 0) { - const filePath = importResult.resolvedPaths[ - importResult.resolvedPaths.length - 1]; + const filePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1]; if (filePath) { newImportPathMap.set(filePath, { isTypeshedFile: !!importResult.isTypeshedFile, - isThirdPartyImport: importResult.importType === ImportType.ThirdParty || + isThirdPartyImport: + importResult.importType === ImportType.ThirdParty || (sourceFileInfo.isThirdPartyImport && importResult.importType === ImportType.Local) }); } @@ -1049,18 +1058,21 @@ export class Program { if (this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) { newImportPathMap.set(implicitImport.path, { isTypeshedFile: !!importResult.isTypeshedFile, - isThirdPartyImport: importResult.importType === ImportType.ThirdParty || + isThirdPartyImport: + importResult.importType === ImportType.ThirdParty || (sourceFileInfo.isThirdPartyImport && importResult.importType === ImportType.Local) }); } }); } else if (options.verboseOutput) { if (!sourceFileInfo.isTypeshedFile || options.diagnosticSettings.reportTypeshedErrors) { - this._console.log(`Could not import '${ importResult.importName }' ` + - `in file '${ sourceFileInfo.sourceFile.getFilePath() }'`); + this._console.log( + `Could not import '${importResult.importName}' ` + + `in file '${sourceFileInfo.sourceFile.getFilePath()}'` + ); if (importResult.importFailureInfo) { importResult.importFailureInfo.forEach(diag => { - this._console.log(` ${ diag }`); + this._console.log(` ${diag}`); }); } } @@ -1074,7 +1086,8 @@ export class Program { // A previous import was removed. if (!newImportPathMap.has(oldFilePath)) { importInfo.importedBy = importInfo.importedBy.filter( - fi => fi.sourceFile.getFilePath() !== sourceFileInfo.sourceFile.getFilePath()); + fi => fi.sourceFile.getFilePath() !== sourceFileInfo.sourceFile.getFilePath() + ); } else { updatedImportMap.set(oldFilePath, importInfo); } @@ -1091,8 +1104,11 @@ export class Program { } else { const sourceFile = new SourceFile( this._fs, - importPath, importInfo.isTypeshedFile, - importInfo.isThirdPartyImport, this._console); + importPath, + importInfo.isTypeshedFile, + importInfo.isThirdPartyImport, + this._console + ); importedFileInfo = { sourceFile, isTracked: false, @@ -1127,8 +1143,7 @@ export class Program { sourceFileInfo.builtinsImport = undefined; const builtinsImport = sourceFileInfo.sourceFile.getBuiltinsImport(); if (builtinsImport) { - const resolvedBuiltinsPath = builtinsImport.resolvedPaths[ - builtinsImport.resolvedPaths.length - 1]; + const resolvedBuiltinsPath = builtinsImport.resolvedPaths[builtinsImport.resolvedPaths.length - 1]; sourceFileInfo.builtinsImport = this._sourceFileMap.get(resolvedBuiltinsPath); } diff --git a/server/src/analyzer/pythonPathUtils.ts b/server/src/analyzer/pythonPathUtils.ts index 104cf8bdb..fa0ccb110 100644 --- a/server/src/analyzer/pythonPathUtils.ts +++ b/server/src/analyzer/pythonPathUtils.ts @@ -1,18 +1,24 @@ /* -* pythonPathUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Utility routines used to resolve various paths in python. -*/ + * pythonPathUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Utility routines used to resolve various paths in python. + */ import * as child_process from 'child_process'; import { ConfigOptions } from '../common/configOptions'; import * as pathConsts from '../common/pathConsts'; -import { combinePaths, ensureTrailingDirectorySeparator, getDirectoryPath, - getFileSystemEntries, isDirectory, normalizePath } from '../common/pathUtils'; +import { + combinePaths, + ensureTrailingDirectorySeparator, + getDirectoryPath, + getFileSystemEntries, + isDirectory, + normalizePath +} from '../common/pathUtils'; import { VirtualFileSystem } from '../common/vfs'; const cachedSearchPaths = new Map(); @@ -23,8 +29,7 @@ export function getTypeShedFallbackPath(fs: VirtualFileSystem) { return undefined; } - moduleDirectory = getDirectoryPath(ensureTrailingDirectorySeparator( - normalizePath(moduleDirectory))); + moduleDirectory = getDirectoryPath(ensureTrailingDirectorySeparator(normalizePath(moduleDirectory))); const typeshedPath = combinePaths(moduleDirectory, pathConsts.typeshedFallback); if (fs.existsSync(typeshedPath)) { @@ -45,9 +50,12 @@ export function getTypeshedSubdirectory(typeshedPath: string, isStdLib: boolean) return combinePaths(typeshedPath, isStdLib ? 'stdlib' : 'third_party'); } -export function findPythonSearchPaths(fs: VirtualFileSystem, configOptions: ConfigOptions, - venv: string | undefined, importFailureInfo: string[]): string[] | undefined { - +export function findPythonSearchPaths( + fs: VirtualFileSystem, + configOptions: ConfigOptions, + venv: string | undefined, + importFailureInfo: string[] +): string[] | undefined { importFailureInfo.push('Finding python search paths'); let venvPath: string | undefined; @@ -64,14 +72,14 @@ export function findPythonSearchPaths(fs: VirtualFileSystem, configOptions: Conf if (venvPath) { let libPath = combinePaths(venvPath, pathConsts.lib); if (fs.existsSync(libPath)) { - importFailureInfo.push(`Found path '${ libPath }'; looking for ${ pathConsts.sitePackages }`); + importFailureInfo.push(`Found path '${libPath}'; looking for ${pathConsts.sitePackages}`); } else { - importFailureInfo.push(`Did not find '${ libPath }'; trying 'Lib' instead`); + importFailureInfo.push(`Did not find '${libPath}'; trying 'Lib' instead`); libPath = combinePaths(venvPath, 'Lib'); if (fs.existsSync(libPath)) { - importFailureInfo.push(`Found path '${ libPath }'; looking for ${ pathConsts.sitePackages }`); + importFailureInfo.push(`Found path '${libPath}'; looking for ${pathConsts.sitePackages}`); } else { - importFailureInfo.push(`Did not find '${ libPath }'`); + importFailureInfo.push(`Did not find '${libPath}'`); libPath = ''; } } @@ -79,10 +87,10 @@ export function findPythonSearchPaths(fs: VirtualFileSystem, configOptions: Conf if (libPath) { const sitePackagesPath = combinePaths(libPath, pathConsts.sitePackages); if (fs.existsSync(sitePackagesPath)) { - importFailureInfo.push(`Found path '${ sitePackagesPath }'`); + importFailureInfo.push(`Found path '${sitePackagesPath}'`); return [sitePackagesPath]; } else { - importFailureInfo.push(`Did not find '${ sitePackagesPath }', so looking for python subdirectory`); + importFailureInfo.push(`Did not find '${sitePackagesPath}', so looking for python subdirectory`); } // We didn't find a site-packages directory directly in the lib @@ -93,26 +101,27 @@ export function findPythonSearchPaths(fs: VirtualFileSystem, configOptions: Conf if (dirName.startsWith('python')) { const dirPath = combinePaths(libPath, dirName, pathConsts.sitePackages); if (fs.existsSync(dirPath)) { - importFailureInfo.push(`Found path '${ dirPath }'`); + importFailureInfo.push(`Found path '${dirPath}'`); return [dirPath]; } else { - importFailureInfo.push(`Path '${ dirPath }' is not a valid directory`); + importFailureInfo.push(`Path '${dirPath}' is not a valid directory`); } } } } - importFailureInfo.push(`Did not find '${ pathConsts.sitePackages }'. Falling back on python interpreter.`); + importFailureInfo.push(`Did not find '${pathConsts.sitePackages}'. Falling back on python interpreter.`); } // Fall back on the python interpreter. return getPythonPathFromPythonInterpreter(fs, configOptions.pythonPath, importFailureInfo); } -export function getPythonPathFromPythonInterpreter(fs: VirtualFileSystem, +export function getPythonPathFromPythonInterpreter( + fs: VirtualFileSystem, interpreterPath: string | undefined, - importFailureInfo: string[]): string[] { - + importFailureInfo: string[] +): string[] { const searchKey = interpreterPath || ''; // If we've seen this request before, return the cached results. @@ -136,13 +145,11 @@ export function getPythonPathFromPythonInterpreter(fs: VirtualFileSystem, let execOutput: string; if (interpreterPath) { - importFailureInfo.push(`Executing interpreter at '${ interpreterPath }'`); - execOutput = child_process.execFileSync( - interpreterPath, commandLineArgs, { encoding: 'utf8' }); + importFailureInfo.push(`Executing interpreter at '${interpreterPath}'`); + execOutput = child_process.execFileSync(interpreterPath, commandLineArgs, { encoding: 'utf8' }); } else { importFailureInfo.push(`Executing python interpreter`); - execOutput = child_process.execFileSync( - 'python', commandLineArgs, { encoding: 'utf8' }); + execOutput = child_process.execFileSync('python', commandLineArgs, { encoding: 'utf8' }); } // Parse the execOutput. It should be a JSON-encoded array of paths. @@ -157,7 +164,7 @@ export function getPythonPathFromPythonInterpreter(fs: VirtualFileSystem, if (fs.existsSync(normalizedPath) && isDirectory(fs, normalizedPath)) { pythonPaths.push(normalizedPath); } else { - importFailureInfo.push(`Skipping '${ normalizedPath }' because it is not a valid directory`); + importFailureInfo.push(`Skipping '${normalizedPath}' because it is not a valid directory`); } } } @@ -166,7 +173,7 @@ export function getPythonPathFromPythonInterpreter(fs: VirtualFileSystem, importFailureInfo.push(`Found no valid directories`); } } catch (err) { - importFailureInfo.push(`Could not parse output: '${ execOutput }'`); + importFailureInfo.push(`Could not parse output: '${execOutput}'`); throw err; } } catch { @@ -174,9 +181,9 @@ export function getPythonPathFromPythonInterpreter(fs: VirtualFileSystem, } cachedSearchPaths.set(searchKey, pythonPaths); - importFailureInfo.push(`Received ${ pythonPaths.length } paths from interpreter`); + importFailureInfo.push(`Received ${pythonPaths.length} paths from interpreter`); pythonPaths.forEach(path => { - importFailureInfo.push(` ${ path }`); + importFailureInfo.push(` ${path}`); }); return pythonPaths; } diff --git a/server/src/analyzer/scope.ts b/server/src/analyzer/scope.ts index 42ed29c7a..53adc5acb 100644 --- a/server/src/analyzer/scope.ts +++ b/server/src/analyzer/scope.ts @@ -1,13 +1,13 @@ /* -* scope.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Represents an evaluation scope and its defined symbols. -* It also contains a link to a parent scope (except for the -* top-most built-in scope). -*/ + * scope.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Represents an evaluation scope and its defined symbols. + * It also contains a link to a parent scope (except for the + * top-most built-in scope). + */ import { fail } from '../common/debug'; import { Symbol, SymbolFlags, SymbolTable } from './symbol'; @@ -103,9 +103,11 @@ export class Scope { return symbol; } - private _lookUpSymbolRecursiveInternal(name: string, isOutsideCallerModule: boolean, - isBeyondExecutionScope: boolean): SymbolWithScope | undefined { - + private _lookUpSymbolRecursiveInternal( + name: string, + isOutsideCallerModule: boolean, + isBeyondExecutionScope: boolean + ): SymbolWithScope | undefined { const symbol = this.symbolTable.get(name); if (symbol) { @@ -127,9 +129,11 @@ export class Scope { // If our recursion is about to take us outside the scope of the current // module (i.e. into a built-in scope), indicate as such with the second // parameter. - return this.parent._lookUpSymbolRecursiveInternal(name, + return this.parent._lookUpSymbolRecursiveInternal( + name, isOutsideCallerModule || this.type === ScopeType.Module, - isBeyondExecutionScope || this.isIndependentlyExecutable()); + isBeyondExecutionScope || this.isIndependentlyExecutable() + ); } return undefined; diff --git a/server/src/analyzer/scopeUtils.ts b/server/src/analyzer/scopeUtils.ts index 577644df5..24f3b4d1c 100644 --- a/server/src/analyzer/scopeUtils.ts +++ b/server/src/analyzer/scopeUtils.ts @@ -1,12 +1,12 @@ /* -* scopeUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Static utility methods related to scopes and their related -* symbol tables. -*/ + * scopeUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Static utility methods related to scopes and their related + * symbol tables. + */ import { ParseNode } from '../parser/parseNodes'; import { getScope } from './analyzerNodeInfo'; diff --git a/server/src/analyzer/service.ts b/server/src/analyzer/service.ts index ebc6a0a1a..0b3e54956 100644 --- a/server/src/analyzer/service.ts +++ b/server/src/analyzer/service.ts @@ -1,12 +1,12 @@ /* -* service.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* A persistent service that is able to analyze a collection of -* Python files. -*/ + * service.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * A persistent service that is able to analyze a collection of + * Python files. + */ import { CompletionItem, CompletionList, DocumentSymbol, SymbolInformation } from 'vscode-languageserver'; @@ -18,9 +18,16 @@ import { Diagnostic } from '../common/diagnostic'; import { FileDiagnostics } from '../common/diagnosticSink'; import { FileEditAction, TextEditAction } from '../common/editAction'; import { - combinePaths, FileSpec, forEachAncestorDirectory, getDirectoryPath, - getFileName, getFileSpec, getFileSystemEntries, isDirectory, - normalizePath, stripFileExtension + combinePaths, + FileSpec, + forEachAncestorDirectory, + getDirectoryPath, + getFileName, + getFileSpec, + getFileSystemEntries, + isDirectory, + normalizePath, + stripFileExtension } from '../common/pathUtils'; import { DocumentRange, Position, Range } from '../common/textRange'; import { Duration, timingStats } from '../common/timing'; @@ -70,9 +77,13 @@ export class AnalyzerService { private _requireTrackedFileUpdate = true; private _lastUserInteractionTime = Date.now(); - constructor(instanceName: string, fs: VirtualFileSystem, console?: ConsoleInterface, - importResolverFactory?: ImportResolverFactory, configOptions?: ConfigOptions) { - + constructor( + instanceName: string, + fs: VirtualFileSystem, + console?: ConsoleInterface, + importResolverFactory?: ImportResolverFactory, + configOptions?: ConfigOptions + ) { this._instanceName = instanceName; this._console = console || new StandardConsole(); this._configOptions = configOptions ?? new ConfigOptions(process.cwd()); @@ -84,7 +95,13 @@ export class AnalyzerService { } clone(instanceName: string): AnalyzerService { - return new AnalyzerService(instanceName, this._fs, this._console, this._importResolverFactory, this._configOptions); + return new AnalyzerService( + instanceName, + this._fs, + this._console, + this._importResolverFactory, + this._configOptions + ); } dispose() { @@ -113,8 +130,9 @@ export class AnalyzerService { this._program.setConfigOptions(this._configOptions); this._typeStubTargetImportName = commandLineOptions.typeStubTargetImportName; - this._executionRootPath = normalizePath(combinePaths( - commandLineOptions.executionRoot, this._configOptions.projectRoot)); + this._executionRootPath = normalizePath( + combinePaths(commandLineOptions.executionRoot, this._configOptions.projectRoot) + ); this._applyConfigOptions(); } @@ -135,15 +153,15 @@ export class AnalyzerService { this._scheduleReanalysis(false); } - getDefinitionForPosition(filePath: string, position: Position): - DocumentRange[] | undefined { - + getDefinitionForPosition(filePath: string, position: Position): DocumentRange[] | undefined { return this._program.getDefinitionsForPosition(filePath, position); } - getReferencesForPosition(filePath: string, position: Position, - includeDeclaration: boolean): DocumentRange[] | undefined { - + getReferencesForPosition( + filePath: string, + position: Position, + includeDeclaration: boolean + ): DocumentRange[] | undefined { return this._program.getReferencesForPosition(filePath, position, includeDeclaration); } @@ -155,21 +173,15 @@ export class AnalyzerService { this._program.addSymbolsForWorkspace(symbolList, query); } - getHoverForPosition(filePath: string, position: Position): - HoverResults | undefined { - + getHoverForPosition(filePath: string, position: Position): HoverResults | undefined { return this._program.getHoverForPosition(filePath, position); } - getSignatureHelpForPosition(filePath: string, position: Position): - SignatureHelpResults | undefined { - + getSignatureHelpForPosition(filePath: string, position: Position): SignatureHelpResults | undefined { return this._program.getSignatureHelpForPosition(filePath, position); } - getCompletionsForPosition(filePath: string, position: Position, - workspacePath: string): CompletionList | undefined { - + getCompletionsForPosition(filePath: string, position: Position, workspacePath: string): CompletionList | undefined { return this._program.getCompletionsForPosition(filePath, position, workspacePath); } @@ -181,9 +193,7 @@ export class AnalyzerService { return this._program.performQuickAction(filePath, command, args); } - renameSymbolAtPosition(filePath: string, position: Position, - newName: string): FileEditAction[] | undefined { - + renameSymbolAtPosition(filePath: string, position: Position, newName: string): FileEditAction[] | undefined { return this._program.renameSymbolAtPosition(filePath, position, newName); } @@ -251,10 +261,12 @@ export class AnalyzerService { // If the config file path was specified, determine whether it's // a directory (in which case the default config file name is assumed) // or a file. - configFilePath = combinePaths(commandLineOptions.executionRoot, - normalizePath(commandLineOptions.configFilePath)); + configFilePath = combinePaths( + commandLineOptions.executionRoot, + normalizePath(commandLineOptions.configFilePath) + ); if (!this._fs.existsSync(configFilePath)) { - this._console.log(`Configuration file not found at ${ configFilePath }.`); + this._console.log(`Configuration file not found at ${configFilePath}.`); configFilePath = commandLineOptions.executionRoot; } else { if (configFilePath.toLowerCase().endsWith('.json')) { @@ -263,7 +275,7 @@ export class AnalyzerService { projectRoot = configFilePath; configFilePath = this._findConfigFile(configFilePath); if (!configFilePath) { - this._console.log(`Configuration file not found at ${ projectRoot }.`); + this._console.log(`Configuration file not found at ${projectRoot}.`); } } } @@ -302,7 +314,7 @@ export class AnalyzerService { // If we found a config file, parse it to compute the effective options. if (configFilePath) { - this._console.log(`Loading configuration file at ${ configFilePath }`); + this._console.log(`Loading configuration file at ${configFilePath}`); const configJsonObj = this._parseConfigFile(configFilePath); if (configJsonObj) { configOptions.initializeFromJson(configJsonObj, this._console); @@ -312,14 +324,14 @@ export class AnalyzerService { // If no include paths were provided, assume that all files within // the project should be included. if (configOptions.include.length === 0) { - this._console.log(`No include entries specified; assuming ${ configFilePath }`); + this._console.log(`No include entries specified; assuming ${configFilePath}`); configOptions.include.push(getFileSpec(configFileDir, '.')); } // If there was no explicit set of excludes, add a few common ones to avoid long scan times. if (configOptions.exclude.length === 0) { defaultExcludes.forEach(exclude => { - this._console.log(`Auto-excluding ${ exclude }`); + this._console.log(`Auto-excluding ${exclude}`); configOptions.exclude.push(getFileSpec(configFileDir, exclude)); }); } @@ -328,12 +340,14 @@ export class AnalyzerService { } const reportDuplicateSetting = (settingName: string) => { - const settingSource = commandLineOptions.fromVsCodeExtension ? - 'the VS Code settings' : 'a command-line option'; + const settingSource = commandLineOptions.fromVsCodeExtension + ? 'the VS Code settings' + : 'a command-line option'; this._console.log( - `The ${ settingName } has been specified in both the config file and ` + - `${ settingSource }. The value in the config file (${ configOptions.venvPath }) ` + - `will take precedence`); + `The ${settingName} has been specified in both the config file and ` + + `${settingSource}. The value in the config file (${configOptions.venvPath}) ` + + `will take precedence` + ); }; // Apply the command-line options if the corresponding @@ -348,8 +362,9 @@ export class AnalyzerService { } if (commandLineOptions.pythonPath) { - this._console.log(`Setting pythonPath for service "${ this._instanceName }": ` + - `"${ commandLineOptions.pythonPath }"`); + this._console.log( + `Setting pythonPath for service "${this._instanceName}": ` + `"${commandLineOptions.pythonPath}"` + ); configOptions.pythonPath = commandLineOptions.pythonPath; } @@ -374,8 +389,7 @@ export class AnalyzerService { // or inconsistent information. if (configOptions.venvPath) { if (!this._fs.existsSync(configOptions.venvPath) || !isDirectory(this._fs, configOptions.venvPath)) { - this._console.log( - `venvPath ${ configOptions.venvPath } is not a valid directory.`); + this._console.log(`venvPath ${configOptions.venvPath} is not a valid directory.`); } // venvPath without defaultVenv means it won't do anything while resolveImport. @@ -387,18 +401,20 @@ export class AnalyzerService { if (!this._fs.existsSync(fullVenvPath) || !isDirectory(this._fs, fullVenvPath)) { this._console.log( - `venv ${ configOptions.defaultVenv } subdirectory not found ` + - `in venv path ${ configOptions.venvPath }.`); + `venv ${configOptions.defaultVenv} subdirectory not found ` + + `in venv path ${configOptions.venvPath}.` + ); } else { const importFailureInfo: string[] = []; if (findPythonSearchPaths(this._fs, configOptions, undefined, importFailureInfo) === undefined) { this._console.log( `site-packages directory cannot be located for venvPath ` + - `${ configOptions.venvPath } and venv ${ configOptions.defaultVenv }.`); + `${configOptions.venvPath} and venv ${configOptions.defaultVenv}.` + ); if (configOptions.verboseOutput) { importFailureInfo.forEach(diag => { - this._console.log(` ${ diag }`); + this._console.log(` ${diag}`); }); } } @@ -406,28 +422,29 @@ export class AnalyzerService { } } else { const importFailureInfo: string[] = []; - const pythonPaths = getPythonPathFromPythonInterpreter(this._fs, configOptions.pythonPath, importFailureInfo); + const pythonPaths = getPythonPathFromPythonInterpreter( + this._fs, + configOptions.pythonPath, + importFailureInfo + ); if (pythonPaths.length === 0) { if (configOptions.verboseOutput) { - this._console.log( - `No search paths found for configured python interpreter.`); + this._console.log(`No search paths found for configured python interpreter.`); } } else { if (configOptions.verboseOutput) { - this._console.log( - `Search paths found for configured python interpreter:`); + this._console.log(`Search paths found for configured python interpreter:`); pythonPaths.forEach(path => { - this._console.log(` ${ path }`); + this._console.log(` ${path}`); }); } } if (configOptions.verboseOutput) { if (importFailureInfo.length > 0) { - this._console.log( - `When attempting to get search paths from python interpreter:`); + this._console.log(`When attempting to get search paths from python interpreter:`); importFailureInfo.forEach(diag => { - this._console.log(` ${ diag }`); + this._console.log(` ${diag}`); }); } } @@ -436,22 +453,22 @@ export class AnalyzerService { // Is there a reference to a venv? If so, there needs to be a valid venvPath. if (configOptions.defaultVenv || configOptions.executionEnvironments.find(e => !!e.venv)) { if (!configOptions.venvPath) { - this._console.log( - `venvPath not specified, so venv settings will be ignored.`); + this._console.log(`venvPath not specified, so venv settings will be ignored.`); } } if (configOptions.typeshedPath) { - if (!this._fs.existsSync(configOptions.typeshedPath) || !isDirectory(this._fs, configOptions.typeshedPath)) { - this._console.log( - `typeshedPath ${ configOptions.typeshedPath } is not a valid directory.`); + if ( + !this._fs.existsSync(configOptions.typeshedPath) || + !isDirectory(this._fs, configOptions.typeshedPath) + ) { + this._console.log(`typeshedPath ${configOptions.typeshedPath} is not a valid directory.`); } } if (configOptions.typingsPath) { if (!this._fs.existsSync(configOptions.typingsPath) || !isDirectory(this._fs, configOptions.typingsPath)) { - this._console.log( - `typingsPath ${ configOptions.typingsPath } is not a valid directory.`); + this._console.log(`typingsPath ${configOptions.typingsPath} is not a valid directory.`); } } @@ -461,8 +478,7 @@ export class AnalyzerService { writeTypeStub() { const typingsPath = this._configOptions.typingsPath; if (!this._typeStubTargetPath || !this._typeStubTargetImportName) { - const errMsg = `Import '${ this._typeStubTargetImportName }'` + - ` could not be resolved`; + const errMsg = `Import '${this._typeStubTargetImportName}'` + ` could not be resolved`; this._console.error(errMsg); throw new Error(errMsg); } @@ -479,8 +495,7 @@ export class AnalyzerService { if (typeStubInputTargetParts[0].length === 0) { // We should never get here because the import resolution // would have failed. - const errMsg = `Import '${ this._typeStubTargetImportName }'` + - ` could not be resolved`; + const errMsg = `Import '${this._typeStubTargetImportName}'` + ` could not be resolved`; this._console.error(errMsg); throw new Error(errMsg); } @@ -491,7 +506,7 @@ export class AnalyzerService { this._fs.mkdirSync(typingsPath); } } catch (e) { - const errMsg = `Could not create typings directory '${ typingsPath }'`; + const errMsg = `Could not create typings directory '${typingsPath}'`; this._console.error(errMsg); throw new Error(errMsg); } @@ -504,7 +519,7 @@ export class AnalyzerService { this._fs.mkdirSync(typingsSubdirPath); } } catch (e) { - const errMsg = `Could not create typings subdirectory '${ typingsSubdirPath }'`; + const errMsg = `Could not create typings subdirectory '${typingsSubdirPath}'`; this._console.error(errMsg); throw new Error(errMsg); } @@ -551,7 +566,7 @@ export class AnalyzerService { try { configContents = this._fs.readFileSync(configPath, 'utf8'); } catch { - this._console.log(`Config file "${ configPath }" could not be read.`); + this._console.log(`Config file "${configPath}" could not be read.`); this._reportConfigParseError(); return undefined; } @@ -575,7 +590,7 @@ export class AnalyzerService { // resulting in parse errors. We'll give it a little more time and // try again. if (parseAttemptCount++ >= 5) { - this._console.log(`Config file "${ configPath }" could not be parsed. Verify that JSON is correct.`); + this._console.log(`Config file "${configPath}" could not be parsed. Verify that JSON is correct.`); this._reportConfigParseError(); return undefined; } @@ -587,8 +602,7 @@ export class AnalyzerService { const fileMap = new Map(); timingStats.findFilesTime.timeOperation(() => { - const matchedFiles = this._matchFiles(this._configOptions.include, - this._configOptions.exclude); + const matchedFiles = this._matchFiles(this._configOptions.include, this._configOptions.exclude); for (const file of matchedFiles) { fileMap.set(file, file); @@ -614,16 +628,14 @@ export class AnalyzerService { importedSymbols: [] }; - const importResult = this._importResolver.resolveImport( - '', execEnv, moduleDescriptor); + const importResult = this._importResolver.resolveImport('', execEnv, moduleDescriptor); if (importResult.isImportFound) { const filesToImport: string[] = []; // Namespace packages resolve to a directory name, so // don't include those. - const resolvedPath = importResult.resolvedPaths[ - importResult.resolvedPaths.length - 1]; + const resolvedPath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1]; // Get the directory that contains the root package. let targetPath = getDirectoryPath(resolvedPath); @@ -647,7 +659,8 @@ export class AnalyzerService { this._typeStubTargetIsSingleFile = false; } else { filesToImport.push(resolvedPath); - this._typeStubTargetIsSingleFile = importResult.resolvedPaths.length === 1 && + this._typeStubTargetIsSingleFile = + importResult.resolvedPaths.length === 1 && stripFileExtension(getFileName(importResult.resolvedPaths[0])) !== '__init__'; } @@ -659,7 +672,7 @@ export class AnalyzerService { this._program.setAllowedThirdPartyImports([this._typeStubTargetImportName]); this._program.setTrackedFiles(filesToImport); } else { - this._console.log(`Import '${ this._typeStubTargetImportName }' not found`); + this._console.log(`Import '${this._typeStubTargetImportName}' not found`); } } else { let fileList: string[] = []; @@ -673,8 +686,7 @@ export class AnalyzerService { if (fileList.length === 0) { this._console.log(`No source files found.`); } else { - this._console.log(`Found ${ fileList.length } ` + - `source ${ fileList.length === 1 ? 'file' : 'files' }`); + this._console.log(`Found ${fileList.length} ` + `source ${fileList.length === 1 ? 'file' : 'files'}`); } } @@ -733,7 +745,7 @@ export class AnalyzerService { } if (!foundFileSpec) { - this._console.log(`File or directory "${ includeSpec.wildcardRoot }" does not exist.`); + this._console.log(`File or directory "${includeSpec.wildcardRoot}" does not exist.`); } }); @@ -766,12 +778,12 @@ export class AnalyzerService { try { if (this._verboseOutput) { - this._console.log(`Adding fs watcher for directories:\n ${ fileList.join('\n') }`); + this._console.log(`Adding fs watcher for directories:\n ${fileList.join('\n')}`); } this._sourceFileWatcher = this._fs.createFileSystemWatcher(fileList, 'all', (event, path) => { if (this._verboseOutput) { - this._console.log(`Received fs event '${ event }' for path '${ path }'`); + this._console.log(`Received fs event '${event}' for path '${path}'`); } if (event === 'change') { @@ -782,7 +794,7 @@ export class AnalyzerService { } }); } catch { - this._console.log(`Exception caught when installing fs watcher for:\n ${ fileList.join('\n') }`); + this._console.log(`Exception caught when installing fs watcher for:\n ${fileList.join('\n')}`); } } } @@ -798,13 +810,12 @@ export class AnalyzerService { this._removeConfigFileWatcher(); if (this._configFilePath) { - this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath], - 'all', event => { - if (this._verboseOutput) { - this._console.log(`Received fs event '${ event }' for config file`); - } - this._scheduleReloadConfigFile(); - }); + this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath], 'all', event => { + if (this._verboseOutput) { + this._console.log(`Received fs event '${event}' for config file`); + } + this._scheduleReloadConfigFile(); + }); } } @@ -832,7 +843,7 @@ export class AnalyzerService { if (this._configFilePath) { this._updateConfigFileWatcher(); - this._console.log(`Reloading configuration file at ${ this._configFilePath }`); + this._console.log(`Reloading configuration file at ${this._configFilePath}`); const configJsonObj = this._parseConfigFile(this._configFilePath); if (configJsonObj) { this._configOptions.initializeFromJson(configJsonObj, this._console); @@ -881,7 +892,8 @@ export class AnalyzerService { const timeUntilNextAnalysisInMs = Math.max( minBackoffTimeInMs - timeSinceLastUserInteractionInMs, - minTimeBetweenAnalysisPassesInMs); + minTimeBetweenAnalysisPassesInMs + ); // Schedule a new timer. this._analyzeTimer = setTimeout(() => { @@ -943,7 +955,8 @@ export class AnalyzerService { } } } catch (e) { - const message: string = (e.stack ? e.stack.toString() : undefined) || + const message: string = + (e.stack ? e.stack.toString() : undefined) || (typeof e.message === 'string' ? e.message : undefined) || JSON.stringify(e); this._console.log('Error performing analysis: ' + message); diff --git a/server/src/analyzer/sourceFile.ts b/server/src/analyzer/sourceFile.ts index fcce25504..f590500c3 100644 --- a/server/src/analyzer/sourceFile.ts +++ b/server/src/analyzer/sourceFile.ts @@ -1,18 +1,15 @@ /* -* sourceFile.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Class that represents a single python source file. -*/ + * sourceFile.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Class that represents a single python source file. + */ import { CompletionItem, CompletionList, DocumentSymbol, SymbolInformation } from 'vscode-languageserver'; -import { - ConfigOptions, ExecutionEnvironment, - getDefaultDiagnosticSettings -} from '../common/configOptions'; +import { ConfigOptions, ExecutionEnvironment, getDefaultDiagnosticSettings } from '../common/configOptions'; import { ConsoleInterface, StandardConsole } from '../common/console'; import { assert } from '../common/debug'; import { Diagnostic, DiagnosticCategory } from '../common/diagnostic'; @@ -142,9 +139,13 @@ export class SourceFile { readonly fileSystem: VirtualFileSystem; - constructor(fs: VirtualFileSystem, filePath: string, isTypeshedStubFile: boolean, - isThirdPartyImport: boolean, console?: ConsoleInterface) { - + constructor( + fs: VirtualFileSystem, + filePath: string, + isTypeshedStubFile: boolean, + isThirdPartyImport: boolean, + console?: ConsoleInterface + ) { this.fileSystem = fs; this._console = console || new StandardConsole(); this._filePath = filePath; @@ -152,18 +153,19 @@ export class SourceFile { this._isTypeshedStubFile = isTypeshedStubFile; this._isThirdPartyImport = isThirdPartyImport; const fileName = getFileName(filePath); - this._isTypingStubFile = this._isStubFile && ( - fileName === 'typing.pyi' || fileName === 'typing_extensions.pyi'); + this._isTypingStubFile = + this._isStubFile && (fileName === 'typing.pyi' || fileName === 'typing_extensions.pyi'); this._isBuiltInStubFile = false; if (this._isStubFile) { - if (this._filePath.endsWith(normalizeSlashes('/collections/__init__.pyi')) || + if ( + this._filePath.endsWith(normalizeSlashes('/collections/__init__.pyi')) || fileName === 'builtins.pyi' || fileName === '_importlib_modulespec.pyi' || fileName === 'dataclasses.pyi' || fileName === 'abc.pyi' || - fileName === 'enum.pyi') { - + fileName === 'enum.pyi' + ) { this._isBuiltInStubFile = true; } } @@ -184,9 +186,7 @@ export class SourceFile { // Returns a list of cached diagnostics from the latest analysis job. // If the prevVersion is specified, the method returns undefined if // the diagnostics haven't changed. - getDiagnostics(options: ConfigOptions, prevDiagnosticVersion?: number): - Diagnostic[] | undefined { - + getDiagnostics(options: ConfigOptions, prevDiagnosticVersion?: number): Diagnostic[] | undefined { if (this._diagnosticVersion === prevDiagnosticVersion) { return undefined; } @@ -200,15 +200,11 @@ export class SourceFile { } let diagList: Diagnostic[] = []; - diagList = diagList.concat( - this._parseDiagnostics, - this._bindDiagnostics, - this._checkerDiagnostics); + diagList = diagList.concat(this._parseDiagnostics, this._bindDiagnostics, this._checkerDiagnostics); // Filter the diagnostics based on "type: ignore" lines. if (options.diagnosticSettings.enableTypeIgnoreComments) { - const typeIgnoreLines = this._parseResults ? - this._parseResults.tokenizerOutput.typeIgnoreLines : {}; + const typeIgnoreLines = this._parseResults ? this._parseResults.tokenizerOutput.typeIgnoreLines : {}; if (Object.keys(typeIgnoreLines).length > 0) { diagList = diagList.filter(d => { for (let line = d.range.start.line; line <= d.range.end.line; line++) { @@ -223,19 +219,34 @@ export class SourceFile { } if (options.diagnosticSettings.reportImportCycles !== 'none' && this._circularDependencies.length > 0) { - const category = options.diagnosticSettings.reportImportCycles === 'warning' ? - DiagnosticCategory.Warning : DiagnosticCategory.Error; + const category = + options.diagnosticSettings.reportImportCycles === 'warning' + ? DiagnosticCategory.Warning + : DiagnosticCategory.Error; this._circularDependencies.forEach(cirDep => { - diagList.push(new Diagnostic(category, 'Cycle detected in import chain\n' + - cirDep.getPaths().map(path => ' ' + path).join('\n'), getEmptyRange())); + diagList.push( + new Diagnostic( + category, + 'Cycle detected in import chain\n' + + cirDep + .getPaths() + .map(path => ' ' + path) + .join('\n'), + getEmptyRange() + ) + ); }); } if (this._hitMaxImportDepth !== undefined) { - diagList.push(new Diagnostic(DiagnosticCategory.Error, - `Import chain depth exceeded ${ this._hitMaxImportDepth }`, - getEmptyRange())); + diagList.push( + new Diagnostic( + DiagnosticCategory.Error, + `Import chain depth exceeded ${this._hitMaxImportDepth}`, + getEmptyRange() + ) + ); } if (this._isTypeshedStubFile) { @@ -245,8 +256,7 @@ export class SourceFile { // Convert all the errors to warnings. diagList = diagList.map(diag => { if (diag.category === DiagnosticCategory.Error) { - return new Diagnostic(DiagnosticCategory.Warning, - diag.message, diag.range); + return new Diagnostic(DiagnosticCategory.Warning, diag.message, diag.range); } return diag; }); @@ -471,24 +481,30 @@ export class SourceFile { // Resolve imports. timingStats.resolveImportsTime.timeOperation(() => { - [this._imports, this._builtinsImport, this._typingModulePath, this._collectionsModulePath] = - this._resolveImports(importResolver, parseResults.importedModules, execEnvironment); + [ + this._imports, + this._builtinsImport, + this._typingModulePath, + this._collectionsModulePath + ] = this._resolveImports(importResolver, parseResults.importedModules, execEnvironment); this._parseDiagnostics = diagSink.fetchAndClear(); }); // Is this file in a "strict" path? - const useStrict = configOptions.strict.find( - strictFileSpec => strictFileSpec.regExp.test(this._filePath)) !== undefined; + const useStrict = + configOptions.strict.find(strictFileSpec => strictFileSpec.regExp.test(this._filePath)) !== undefined; this._diagnosticSettings = CommentUtils.getFileLevelDirectives( - this._parseResults.tokenizerOutput.tokens, configOptions.diagnosticSettings, - useStrict); + this._parseResults.tokenizerOutput.tokens, + configOptions.diagnosticSettings, + useStrict + ); } catch (e) { - const message: string = (e.stack ? e.stack.toString() : undefined) || + const message: string = + (e.stack ? e.stack.toString() : undefined) || (typeof e.message === 'string' ? e.message : undefined) || JSON.stringify(e); - this._console.log( - `An internal error occurred while parsing ${ this.getFilePath() }: ` + message); + this._console.log(`An internal error occurred while parsing ${this.getFilePath()}: ` + message); // Create dummy parse results. this._parseResults = { @@ -503,7 +519,7 @@ export class SourceFile { typeIgnoreLines: {}, predominantEndOfLineSequence: '\n', predominantTabSequence: ' ', - predominantSingleQuoteCharacter: '\'' + predominantSingleQuoteCharacter: "'" }, containsWildcardImport: false }; @@ -525,41 +541,47 @@ export class SourceFile { return true; } - getDefinitionsForPosition(position: Position, - evaluator: TypeEvaluator): DocumentRange[] | undefined { - + getDefinitionsForPosition(position: Position, evaluator: TypeEvaluator): DocumentRange[] | undefined { // If we have no completed analysis job, there's nothing to do. if (!this._parseResults) { return undefined; } - return DefinitionProvider.getDefinitionsForPosition( - this._parseResults, position, evaluator); + return DefinitionProvider.getDefinitionsForPosition(this._parseResults, position, evaluator); } - getReferencesForPosition(position: Position, includeDeclaration: boolean, - evaluator: TypeEvaluator): ReferencesResult | undefined { - + getReferencesForPosition( + position: Position, + includeDeclaration: boolean, + evaluator: TypeEvaluator + ): ReferencesResult | undefined { // If we have no completed analysis job, there's nothing to do. if (!this._parseResults) { return undefined; } - return ReferencesProvider.getReferencesForPosition(this._parseResults, - this._filePath, position, includeDeclaration, evaluator); + return ReferencesProvider.getReferencesForPosition( + this._parseResults, + this._filePath, + position, + includeDeclaration, + evaluator + ); } - addReferences(referencesResult: ReferencesResult, includeDeclaration: boolean, - evaluator: TypeEvaluator): void { - + addReferences(referencesResult: ReferencesResult, includeDeclaration: boolean, evaluator: TypeEvaluator): void { // If we have no completed analysis job, there's nothing to do. if (!this._parseResults) { return; } ReferencesProvider.addReferences( - this._parseResults, this._filePath, referencesResult, includeDeclaration, - evaluator); + this._parseResults, + this._filePath, + referencesResult, + includeDeclaration, + evaluator + ); } addHierarchicalSymbolsForDocument(symbolList: DocumentSymbol[], evaluator: TypeEvaluator) { @@ -568,51 +590,49 @@ export class SourceFile { return; } - DocumentSymbolProvider.addHierarchicalSymbolsForDocument(symbolList, - this._parseResults, evaluator); + DocumentSymbolProvider.addHierarchicalSymbolsForDocument(symbolList, this._parseResults, evaluator); } - addSymbolsForDocument(symbolList: SymbolInformation[], evaluator: TypeEvaluator, - query?: string) { - + addSymbolsForDocument(symbolList: SymbolInformation[], evaluator: TypeEvaluator, query?: string) { // If we have no completed analysis job, there's nothing to do. if (!this._parseResults) { return; } - DocumentSymbolProvider.addSymbolsForDocument(symbolList, query, - this._filePath, this._parseResults, evaluator); + DocumentSymbolProvider.addSymbolsForDocument(symbolList, query, this._filePath, this._parseResults, evaluator); } - getHoverForPosition(position: Position, - evaluator: TypeEvaluator): HoverResults | undefined { - + getHoverForPosition(position: Position, evaluator: TypeEvaluator): HoverResults | undefined { // If this file hasn't been bound, no hover info is available. if (this._isBindingNeeded || !this._parseResults) { return undefined; } - return HoverProvider.getHoverForPosition( - this._parseResults, position, evaluator); + return HoverProvider.getHoverForPosition(this._parseResults, position, evaluator); } - getSignatureHelpForPosition(position: Position, - importLookup: ImportLookup, evaluator: TypeEvaluator): SignatureHelpResults | undefined { - + getSignatureHelpForPosition( + position: Position, + importLookup: ImportLookup, + evaluator: TypeEvaluator + ): SignatureHelpResults | undefined { // If we have no completed analysis job, there's nothing to do. if (!this._parseResults) { return undefined; } - return SignatureHelpProvider.getSignatureHelpForPosition( - this._parseResults, position, evaluator); + return SignatureHelpProvider.getSignatureHelpForPosition(this._parseResults, position, evaluator); } - getCompletionsForPosition(position: Position, - workspacePath: string, configOptions: ConfigOptions, importResolver: ImportResolver, - importLookup: ImportLookup, evaluator: TypeEvaluator, - moduleSymbolsCallback: () => ModuleSymbolMap): CompletionList | undefined { - + getCompletionsForPosition( + position: Position, + workspacePath: string, + configOptions: ConfigOptions, + importResolver: ImportResolver, + importLookup: ImportLookup, + evaluator: TypeEvaluator, + moduleSymbolsCallback: () => ModuleSymbolMap + ): CompletionList | undefined { // If we have no completed analysis job, there's nothing to do. if (!this._parseResults) { return undefined; @@ -625,28 +645,46 @@ export class SourceFile { } const completionProvider = new CompletionProvider( - workspacePath, this._parseResults, this._fileContents, - importResolver, position, - this._filePath, configOptions, importLookup, evaluator, - moduleSymbolsCallback); + workspacePath, + this._parseResults, + this._fileContents, + importResolver, + position, + this._filePath, + configOptions, + importLookup, + evaluator, + moduleSymbolsCallback + ); return completionProvider.getCompletionsForPosition(); } - resolveCompletionItem(configOptions: ConfigOptions, importResolver: ImportResolver, - importLookup: ImportLookup, evaluator: TypeEvaluator, - moduleSymbolsCallback: () => ModuleSymbolMap, completionItem: CompletionItem) { - + resolveCompletionItem( + configOptions: ConfigOptions, + importResolver: ImportResolver, + importLookup: ImportLookup, + evaluator: TypeEvaluator, + moduleSymbolsCallback: () => ModuleSymbolMap, + completionItem: CompletionItem + ) { if (!this._parseResults || this._fileContents === undefined) { return; } const completionData = completionItem.data as CompletionItemData; const completionProvider = new CompletionProvider( - completionData.workspacePath, this._parseResults, this._fileContents, - importResolver, completionData.position, - this._filePath, configOptions, importLookup, evaluator, - moduleSymbolsCallback); + completionData.workspacePath, + this._parseResults, + this._fileContents, + importResolver, + completionData.position, + this._filePath, + configOptions, + importLookup, + evaluator, + moduleSymbolsCallback + ); completionProvider.resolveCompletionItem(completionItem); } @@ -681,8 +719,12 @@ export class SourceFile { timingStats.bindTime.timeOperation(() => { this._cleanParseTreeIfRequired(); - const fileInfo = this._buildFileInfo(configOptions, this._parseResults!.text, - importLookup, builtinsScope); + const fileInfo = this._buildFileInfo( + configOptions, + this._parseResults!.text, + importLookup, + builtinsScope + ); AnalyzerNodeInfo.setFileInfo(this._parseResults!.parseTree, fileInfo); const binder = new Binder(fileInfo); @@ -702,15 +744,16 @@ export class SourceFile { this._moduleSymbolTable = moduleScope!.symbolTable; }); } catch (e) { - const message: string = (e.stack ? e.stack.toString() : undefined) || + const message: string = + (e.stack ? e.stack.toString() : undefined) || (typeof e.message === 'string' ? e.message : undefined) || JSON.stringify(e); this._console.log( - `An internal error occurred while performing name binding for ${ this.getFilePath() }: ` + message); + `An internal error occurred while performing name binding for ${this.getFilePath()}: ` + message + ); const diagSink = new DiagnosticSink(); - diagSink.addError(`An internal error occurred while performing name binding`, - getEmptyRange()); + diagSink.addError(`An internal error occurred while performing name binding`, getEmptyRange()); this._bindDiagnostics = diagSink.fetchAndClear(); } finally { this._isBindingInProgress = false; @@ -739,14 +782,15 @@ export class SourceFile { this._checkerDiagnostics = fileInfo.diagnosticSink.fetchAndClear(); }); } catch (e) { - const message: string = (e.stack ? e.stack.toString() : undefined) || + const message: string = + (e.stack ? e.stack.toString() : undefined) || (typeof e.message === 'string' ? e.message : undefined) || JSON.stringify(e); this._console.log( - `An internal error occurred while while performing type checking for ${ this.getFilePath() }: ` + message); + `An internal error occurred while while performing type checking for ${this.getFilePath()}: ` + message + ); const diagSink = new DiagnosticSink(); - diagSink.addError(`An internal error occurred while performing type checking`, - getEmptyRange()); + diagSink.addError(`An internal error occurred while performing type checking`, getEmptyRange()); // Mark the file as complete so we don't get into an infinite loop. this._isCheckingNeeded = false; @@ -760,9 +804,12 @@ export class SourceFile { this._diagnosticVersion++; } - private _buildFileInfo(configOptions: ConfigOptions, fileContents: string, - importLookup: ImportLookup, builtinsScope?: Scope) { - + private _buildFileInfo( + configOptions: ConfigOptions, + fileContents: string, + importLookup: ImportLookup, + builtinsScope?: Scope + ) { assert(this._parseResults !== undefined); const analysisDiagnostics = new TextRangeDiagnosticSink(this._parseResults!.tokenizerOutput.lines); @@ -796,45 +843,43 @@ export class SourceFile { } } - private _resolveImports(importResolver: ImportResolver, moduleImports: ModuleImport[], - execEnv: ExecutionEnvironment): [ImportResult[], ImportResult?, string?, string?] { - + private _resolveImports( + importResolver: ImportResolver, + moduleImports: ModuleImport[], + execEnv: ExecutionEnvironment + ): [ImportResult[], ImportResult?, string?, string?] { const imports: ImportResult[] = []; // Always include an implicit import of the builtins module. - let builtinsImportResult: ImportResult | undefined = importResolver.resolveImport( - this._filePath, - execEnv, - { - leadingDots: 0, - nameParts: ['builtins'], - importedSymbols: undefined - } - ); + let builtinsImportResult: ImportResult | undefined = importResolver.resolveImport(this._filePath, execEnv, { + leadingDots: 0, + nameParts: ['builtins'], + importedSymbols: undefined + }); // Avoid importing builtins from the builtins.pyi file itself. - if (builtinsImportResult.resolvedPaths.length === 0 || - builtinsImportResult.resolvedPaths[0] !== this.getFilePath()) { + if ( + builtinsImportResult.resolvedPaths.length === 0 || + builtinsImportResult.resolvedPaths[0] !== this.getFilePath() + ) { imports.push(builtinsImportResult); } else { builtinsImportResult = undefined; } // Always include an implicit import of the typing module. - const typingImportResult: ImportResult | undefined = importResolver.resolveImport( - this._filePath, - execEnv, - { - leadingDots: 0, - nameParts: ['typing'], - importedSymbols: undefined - } - ); + const typingImportResult: ImportResult | undefined = importResolver.resolveImport(this._filePath, execEnv, { + leadingDots: 0, + nameParts: ['typing'], + importedSymbols: undefined + }); // Avoid importing typing from the typing.pyi file itself. let typingModulePath: string | undefined; - if (typingImportResult.resolvedPaths.length === 0 || - typingImportResult.resolvedPaths[0] !== this.getFilePath()) { + if ( + typingImportResult.resolvedPaths.length === 0 || + typingImportResult.resolvedPaths[0] !== this.getFilePath() + ) { imports.push(typingImportResult); typingModulePath = typingImportResult.resolvedPaths[0]; } @@ -842,15 +887,11 @@ export class SourceFile { let collectionsModulePath: string | undefined; for (const moduleImport of moduleImports) { - const importResult = importResolver.resolveImport( - this._filePath, - execEnv, - { - leadingDots: moduleImport.leadingDots, - nameParts: moduleImport.nameParts, - importedSymbols: moduleImport.importedSymbols - } - ); + const importResult = importResolver.resolveImport(this._filePath, execEnv, { + leadingDots: moduleImport.leadingDots, + nameParts: moduleImport.nameParts, + importedSymbols: moduleImport.importedSymbols + }); // If the file imports the stdlib 'collections' module, stash // away its file path. The type analyzer may need this to diff --git a/server/src/analyzer/staticExpressions.ts b/server/src/analyzer/staticExpressions.ts index 298ca4c06..c84feb26e 100644 --- a/server/src/analyzer/staticExpressions.ts +++ b/server/src/analyzer/staticExpressions.ts @@ -1,12 +1,12 @@ /* -* staticExpressions.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Collection of static methods that operate on expressions -* (parse node trees). -*/ + * staticExpressions.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Collection of static methods that operate on expressions + * (parse node trees). + */ import { ExecutionEnvironment } from '../common/configOptions'; import { ExpressionNode, NumberNode, ParseNodeType, TupleNode } from '../parser/parseNodes'; @@ -14,9 +14,7 @@ import { KeywordType, OperatorType } from '../parser/tokenizerTypes'; // Returns undefined if the expression cannot be evaluated // statically as a bool value or true/false if it can. -export function evaluateStaticBoolExpression(node: ExpressionNode, - execEnv: ExecutionEnvironment): boolean | undefined { - +export function evaluateStaticBoolExpression(node: ExpressionNode, execEnv: ExecutionEnvironment): boolean | undefined { if (node.nodeType === ParseNodeType.UnaryOperation) { if (node.operator === OperatorType.Or || node.operator === OperatorType.And) { const value = evaluateStaticBoolLikeExpression(node.expression, execEnv); @@ -41,40 +39,42 @@ export function evaluateStaticBoolExpression(node: ExpressionNode, } } - if (_isSysVersionInfoExpression(node.leftExpression) && - node.rightExpression.nodeType === ParseNodeType.Tuple) { - + if (_isSysVersionInfoExpression(node.leftExpression) && node.rightExpression.nodeType === ParseNodeType.Tuple) { // Handle the special case of "sys.version_info >= (3, x)" const comparisonVersion = _convertTupleToVersion(node.rightExpression); - return _evaluateNumericBinaryOperation(node.operator, - execEnv.pythonVersion, comparisonVersion); - - } else if (node.leftExpression.nodeType === ParseNodeType.Index && - _isSysVersionInfoExpression(node.leftExpression.baseExpression) && - node.leftExpression.items.items.length === 1 && - node.leftExpression.items.items[0].nodeType === ParseNodeType.Number && - !node.leftExpression.items.items[0].isImaginary && - node.leftExpression.items.items[0].value === 0 && - node.rightExpression.nodeType === ParseNodeType.Number) { - + return _evaluateNumericBinaryOperation(node.operator, execEnv.pythonVersion, comparisonVersion); + } else if ( + node.leftExpression.nodeType === ParseNodeType.Index && + _isSysVersionInfoExpression(node.leftExpression.baseExpression) && + node.leftExpression.items.items.length === 1 && + node.leftExpression.items.items[0].nodeType === ParseNodeType.Number && + !node.leftExpression.items.items[0].isImaginary && + node.leftExpression.items.items[0].value === 0 && + node.rightExpression.nodeType === ParseNodeType.Number + ) { // Handle the special case of "sys.version_info[0] >= X" - return _evaluateNumericBinaryOperation(node.operator, - Math.floor(execEnv.pythonVersion / 256), node.rightExpression.value); - } else if (_isSysPlatformInfoExpression(node.leftExpression) && - node.rightExpression.nodeType === ParseNodeType.StringList) { + return _evaluateNumericBinaryOperation( + node.operator, + Math.floor(execEnv.pythonVersion / 256), + node.rightExpression.value + ); + } else if ( + _isSysPlatformInfoExpression(node.leftExpression) && + node.rightExpression.nodeType === ParseNodeType.StringList + ) { // Handle the special case of "sys.platform != 'X'" const comparisonPlatform = node.rightExpression.strings.map(s => s.value).join(''); const expectedPlatformName = _getExpectedPlatformNameFromPlatform(execEnv); - return _evaluateStringBinaryOperation(node.operator, - expectedPlatformName || '', comparisonPlatform); - } else if (_isOsNameInfoExpression(node.leftExpression) && - node.rightExpression.nodeType === ParseNodeType.StringList) { + return _evaluateStringBinaryOperation(node.operator, expectedPlatformName || '', comparisonPlatform); + } else if ( + _isOsNameInfoExpression(node.leftExpression) && + node.rightExpression.nodeType === ParseNodeType.StringList + ) { // Handle the special case of "os.name == 'X'" const comparisonOsName = node.rightExpression.strings.map(s => s.value).join(''); const expectedOsName = _getExpectedOsNameFromPlatform(execEnv); if (expectedOsName !== undefined) { - return _evaluateStringBinaryOperation(node.operator, - expectedOsName, comparisonOsName); + return _evaluateStringBinaryOperation(node.operator, expectedOsName, comparisonOsName); } } } else if (node.nodeType === ParseNodeType.Constant) { @@ -87,11 +87,12 @@ export function evaluateStaticBoolExpression(node: ExpressionNode, if (node.value === 'TYPE_CHECKING') { return true; } - } else if (node.nodeType === ParseNodeType.MemberAccess && - node.memberName.value === 'TYPE_CHECKING' && - node.leftExpression.nodeType === ParseNodeType.Name && - node.leftExpression.value === 'typing') { - + } else if ( + node.nodeType === ParseNodeType.MemberAccess && + node.memberName.value === 'TYPE_CHECKING' && + node.leftExpression.nodeType === ParseNodeType.Name && + node.leftExpression.value === 'typing' + ) { return true; } @@ -101,9 +102,10 @@ export function evaluateStaticBoolExpression(node: ExpressionNode, // Similar to evaluateStaticBoolExpression except that it handles // other non-bool values that are statically falsy or truthy // (like "None"). -export function evaluateStaticBoolLikeExpression(node: ExpressionNode, - execEnv: ExecutionEnvironment): boolean | undefined { - +export function evaluateStaticBoolLikeExpression( + node: ExpressionNode, + execEnv: ExecutionEnvironment +): boolean | undefined { if (node.nodeType === ParseNodeType.Constant) { if (node.constType === KeywordType.None) { return false; @@ -116,11 +118,12 @@ export function evaluateStaticBoolLikeExpression(node: ExpressionNode, function _convertTupleToVersion(node: TupleNode): number | undefined { let comparisonVersion: number | undefined; if (node.expressions.length === 2) { - if (node.expressions[0].nodeType === ParseNodeType.Number && - !node.expressions[0].isImaginary && - node.expressions[1].nodeType === ParseNodeType.Number && - !node.expressions[1].isImaginary) { - + if ( + node.expressions[0].nodeType === ParseNodeType.Number && + !node.expressions[0].isImaginary && + node.expressions[1].nodeType === ParseNodeType.Number && + !node.expressions[1].isImaginary + ) { const majorVersion = node.expressions[0]; const minorVersion = node.expressions[1]; comparisonVersion = majorVersion.value * 256 + minorVersion.value; @@ -133,8 +136,11 @@ function _convertTupleToVersion(node: TupleNode): number | undefined { return comparisonVersion; } -function _evaluateNumericBinaryOperation(operatorType: OperatorType, leftValue: number | undefined, - rightValue: number | undefined): any | undefined { +function _evaluateNumericBinaryOperation( + operatorType: OperatorType, + leftValue: number | undefined, + rightValue: number | undefined +): any | undefined { if (leftValue !== undefined && rightValue !== undefined) { if (operatorType === OperatorType.LessThan) { return leftValue < rightValue; @@ -154,8 +160,11 @@ function _evaluateNumericBinaryOperation(operatorType: OperatorType, leftValue: return undefined; } -function _evaluateStringBinaryOperation(operatorType: OperatorType, - leftValue: string | undefined, rightValue: string | undefined): any | undefined { +function _evaluateStringBinaryOperation( + operatorType: OperatorType, + leftValue: string | undefined, + rightValue: string | undefined +): any | undefined { if (leftValue !== undefined && rightValue !== undefined) { if (operatorType === OperatorType.Equals) { return leftValue === rightValue; @@ -169,9 +178,11 @@ function _evaluateStringBinaryOperation(operatorType: OperatorType, function _isSysVersionInfoExpression(node: ExpressionNode): boolean { if (node.nodeType === ParseNodeType.MemberAccess) { - if (node.leftExpression.nodeType === ParseNodeType.Name && - node.leftExpression.value === 'sys' && - node.memberName.value === 'version_info') { + if ( + node.leftExpression.nodeType === ParseNodeType.Name && + node.leftExpression.value === 'sys' && + node.memberName.value === 'version_info' + ) { return true; } } @@ -181,9 +192,11 @@ function _isSysVersionInfoExpression(node: ExpressionNode): boolean { function _isSysPlatformInfoExpression(node: ExpressionNode): boolean { if (node.nodeType === ParseNodeType.MemberAccess) { - if (node.leftExpression.nodeType === ParseNodeType.Name && - node.leftExpression.value === 'sys' && - node.memberName.value === 'platform') { + if ( + node.leftExpression.nodeType === ParseNodeType.Name && + node.leftExpression.value === 'sys' && + node.memberName.value === 'platform' + ) { return true; } } @@ -193,9 +206,11 @@ function _isSysPlatformInfoExpression(node: ExpressionNode): boolean { function _isOsNameInfoExpression(node: ExpressionNode): boolean { if (node.nodeType === ParseNodeType.MemberAccess) { - if (node.leftExpression.nodeType === ParseNodeType.Name && - node.leftExpression.value === 'os' && - node.memberName.value === 'name') { + if ( + node.leftExpression.nodeType === ParseNodeType.Name && + node.leftExpression.value === 'os' && + node.memberName.value === 'name' + ) { return true; } } diff --git a/server/src/analyzer/symbol.ts b/server/src/analyzer/symbol.ts index 3925de460..215916e1f 100644 --- a/server/src/analyzer/symbol.ts +++ b/server/src/analyzer/symbol.ts @@ -1,13 +1,13 @@ /* -* symbol.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Represents an association between a name and the type -* (or multiple types) that the symbol is associated with -* in the program. -*/ + * symbol.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Represents an association between a name and the type + * (or multiple types) that the symbol is associated with + * in the program. + */ import { Declaration, DeclarationType } from './declaration'; import { areDeclarationsSame, hasTypeForDeclaration } from './declarationUtils'; @@ -129,8 +129,7 @@ export class Symbol { // See if this node was already identified as a declaration. If so, // replace it. Otherwise, add it as a new declaration to the end of // the list. - const declIndex = this._declarations.findIndex( - decl => areDeclarationsSame(decl, declaration)); + const declIndex = this._declarations.findIndex(decl => areDeclarationsSame(decl, declaration)); if (declIndex < 0) { this._declarations.push(declaration); } else { @@ -139,9 +138,7 @@ export class Symbol { const curDecl = this._declarations[declIndex]; if (hasTypeForDeclaration(declaration)) { this._declarations[declIndex] = declaration; - if (curDecl.type === DeclarationType.Variable && - declaration.type === DeclarationType.Variable) { - + if (curDecl.type === DeclarationType.Variable && declaration.type === DeclarationType.Variable) { if (!declaration.inferredTypeSource && curDecl.inferredTypeSource) { declaration.inferredTypeSource = curDecl.inferredTypeSource; } @@ -180,13 +177,11 @@ export class Symbol { return true; } - return this.getDeclarations().some( - decl => hasTypeForDeclaration(decl)); + return this.getDeclarations().some(decl => hasTypeForDeclaration(decl)); } getTypedDeclarations() { - return this.getDeclarations().filter( - decl => hasTypeForDeclaration(decl)); + return this.getDeclarations().filter(decl => hasTypeForDeclaration(decl)); } getSynthesizedType() { diff --git a/server/src/analyzer/symbolNameUtils.ts b/server/src/analyzer/symbolNameUtils.ts index ed39cadf2..086480de5 100644 --- a/server/src/analyzer/symbolNameUtils.ts +++ b/server/src/analyzer/symbolNameUtils.ts @@ -1,27 +1,23 @@ /* -* symbolNameUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Static methods that apply to symbols or symbol names. -*/ + * symbolNameUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Static methods that apply to symbols or symbol names. + */ const _constantRegEx = /^[A-Z0-9_]+$/; const _underscoreOnlyRegEx = /^[_]+$/; // Private symbol names start with a double underscore. export function isPrivateName(name: string) { - return name.length > 2 && - name.startsWith('__') && - !name.endsWith('__'); + return name.length > 2 && name.startsWith('__') && !name.endsWith('__'); } // Protected symbol names start with a single underscore. export function isProtectedName(name: string) { - return name.length > 1 && - name.startsWith('_') && - !name.startsWith('__'); + return name.length > 1 && name.startsWith('_') && !name.startsWith('__'); } export function isPrivateOrProtectedName(name: string) { @@ -30,9 +26,7 @@ export function isPrivateOrProtectedName(name: string) { // "Dunder" names start and end with two underscores. export function isDunderName(name: string) { - return name.length > 4 && - name.startsWith('__') && - name.endsWith('__'); + return name.length > 4 && name.startsWith('__') && name.endsWith('__'); } // Constants are all-caps with possible numbers and underscores. diff --git a/server/src/analyzer/symbolUtils.ts b/server/src/analyzer/symbolUtils.ts index fc87a6649..11eb268d5 100644 --- a/server/src/analyzer/symbolUtils.ts +++ b/server/src/analyzer/symbolUtils.ts @@ -1,11 +1,11 @@ /* -* symbolUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Collection of functions that operate on Symbol objects. -*/ + * symbolUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Collection of functions that operate on Symbol objects. + */ import { Declaration, DeclarationType } from './declaration'; import { isFinalVariableDeclaration } from './declarationUtils'; diff --git a/server/src/analyzer/testWalker.ts b/server/src/analyzer/testWalker.ts index ea066a714..d4068d3af 100644 --- a/server/src/analyzer/testWalker.ts +++ b/server/src/analyzer/testWalker.ts @@ -1,8 +1,8 @@ /* -* testWalker.ts -* -* Walks a parse tree to validate internal consistency and completeness. -*/ + * testWalker.ts + * + * Walks a parse tree to validate internal consistency and completeness. + */ import { ParseTreeWalker } from '../analyzer/parseTreeWalker'; import { fail } from '../common/debug'; @@ -27,8 +27,9 @@ export class TestWalker extends ParseTreeWalker { children.forEach(child => { if (child) { if (child.parent !== node) { - fail(`Child node ${ child.nodeType } does not ` + - `contain a reference to its parent ${ node.nodeType }`); + fail( + `Child node ${child.nodeType} does not ` + `contain a reference to its parent ${node.nodeType}` + ); } } }); @@ -62,8 +63,7 @@ export class TestWalker extends ParseTreeWalker { if (!skipCheck) { // Make sure the child is contained within the parent. if (child.start < node.start || TextRange.getEnd(child) > TextRange.getEnd(node)) { - fail(`Child node ${ child.nodeType } is not ` + - `contained within its parent ${ node.nodeType }`); + fail(`Child node ${child.nodeType} is not ` + `contained within its parent ${node.nodeType}`); } if (prevNode) { // Make sure the child is after the previous child. diff --git a/server/src/analyzer/typeEvaluator.ts b/server/src/analyzer/typeEvaluator.ts index 63eaceb65..56396395f 100644 --- a/server/src/analyzer/typeEvaluator.ts +++ b/server/src/analyzer/typeEvaluator.ts @@ -1,18 +1,18 @@ /* -* typeEvaluator.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Module that evaluates types of parse tree nodes within -* a program. -* -* Note: This is a gargantuan module - much larger than I would -* normally create. It is written this way primarily for performance, -* with the internal methods having access to the full closure of -* the createTypeEvaluator function. This is the same approach -* taken by the TypeScript compiler. -*/ + * typeEvaluator.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Module that evaluates types of parse tree nodes within + * a program. + * + * Note: This is a gargantuan module - much larger than I would + * normally create. It is written this way primarily for performance, + * with the internal methods having access to the full closure of + * the createTypeEvaluator function. This is the same approach + * taken by the TypeScript compiler. + */ import { Commands } from '../commands/commands'; import { DiagnosticLevel } from '../common/configOptions'; @@ -23,22 +23,72 @@ import { convertOffsetsToRange } from '../common/positionUtils'; import { PythonVersion } from '../common/pythonVersion'; import { getEmptyRange } from '../common/textRange'; import { TextRange } from '../common/textRange'; -import { ArgumentCategory, AssignmentNode, AugmentedAssignmentNode, BinaryOperationNode, - CallNode, ClassNode, ConstantNode, DecoratorNode, DictionaryNode, ExceptNode, ExpressionNode, - ForNode, FunctionNode, ImportAsNode, ImportFromAsNode, ImportFromNode, IndexItemsNode, - IndexNode, isExpressionNode, LambdaNode, ListComprehensionNode, ListNode, MemberAccessNode, - NameNode, ParameterCategory, ParameterNode, ParseNode, ParseNodeType, SetNode, - SliceNode, StringListNode, TernaryNode, TupleNode, TypeAnnotationNode, UnaryOperationNode, - WithItemNode, YieldFromNode, YieldNode } from '../parser/parseNodes'; +import { + ArgumentCategory, + AssignmentNode, + AugmentedAssignmentNode, + BinaryOperationNode, + CallNode, + ClassNode, + ConstantNode, + DecoratorNode, + DictionaryNode, + ExceptNode, + ExpressionNode, + ForNode, + FunctionNode, + ImportAsNode, + ImportFromAsNode, + ImportFromNode, + IndexItemsNode, + IndexNode, + isExpressionNode, + LambdaNode, + ListComprehensionNode, + ListNode, + MemberAccessNode, + NameNode, + ParameterCategory, + ParameterNode, + ParseNode, + ParseNodeType, + SetNode, + SliceNode, + StringListNode, + TernaryNode, + TupleNode, + TypeAnnotationNode, + UnaryOperationNode, + WithItemNode, + YieldFromNode, + YieldNode +} from '../parser/parseNodes'; import { ParseOptions, Parser } from '../parser/parser'; import { KeywordType, OperatorType, StringTokenFlags } from '../parser/tokenizerTypes'; import { AnalyzerFileInfo, ImportLookup, ImportLookupResult } from './analyzerFileInfo'; import * as AnalyzerNodeInfo from './analyzerNodeInfo'; -import { createKeyForReference, FlowAssignment, FlowAssignmentAlias, FlowCall, FlowCondition, FlowFlags, - FlowLabel, FlowNode, FlowPostFinally, FlowPreFinallyGate, FlowWildcardImport, - isCodeFlowSupportedForReference } from './codeFlow'; -import { AliasDeclaration, Declaration, DeclarationType, FunctionDeclaration, - ModuleLoaderActions, VariableDeclaration } from './declaration'; +import { + createKeyForReference, + FlowAssignment, + FlowAssignmentAlias, + FlowCall, + FlowCondition, + FlowFlags, + FlowLabel, + FlowNode, + FlowPostFinally, + FlowPreFinallyGate, + FlowWildcardImport, + isCodeFlowSupportedForReference +} from './codeFlow'; +import { + AliasDeclaration, + Declaration, + DeclarationType, + FunctionDeclaration, + ModuleLoaderActions, + VariableDeclaration +} from './declaration'; import * as ParseTreeUtils from './parseTreeUtils'; import { ScopeType } from './scope'; import * as ScopeUtils from './scopeUtils'; @@ -46,22 +96,80 @@ import { evaluateStaticBoolExpression } from './staticExpressions'; import { indeterminateSymbolId, Symbol, SymbolFlags } from './symbol'; import { isConstantName, isPrivateOrProtectedName } from './symbolNameUtils'; import { getLastTypedDeclaredForSymbol, isFinalVariable } from './symbolUtils'; -import { AnyType, ClassType, ClassTypeFlags, combineTypes, DataClassEntry,FunctionParameter, - FunctionType, FunctionTypeFlags, InheritanceChain, isAnyOrUnknown, isNoneOrNever, - isPossiblyUnbound, isSameWithoutLiteralValue, isTypeSame, isUnbound, LiteralValue, - maxTypeRecursionCount, ModuleType, NeverType, NoneType, ObjectType, - OverloadedFunctionType, removeNoneFromUnion, removeUnboundFromUnion, Type, TypeCategory, - TypeSourceId, TypeVarType, UnboundType, UnknownType } from './types'; -import { addDefaultFunctionParameters, addTypeVarsToListIfUnique, - areTypesSame, buildTypeVarMap, buildTypeVarMapFromSpecializedClass, CanAssignFlags, canBeFalsy, - canBeTruthy, ClassMember, ClassMemberLookupFlags, containsUnknown, convertClassToObject, - derivesFromClassRecursive, doForSubtypes, getConcreteTypeFromTypeVar, getDeclaredGeneratorReturnType, - getDeclaredGeneratorSendType, getMetaclass, getSpecializedTupleType, getTypeVarArgumentsRecursive, - isEllipsisType, isNoReturnType, isOptionalType, isProperty, lookUpClassMember, lookUpObjectMember, - partiallySpecializeType, printLiteralType, printLiteralValue, removeFalsinessFromType, - removeTruthinessFromType, requiresSpecialization, selfSpecializeClassType, setTypeArgumentsRecursive, - specializeType, stripFirstParameter, stripLiteralTypeArgsValue, stripLiteralValue, - transformTypeObjectToClass, TypedDictEntry } from './typeUtils'; +import { + AnyType, + ClassType, + ClassTypeFlags, + combineTypes, + DataClassEntry, + FunctionParameter, + FunctionType, + FunctionTypeFlags, + InheritanceChain, + isAnyOrUnknown, + isNoneOrNever, + isPossiblyUnbound, + isSameWithoutLiteralValue, + isTypeSame, + isUnbound, + LiteralValue, + maxTypeRecursionCount, + ModuleType, + NeverType, + NoneType, + ObjectType, + OverloadedFunctionType, + removeNoneFromUnion, + removeUnboundFromUnion, + Type, + TypeCategory, + TypeSourceId, + TypeVarType, + UnboundType, + UnknownType +} from './types'; +import { + addDefaultFunctionParameters, + addTypeVarsToListIfUnique, + areTypesSame, + buildTypeVarMap, + buildTypeVarMapFromSpecializedClass, + CanAssignFlags, + canBeFalsy, + canBeTruthy, + ClassMember, + ClassMemberLookupFlags, + containsUnknown, + convertClassToObject, + derivesFromClassRecursive, + doForSubtypes, + getConcreteTypeFromTypeVar, + getDeclaredGeneratorReturnType, + getDeclaredGeneratorSendType, + getMetaclass, + getSpecializedTupleType, + getTypeVarArgumentsRecursive, + isEllipsisType, + isNoReturnType, + isOptionalType, + isProperty, + lookUpClassMember, + lookUpObjectMember, + partiallySpecializeType, + printLiteralType, + printLiteralValue, + removeFalsinessFromType, + removeTruthinessFromType, + requiresSpecialization, + selfSpecializeClassType, + setTypeArgumentsRecursive, + specializeType, + stripFirstParameter, + stripLiteralTypeArgsValue, + stripLiteralValue, + transformTypeObjectToClass, + TypedDictEntry +} from './typeUtils'; import { TypeVarMap } from './typeVarMap'; interface TypeResult { @@ -251,8 +359,12 @@ export interface TypeEvaluator { getDeclarationsForNameNode: (node: NameNode) => Declaration[] | undefined; getTypeForDeclaration: (declaration: Declaration) => Type | undefined; resolveAliasDeclaration: (declaration: Declaration) => Declaration | undefined; - getTypeFromIterable: (type: Type, isAsync: boolean, - errorNode: ParseNode | undefined, supportGetItem: boolean) => Type; + getTypeFromIterable: ( + type: Type, + isAsync: boolean, + errorNode: ParseNode | undefined, + supportGetItem: boolean + ) => Type; getTypedDictMembersForClass: (classType: ClassType) => Map; getEffectiveTypeOfSymbol: (symbol: Symbol) => Type; @@ -260,23 +372,36 @@ export interface TypeEvaluator { getFunctionInferredReturnType: (type: FunctionType) => Type; getBuiltInType: (node: ParseNode, name: string) => Type; getTypeOfMember: (member: ClassMember) => Type; - bindFunctionToClassOrObject: (baseType: ClassType | ObjectType | undefined, - memberType: Type, treatAsClassMember: boolean) => Type; - getBoundMethod: (classType: ClassType, memberName: string, treatAsClassMember: boolean) => - FunctionType | OverloadedFunctionType | undefined; + bindFunctionToClassOrObject: ( + baseType: ClassType | ObjectType | undefined, + memberType: Type, + treatAsClassMember: boolean + ) => Type; + getBoundMethod: ( + classType: ClassType, + memberName: string, + treatAsClassMember: boolean + ) => FunctionType | OverloadedFunctionType | undefined; getCallSignatureInfo: (node: ParseNode, insertionOffset: number) => CallSignatureInfo | undefined; - canAssignType: (destType: Type, srcType: Type, diag: DiagnosticAddendum, - typeVarMap?: TypeVarMap) => boolean; - canOverrideMethod: (baseMethod: Type, overrideMethod: FunctionType, - diag: DiagnosticAddendum) => boolean; + canAssignType: (destType: Type, srcType: Type, diag: DiagnosticAddendum, typeVarMap?: TypeVarMap) => boolean; + canOverrideMethod: (baseMethod: Type, overrideMethod: FunctionType, diag: DiagnosticAddendum) => boolean; addError: (message: string, node: ParseNode) => Diagnostic | undefined; addWarning: (message: string, node: ParseNode) => Diagnostic | undefined; - addDiagnostic: (diagLevel: DiagnosticLevel, rule: string, - message: string, node: ParseNode) => Diagnostic | undefined; - addDiagnosticForTextRange: (fileInfo: AnalyzerFileInfo, diagLevel: DiagnosticLevel, - rule: string, message: string, range: TextRange) => Diagnostic | undefined; + addDiagnostic: ( + diagLevel: DiagnosticLevel, + rule: string, + message: string, + node: ParseNode + ) => Diagnostic | undefined; + addDiagnosticForTextRange: ( + fileInfo: AnalyzerFileInfo, + diagLevel: DiagnosticLevel, + rule: string, + message: string, + range: TextRange + ) => Diagnostic | undefined; printType: (type: Type) => string; printFunctionParts: (type: FunctionType) => [string[], string]; @@ -285,8 +410,11 @@ export interface TypeEvaluator { } interface CodeFlowAnalyzer { - getTypeFromCodeFlow: (reference: NameNode | MemberAccessNode, - targetSymbolId: number, initialType: Type | undefined) => FlowNodeTypeResult; + getTypeFromCodeFlow: ( + reference: NameNode | MemberAccessNode, + targetSymbolId: number, + initialType: Type | undefined + ) => FlowNodeTypeResult; } type CachedType = Type | PartialType; @@ -374,7 +502,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!isSpeculativeMode()) { return undefined; } - + const partialType = cachedType as PartialType; // If the cached type was written as part of an older @@ -391,11 +519,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } function writeTypeCache(node: ParseNode, type: Type) { - const cachedType = !isSpeculativeMode() ? type : { - category: partialTypeCategory, - speculativeModeId, - type - }; + const cachedType = !isSpeculativeMode() + ? type + : { + category: partialTypeCategory, + speculativeModeId, + type + }; // Should we use a temporary cache associated with a contextual // analysis of a function, contextualized based on call-site argument types? @@ -438,7 +568,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { function getIndexOfSymbolResolution(symbol: Symbol, declaration: Declaration) { return symbolResolutionStack.findIndex( - entry => entry.symbolId === symbol.id && entry.declaration === declaration); + entry => entry.symbolId === symbol.id && entry.declaration === declaration + ); } function pushSymbolResolution(symbol: Symbol, declaration: Declaration) { @@ -499,9 +630,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return readTypeCache(node); } - function getTypeOfExpression(node: ExpressionNode, expectedType?: Type, - flags = EvaluatorFlags.None): TypeResult { - + function getTypeOfExpression(node: ExpressionNode, expectedType?: Type, flags = EvaluatorFlags.None): TypeResult { // Is this type already cached? const cachedType = readTypeCache(node); if (cachedType) { @@ -542,25 +671,31 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case ParseNodeType.StringList: { - const expectingType = (flags & EvaluatorFlags.EvaluateStringLiteralAsType) !== 0 && - !isAnnotationLiteralValue(node); + const expectingType = + (flags & EvaluatorFlags.EvaluateStringLiteralAsType) !== 0 && !isAnnotationLiteralValue(node); if (expectingType) { if (node.typeAnnotation) { - typeResult = getTypeOfExpression(node.typeAnnotation, undefined, - flags | EvaluatorFlags.AllowForwardReferences); + typeResult = getTypeOfExpression( + node.typeAnnotation, + undefined, + flags | EvaluatorFlags.AllowForwardReferences + ); } else if (!node.typeAnnotation && node.strings.length === 1) { // We didn't know at parse time that this string node was going // to be evaluated as a forward-referenced type. We need // to re-invoke the parser at this stage. const expr = parseStringAsTypeAnnotation(node); if (expr) { - typeResult = getTypeOfExpression(expr, undefined, - flags | EvaluatorFlags.AllowForwardReferences); + typeResult = getTypeOfExpression( + expr, + undefined, + flags | EvaluatorFlags.AllowForwardReferences + ); } } } - + if (expectingType) { if (!typeResult) { addError(`Expecting type but received a string literal`, node); @@ -587,9 +722,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } else { typeResult = { node, - type: cloneBuiltinTypeWithLiteral(node, - isBytes ? 'bytes' : 'str', node.strings.map(s => s.value).join('')) - } + type: cloneBuiltinTypeWithLiteral( + node, + isBytes ? 'bytes' : 'str', + node.strings.map(s => s.value).join('') + ) + }; } } break; @@ -610,8 +748,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if ((flags & EvaluatorFlags.ConvertEllipsisToAny) !== 0) { typeResult = { type: AnyType.create(true), node }; } else { - const ellipsisType = getBuiltInType(node, 'ellipsis') || - AnyType.create(); + const ellipsisType = getBuiltInType(node, 'ellipsis') || AnyType.create(); typeResult = { type: ellipsisType, node }; } break; @@ -708,8 +845,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case ParseNodeType.TypeAnnotation: { - typeResult = getTypeOfExpression(node.typeAnnotation, undefined, - EvaluatorFlags.EvaluateStringLiteralAsType); + typeResult = getTypeOfExpression( + node.typeAnnotation, + undefined, + EvaluatorFlags.EvaluateStringLiteralAsType + ); break; } @@ -728,7 +868,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!typeResult) { // We shouldn't get here. If we do, report an error. - fail(`Unhandled expression type '${ ParseTreeUtils.printExpression(node) }'`); + fail(`Unhandled expression type '${ParseTreeUtils.printExpression(node)}'`); typeResult = { type: UnknownType.create(), node }; } @@ -750,12 +890,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - let evaluatorFlags = EvaluatorFlags.ConvertEllipsisToAny | - EvaluatorFlags.EvaluateStringLiteralAsType; + let evaluatorFlags = EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType; const isAnnotationEvaluationPostponed = - fileInfo.futureImports.get('annotations') !== undefined || - fileInfo.isStubFile; + fileInfo.futureImports.get('annotations') !== undefined || fileInfo.isStubFile; if (isAnnotationEvaluationPostponed) { evaluatorFlags |= EvaluatorFlags.AllowForwardReferences; @@ -765,8 +903,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } function getTypeFromDecorator(node: DecoratorNode, functionOrClassType: Type): Type { - const baseTypeResult = getTypeOfExpression( - node.leftExpression, undefined, EvaluatorFlags.DoNotSpecialize); + const baseTypeResult = getTypeOfExpression(node.leftExpression, undefined, EvaluatorFlags.DoNotSpecialize); let decoratorCall = baseTypeResult; @@ -785,17 +922,28 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // because these errors will already be reported as unknown // parameters. decoratorCall = getTypeFromCallWithBaseType( - node.leftExpression, argList, decoratorCall, - undefined, EvaluatorFlags.DoNotCheckForUnknownArgs); + node.leftExpression, + argList, + decoratorCall, + undefined, + EvaluatorFlags.DoNotCheckForUnknownArgs + ); } - const argList = [{ - argumentCategory: ArgumentCategory.Simple, - type: functionOrClassType - }]; + const argList = [ + { + argumentCategory: ArgumentCategory.Simple, + type: functionOrClassType + } + ]; - return getTypeFromCallWithBaseType(node.leftExpression, argList, decoratorCall, - undefined, EvaluatorFlags.DoNotCheckForUnknownArgs).type; + return getTypeFromCallWithBaseType( + node.leftExpression, + argList, + decoratorCall, + undefined, + EvaluatorFlags.DoNotCheckForUnknownArgs + ).type; } // Gets a member type from an object and if it's a function binds @@ -803,16 +951,30 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // using the objectType parameter. Callers can specify these separately // to handle the case where we're fetching the object member from a // metaclass but binding to the class. - function getTypeFromObjectMember(errorNode: ExpressionNode, objectType: ObjectType, memberName: string, - usage: EvaluatorUsage, diag: DiagnosticAddendum, memberAccessFlags = MemberAccessFlags.None, - bindToClass?: ClassType): Type | undefined { - - const memberInfo = getTypeFromClassMemberName(errorNode, - objectType.classType, memberName, usage, diag, memberAccessFlags); + function getTypeFromObjectMember( + errorNode: ExpressionNode, + objectType: ObjectType, + memberName: string, + usage: EvaluatorUsage, + diag: DiagnosticAddendum, + memberAccessFlags = MemberAccessFlags.None, + bindToClass?: ClassType + ): Type | undefined { + const memberInfo = getTypeFromClassMemberName( + errorNode, + objectType.classType, + memberName, + usage, + diag, + memberAccessFlags + ); let resultType = memberInfo ? memberInfo.type : undefined; if (resultType) { - if (resultType.category === TypeCategory.Function || resultType.category === TypeCategory.OverloadedFunction) { + if ( + resultType.category === TypeCategory.Function || + resultType.category === TypeCategory.OverloadedFunction + ) { if (memberInfo!.isClassMember) { resultType = bindFunctionToClassOrObject(bindToClass || objectType, resultType, !!bindToClass); } @@ -824,15 +986,29 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Gets a member type from a class and if it's a function binds // it to the object. - function getTypeFromClassMember(errorNode: ExpressionNode, classType: ClassType, memberName: string, - usage: EvaluatorUsage, diag: DiagnosticAddendum, memberAccessFlags = MemberAccessFlags.None): Type | undefined { - - const memberInfo = getTypeFromClassMemberName(errorNode, - classType, memberName, usage, diag, memberAccessFlags | MemberAccessFlags.SkipInstanceMembers); + function getTypeFromClassMember( + errorNode: ExpressionNode, + classType: ClassType, + memberName: string, + usage: EvaluatorUsage, + diag: DiagnosticAddendum, + memberAccessFlags = MemberAccessFlags.None + ): Type | undefined { + const memberInfo = getTypeFromClassMemberName( + errorNode, + classType, + memberName, + usage, + diag, + memberAccessFlags | MemberAccessFlags.SkipInstanceMembers + ); let resultType = memberInfo ? memberInfo.type : undefined; if (resultType) { - if (resultType.category === TypeCategory.Function || resultType.category === TypeCategory.OverloadedFunction) { + if ( + resultType.category === TypeCategory.Function || + resultType.category === TypeCategory.OverloadedFunction + ) { if (memberInfo!.isClassMember) { resultType = bindFunctionToClassOrObject(classType, resultType); } @@ -842,29 +1018,39 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return resultType; } - function getBoundMethod(classType: ClassType, memberName: string, treatAsClassMember: boolean): - FunctionType | OverloadedFunctionType | undefined { - + function getBoundMethod( + classType: ClassType, + memberName: string, + treatAsClassMember: boolean + ): FunctionType | OverloadedFunctionType | undefined { const aliasClass = classType.details.aliasClass; if (aliasClass) { classType = aliasClass; } - const memberInfo = lookUpClassMember(classType, memberName, - importLookup, ClassMemberLookupFlags.SkipInstanceVariables | - ClassMemberLookupFlags.SkipObjectBaseClass); + const memberInfo = lookUpClassMember( + classType, + memberName, + importLookup, + ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + ); if (memberInfo) { const unboundMethodType = getTypeOfMember(memberInfo); - if (unboundMethodType.category === TypeCategory.Function || - unboundMethodType.category === TypeCategory.OverloadedFunction) { - + if ( + unboundMethodType.category === TypeCategory.Function || + unboundMethodType.category === TypeCategory.OverloadedFunction + ) { const boundMethod = bindFunctionToClassOrObject( - ObjectType.create(classType), unboundMethodType, treatAsClassMember); - - if (boundMethod.category === TypeCategory.Function || - boundMethod.category === TypeCategory.OverloadedFunction) { + ObjectType.create(classType), + unboundMethodType, + treatAsClassMember + ); + if ( + boundMethod.category === TypeCategory.Function || + boundMethod.category === TypeCategory.OverloadedFunction + ) { return boundMethod; } } @@ -940,9 +1126,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { case TypeCategory.OverloadedFunction: { addFunctionToSignature(subtype); break; - } - + case TypeCategory.Class: { // Try to get the __new__ method first. We skip the base "object", // which typically provides the __new__ method. We'll fall back on @@ -952,7 +1137,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Skip the __new__ lookup for data classes, which always have a // generic synthesized new method. if (!ClassType.isDataClass(subtype)) { - methodType = getBoundMethod(subtype, '__new__', true); + methodType = getBoundMethod(subtype, '__new__', true); } if (!methodType) { methodType = getBoundMethod(subtype, '__init__', false); @@ -964,8 +1149,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case TypeCategory.Object: { - const methodType = getBoundMethod( - subtype.classType, '__call__', false); + const methodType = getBoundMethod(subtype.classType, '__call__', false); if (methodType) { addFunctionToSignature(methodType); } @@ -985,7 +1169,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { signatures, activeArgumentIndex, activeArgumentName - } + }; } // Determines whether the specified expression is a symbol with a declared type @@ -996,8 +1180,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { switch (expression.nodeType) { case ParseNodeType.Name: { - const symbolWithScope = lookUpSymbolRecursive( - expression, expression.value); + const symbolWithScope = lookUpSymbolRecursive(expression, expression.value); if (symbolWithScope) { symbol = symbolWithScope.symbol; } @@ -1013,15 +1196,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let classMemberInfo: ClassMember | undefined; if (baseType.category === TypeCategory.Object) { - classMemberInfo = lookUpObjectMember(baseType, - expression.memberName.value, importLookup, - ClassMemberLookupFlags.DeclaredTypesOnly); + classMemberInfo = lookUpObjectMember( + baseType, + expression.memberName.value, + importLookup, + ClassMemberLookupFlags.DeclaredTypesOnly + ); classOrObjectBase = baseType; } else if (baseType.category === TypeCategory.Class) { - classMemberInfo = lookUpClassMember(baseType, - expression.memberName.value, importLookup, - ClassMemberLookupFlags.SkipInstanceVariables | - ClassMemberLookupFlags.DeclaredTypesOnly); + classMemberInfo = lookUpClassMember( + baseType, + expression.memberName.value, + importLookup, + ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.DeclaredTypesOnly + ); classOrObjectBase = baseType; } @@ -1034,8 +1222,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { case ParseNodeType.Index: { const baseType = getDeclaredTypeForExpression(expression.baseExpression); if (baseType && baseType.category === TypeCategory.Object) { - const setItemMember = lookUpClassMember(baseType.classType, - '__setitem__', importLookup); + const setItemMember = lookUpClassMember(baseType.classType, '__setitem__', importLookup); if (setItemMember) { const setItemType = getTypeOfMember(setItemMember); if (setItemType.category === TypeCategory.Function) { @@ -1059,10 +1246,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (isProperty(declaredType)) { const setterInfo = lookUpClassMember((declaredType as ObjectType).classType, 'fset', importLookup); const setter = setterInfo ? getTypeOfMember(setterInfo) : undefined; - if (!setter || - setter.category !== TypeCategory.Function || - setter.details.parameters.length < 2) { - + if (!setter || setter.category !== TypeCategory.Function || setter.details.parameters.length < 2) { return undefined; } @@ -1098,20 +1282,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (subtype.category === TypeCategory.Object) { - const awaitReturnType = getSpecializedReturnType( - subtype, '__await__'); + const awaitReturnType = getSpecializedReturnType(subtype, '__await__'); if (awaitReturnType) { if (isAnyOrUnknown(awaitReturnType)) { return awaitReturnType; } if (awaitReturnType.category === TypeCategory.Object) { - const iterReturnType = getSpecializedReturnType( - awaitReturnType, '__iter__'); + const iterReturnType = getSpecializedReturnType(awaitReturnType, '__iter__'); if (iterReturnType) { - const generatorReturnType = getReturnTypeFromGenerator( - awaitReturnType); + const generatorReturnType = getReturnTypeFromGenerator(awaitReturnType); if (generatorReturnType) { return generatorReturnType; } @@ -1121,7 +1302,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (errorNode) { - addError(`'${ printType(subtype) }' is not awaitable`, errorNode); + addError(`'${printType(subtype)}' is not awaitable`, errorNode); } return UnknownType.create(); @@ -1130,9 +1311,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Validates that the type is iterable and returns the iterated type. // If errorNode is undefined, no errors are reported. - function getTypeFromIterable(type: Type, isAsync: boolean, - errorNode: ParseNode | undefined, supportGetItem: boolean): Type { - + function getTypeFromIterable( + type: Type, + isAsync: boolean, + errorNode: ParseNode | undefined, + supportGetItem: boolean + ): Type { const iterMethodName = isAsync ? '__aiter__' : '__iter__'; const nextMethodName = isAsync ? '__anext__' : '__next__'; const getItemMethodName = supportGetItem ? '__getitem__' : ''; @@ -1147,42 +1331,43 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { getFileInfo(errorNode).diagnosticSettings.reportOptionalIterable, DiagnosticRule.reportOptionalIterable, `Object of type 'None' cannot be used as iterable value`, - errorNode); + errorNode + ); } type = removeNoneFromUnion(type); } - const getIteratorReturnType = (objType: ObjectType, metaclass: ClassType | undefined, - diag: DiagnosticAddendum): Type | undefined => { - - const iterReturnType = metaclass ? - getSpecializedReturnTypeForMetaclassMethod(metaclass, - objType.classType, iterMethodName) : - getSpecializedReturnType(objType, iterMethodName); + const getIteratorReturnType = ( + objType: ObjectType, + metaclass: ClassType | undefined, + diag: DiagnosticAddendum + ): Type | undefined => { + const iterReturnType = metaclass + ? getSpecializedReturnTypeForMetaclassMethod(metaclass, objType.classType, iterMethodName) + : getSpecializedReturnType(objType, iterMethodName); if (!iterReturnType) { // There was no __iter__. See if we can fall back to // the __getitem__ method instead. if (getItemMethodName) { - const getItemReturnType = getSpecializedReturnType( - objType, getItemMethodName); + const getItemReturnType = getSpecializedReturnType(objType, getItemMethodName); if (getItemReturnType) { return getItemReturnType; } } - diag.addMessage(`'${ iterMethodName }' method not defined`); + diag.addMessage(`'${iterMethodName}' method not defined`); } else { if (isAnyOrUnknown(iterReturnType)) { return iterReturnType; } if (iterReturnType.category === TypeCategory.Object) { - const nextReturnType = getSpecializedReturnType( - iterReturnType, nextMethodName); + const nextReturnType = getSpecializedReturnType(iterReturnType, nextMethodName); if (!nextReturnType) { - diag.addMessage(`'${ nextMethodName }' method not defined on type ` + - `'${ printType(iterReturnType) }'`); + diag.addMessage( + `'${nextMethodName}' method not defined on type ` + `'${printType(iterReturnType)}'` + ); } else { if (!isAsync) { return nextReturnType; @@ -1193,7 +1378,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return getTypeFromAwaitable(nextReturnType, errorNode); } } else { - diag.addMessage(`'${ iterMethodName }' method does not return an object`); + diag.addMessage(`'${iterMethodName}' method does not return an object`); } } @@ -1217,8 +1402,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const metaclassType = getMetaclass(subtype); if (metaclassType) { if (metaclassType.category === TypeCategory.Class) { - const returnType = getIteratorReturnType( - ObjectType.create(subtype), metaclassType, diag); + const returnType = getIteratorReturnType(ObjectType.create(subtype), metaclassType, diag); if (returnType) { return returnType; } @@ -1227,7 +1411,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (errorNode) { - addError(`'${ printType(subtype) }' is not iterable` + diag.getString(), errorNode); + addError(`'${printType(subtype)}' is not iterable` + diag.getString(), errorNode); } return UnknownType.create(); @@ -1239,8 +1423,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { function synthesizeDataClassMethods(node: ClassNode, classType: ClassType, skipSynthesizeInit: boolean) { assert(ClassType.isDataClass(classType)); - const newType = FunctionType.create( - FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod); + const newType = FunctionType.create(FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod); const initType = FunctionType.create(FunctionTypeFlags.SynthesizedMethod); FunctionType.addParameter(newType, { @@ -1279,26 +1462,35 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (statement.nodeType === ParseNodeType.Assignment) { if (statement.leftExpression.nodeType === ParseNodeType.Name) { variableNameNode = statement.leftExpression; - variableTypeEvaluator = () => stripLiteralValue( - getTypeOfExpression(statement.rightExpression).type); - } else if (statement.leftExpression.nodeType === ParseNodeType.TypeAnnotation && - statement.leftExpression.valueExpression.nodeType === ParseNodeType.Name) { - + variableTypeEvaluator = () => + stripLiteralValue(getTypeOfExpression(statement.rightExpression).type); + } else if ( + statement.leftExpression.nodeType === ParseNodeType.TypeAnnotation && + statement.leftExpression.valueExpression.nodeType === ParseNodeType.Name + ) { variableNameNode = statement.leftExpression.valueExpression; - variableTypeEvaluator = () => convertClassToObject( - getTypeOfExpression((statement.leftExpression as TypeAnnotationNode).typeAnnotation, - undefined, EvaluatorFlags.ConvertEllipsisToAny).type); + variableTypeEvaluator = () => + convertClassToObject( + getTypeOfExpression( + (statement.leftExpression as TypeAnnotationNode).typeAnnotation, + undefined, + EvaluatorFlags.ConvertEllipsisToAny + ).type + ); } hasDefaultValue = true; } else if (statement.nodeType === ParseNodeType.TypeAnnotation) { if (statement.valueExpression.nodeType === ParseNodeType.Name) { variableNameNode = statement.valueExpression; - variableTypeEvaluator = () => convertClassToObject( - getTypeOfExpression(statement.typeAnnotation, undefined, - EvaluatorFlags.ConvertEllipsisToAny | - EvaluatorFlags.EvaluateStringLiteralAsType - ).type); + variableTypeEvaluator = () => + convertClassToObject( + getTypeOfExpression( + statement.typeAnnotation, + undefined, + EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType + ).type + ); } } @@ -1336,8 +1528,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // all subsequent entries must also have default values. const firstDefaultValueIndex = fullDataClassEntries.findIndex(p => p.hasDefault); if (!hasDefaultValue && firstDefaultValueIndex >= 0 && firstDefaultValueIndex < insertIndex) { - addError(`Data fields without default value cannot appear after ` + - `data fields with default values`, variableNameNode); + addError( + `Data fields without default value cannot appear after ` + + `data fields with default values`, + variableNameNode + ); } } }); @@ -1368,10 +1563,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); const symbolTable = classType.details.fields; - symbolTable.set('__init__', Symbol.createWithType( - SymbolFlags.ClassMember, initType)); - symbolTable.set('__new__', Symbol.createWithType( - SymbolFlags.ClassMember, newType)); + symbolTable.set('__init__', Symbol.createWithType(SymbolFlags.ClassMember, initType)); + symbolTable.set('__new__', Symbol.createWithType(SymbolFlags.ClassMember, newType)); } } @@ -1379,8 +1572,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { assert(ClassType.isTypedDictClass(classType)); // Synthesize a __new__ method. - const newType = FunctionType.create( - FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod); + const newType = FunctionType.create(FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod); FunctionType.addParameter(newType, { category: ParameterCategory.Simple, name: 'cls', @@ -1425,7 +1617,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { entries.forEach((entry, name) => { const getOverload = FunctionType.create( - FunctionTypeFlags.SynthesizedMethod | FunctionTypeFlags.Overloaded); + FunctionTypeFlags.SynthesizedMethod | FunctionTypeFlags.Overloaded + ); FunctionType.addParameter(getOverload, { category: ParameterCategory.Simple, name: 'self', @@ -1449,8 +1642,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (getOverloads.length > 0) { const mappingClass = getBuiltInType(node, 'Mapping'); if (mappingClass.category === TypeCategory.Class) { - const overriddenGet = getTypeFromClassMemberName(node as ExpressionNode, mappingClass, 'get', - { method: 'get' }, new DiagnosticAddendum(), MemberAccessFlags.SkipBaseClasses); + const overriddenGet = getTypeFromClassMemberName( + node as ExpressionNode, + mappingClass, + 'get', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipBaseClasses + ); if (overriddenGet && overriddenGet.type.category === TypeCategory.OverloadedFunction) { getOverloads.push(overriddenGet.type.overloads[overriddenGet.type.overloads.length - 1]); } @@ -1539,9 +1738,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return undefined; } - function addDiagnostic(diagLevel: DiagnosticLevel, - rule: string, message: string, node: ParseNode) { - + function addDiagnostic(diagLevel: DiagnosticLevel, rule: string, message: string, node: ParseNode) { let diagnostic: Diagnostic | undefined; if (diagLevel === 'error') { @@ -1557,10 +1754,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return diagnostic; } - function addDiagnosticForTextRange(fileInfo: AnalyzerFileInfo, - diagLevel: DiagnosticLevel, rule: string, message: string, - range: TextRange) { - + function addDiagnosticForTextRange( + fileInfo: AnalyzerFileInfo, + diagLevel: DiagnosticLevel, + rule: string, + message: string, + range: TextRange + ) { let diagnostic: Diagnostic | undefined; if (diagLevel === 'error') { @@ -1581,7 +1781,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const symbolWithScope = lookUpSymbolRecursive(nameNode, nameValue); if (!symbolWithScope) { - fail(`Missing symbol '${ nameValue }'`); + fail(`Missing symbol '${nameValue}'`); return; } @@ -1596,9 +1796,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!canAssignType(declaredType, type, diagAddendum)) { addError( - `Expression of type '${ printType(type) }' cannot be ` + - `assigned to declared type '${ printType(declaredType) }'` + diagAddendum.getString(), - srcExpression || nameNode); + `Expression of type '${printType(type)}' cannot be ` + + `assigned to declared type '${printType(declaredType)}'` + + diagAddendum.getString(), + srcExpression || nameNode + ); destType = declaredType; } else { // Constrain the resulting type to match the declared type. @@ -1616,16 +1818,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const isConstant = isConstantName(nameValue); const isPrivate = isPrivateOrProtectedName(nameValue); - if (!isConstant && (!isPrivate || - getFileInfo(nameNode).diagnosticSettings.reportPrivateUsage === 'none')) { - + if ( + !isConstant && + (!isPrivate || getFileInfo(nameNode).diagnosticSettings.reportPrivateUsage === 'none') + ) { destType = stripLiteralValue(destType); } } } - const varDecl: Declaration | undefined = declarations.find( - decl => decl.type === DeclarationType.Variable); + const varDecl: Declaration | undefined = declarations.find(decl => decl.type === DeclarationType.Variable); if (varDecl && varDecl.type === DeclarationType.Variable && srcExpression) { if (varDecl.isConstant) { @@ -1635,8 +1837,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { addDiagnostic( fileInfo.diagnosticSettings.reportConstantRedefinition, DiagnosticRule.reportConstantRedefinition, - `'${ nameValue }' is constant and cannot be redefined`, - nameNode); + `'${nameValue}' is constant and cannot be redefined`, + nameNode + ); } } } @@ -1644,9 +1847,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { writeTypeCache(nameNode, destType); } - function assignTypeToMemberAccessNode(target: MemberAccessNode, type: Type, - srcExpr?: ExpressionNode) { - + function assignTypeToMemberAccessNode(target: MemberAccessNode, type: Type, srcExpr?: ExpressionNode) { const baseTypeResult = getTypeOfExpression(target.leftExpression); // Handle member accesses (e.g. self.x or cls.y). @@ -1673,22 +1874,30 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (ClassType.isProtocolClass(classTypeResults.classType)) { addError( `Assignment to instance or class variables not allowed within a Protocol class`, - target.memberName); + target.memberName + ); } } } } - getTypeFromMemberAccessWithBaseType(target, baseTypeResult, - { method: 'set', setType: type, setErrorNode: srcExpr }, EvaluatorFlags.None); + getTypeFromMemberAccessWithBaseType( + target, + baseTypeResult, + { method: 'set', setType: type, setErrorNode: srcExpr }, + EvaluatorFlags.None + ); writeTypeCache(target.memberName, type); writeTypeCache(target, type); } - function assignTypeToMemberVariable(node: MemberAccessNode, srcType: Type, - isInstanceMember: boolean, srcExprNode?: ExpressionNode) { - + function assignTypeToMemberVariable( + node: MemberAccessNode, + srcType: Type, + isInstanceMember: boolean, + srcExprNode?: ExpressionNode + ) { const memberName = node.memberName.value; const fileInfo = getFileInfo(node); @@ -1701,15 +1910,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const classTypeInfo = getTypeOfClass(classDef); if (classTypeInfo && classTypeInfo.classType.category === TypeCategory.Class) { - let memberInfo = lookUpClassMember(classTypeInfo.classType, memberName, - importLookup, isInstanceMember ? ClassMemberLookupFlags.Default : - ClassMemberLookupFlags.SkipInstanceVariables); + let memberInfo = lookUpClassMember( + classTypeInfo.classType, + memberName, + importLookup, + isInstanceMember ? ClassMemberLookupFlags.Default : ClassMemberLookupFlags.SkipInstanceVariables + ); const memberFields = classTypeInfo.classType.details.fields; if (memberInfo) { // Are we accessing an existing member on this class, or is // it a member on a parent class? - const isThisClass = memberInfo.classType.category === TypeCategory.Class && + const isThisClass = + memberInfo.classType.category === TypeCategory.Class && ClassType.isSameGenericClass(classTypeInfo.classType, memberInfo.classType); if (isThisClass && memberInfo.isInstanceMember === isInstanceMember) { @@ -1720,15 +1933,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let isFinalVar = isFinalVariable(symbol); // Check for an attempt to overwrite a constant or final member variable. - if (typedDecls.length > 0 && typedDecls[0].type === DeclarationType.Variable && - srcExprNode && node.memberName !== typedDecls[0].node) { - + if ( + typedDecls.length > 0 && + typedDecls[0].type === DeclarationType.Variable && + srcExprNode && + node.memberName !== typedDecls[0].node + ) { if (typedDecls[0].isConstant) { addDiagnostic( fileInfo.diagnosticSettings.reportConstantRedefinition, DiagnosticRule.reportConstantRedefinition, - `'${ node.memberName.value }' is constant and cannot be redefined`, - node.memberName); + `'${node.memberName.value}' is constant and cannot be redefined`, + node.memberName + ); } // If a Final instance variable is declared in the class body but is @@ -1740,8 +1957,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (isFinalVar) { addError( - `'${ node.memberName.value }' is declared as Final and cannot be reassigned`, - node.memberName); + `'${node.memberName.value}' is declared as Final and cannot be reassigned`, + node.memberName + ); } } } else { @@ -1762,8 +1980,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Look up the member info again, now that we've potentially updated it. - memberInfo = lookUpClassMember(classTypeInfo.classType, memberName, - importLookup, ClassMemberLookupFlags.DeclaredTypesOnly); + memberInfo = lookUpClassMember( + classTypeInfo.classType, + memberName, + importLookup, + ClassMemberLookupFlags.DeclaredTypesOnly + ); if (memberInfo) { const declaredType = getDeclaredTypeOfSymbol(memberInfo.symbol); if (declaredType && !isAnyOrUnknown(declaredType)) { @@ -1786,7 +2008,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { reportPossibleUnknownAssignment( fileInfo.diagnosticSettings.reportUnknownMemberType, DiagnosticRule.reportUnknownMemberType, - node.memberName, srcType, node); + node.memberName, + srcType, + node + ); } } } @@ -1806,13 +2031,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const entryTypes = tupleType.typeArguments; let entryCount = entryTypes.length; - const sourceEndsInEllipsis = entryCount > 1 && - isEllipsisType(entryTypes[entryCount - 1]); + const sourceEndsInEllipsis = entryCount > 1 && isEllipsisType(entryTypes[entryCount - 1]); if (sourceEndsInEllipsis) { entryCount--; } - const targetEndsWithUnpackOperator = target.expressions.length > 0 && + const targetEndsWithUnpackOperator = + target.expressions.length > 0 && target.expressions[target.expressions.length - 1].nodeType === ParseNodeType.Unpack; if (targetEndsWithUnpackOperator) { @@ -1831,9 +2056,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { targetTypes[target.expressions.length - 1].push(combineTypes(remainingTypes)); } else { addError( - `Tuple size mismatch: expected at least ${ target.expressions.length } entries` + - ` but got ${ entryCount }`, - target); + `Tuple size mismatch: expected at least ${target.expressions.length} entries` + + ` but got ${entryCount}`, + target + ); } } else { if (target.expressions.length === entryCount || sourceEndsInEllipsis) { @@ -1843,16 +2069,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } else { addError( - `Tuple size mismatch: expected ${ target.expressions.length }` + - ` but got ${ entryCount }`, - target); + `Tuple size mismatch: expected ${target.expressions.length}` + ` but got ${entryCount}`, + target + ); } } } else { // The assigned expression isn't a tuple, so it had better // be some iterable type. - const iterableType = getTypeFromIterable( - subtype, false, srcExpr, false); + const iterableType = getTypeFromIterable(subtype, false, srcExpr, false); for (let index = 0; index < target.expressions.length; index++) { targetTypes[index].push(iterableType); } @@ -1886,10 +2111,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (srcExpr.nodeType === ParseNodeType.List) { const fileInfo = getFileInfo(target); srcExpr.entries.forEach(entryExpr => { - if (entryExpr.nodeType === ParseNodeType.StringList || entryExpr.nodeType === ParseNodeType.String) { - const symbolName = entryExpr.nodeType === ParseNodeType.String ? - entryExpr.value : - entryExpr.strings.map(s => s.value).join(''); + if ( + entryExpr.nodeType === ParseNodeType.StringList || + entryExpr.nodeType === ParseNodeType.String + ) { + const symbolName = + entryExpr.nodeType === ParseNodeType.String + ? entryExpr.value + : entryExpr.strings.map(s => s.value).join(''); const symbolInScope = scope.lookUpSymbolRecursive(symbolName); if (symbolInScope) { setSymbolAccessed(fileInfo, symbolInScope.symbol); @@ -1903,7 +2132,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { reportPossibleUnknownAssignment( getFileInfo(target).diagnosticSettings.reportUnknownVariableType, DiagnosticRule.reportUnknownVariableType, - target, type, target); + target, + type, + target + ); assignTypeToNameNode(target, type, srcExpr); break; @@ -1915,11 +2147,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case ParseNodeType.Index: { - const baseTypeResult = getTypeOfExpression(target.baseExpression, - undefined, EvaluatorFlags.DoNotSpecialize); + const baseTypeResult = getTypeOfExpression( + target.baseExpression, + undefined, + EvaluatorFlags.DoNotSpecialize + ); - const indexTypeResult = getTypeFromIndexWithBaseType(target, baseTypeResult.type, - { method: 'set', setType: type, setErrorNode: srcExpr }, EvaluatorFlags.None); + const indexTypeResult = getTypeFromIndexWithBaseType( + target, + baseTypeResult.type, + { method: 'set', setType: type, setErrorNode: srcExpr }, + EvaluatorFlags.None + ); writeTypeCache(target, indexTypeResult.type); break; @@ -1959,8 +2198,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { case ParseNodeType.List: { // The assigned expression had better be some iterable type. - const iteratedType = getTypeFromIterable( - type, false, srcExpr, false); + const iteratedType = getTypeFromIterable(type, false, srcExpr, false); target.entries.forEach(entry => { assignTypeToExpression(entry, iteratedType, srcExpr); @@ -1998,16 +2236,22 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { case ParseNodeType.MemberAccess: { const baseTypeResult = getTypeOfExpression(node.leftExpression); const memberType = getTypeFromMemberAccessWithBaseType( - node, baseTypeResult, { method: 'del' }, EvaluatorFlags.None); + node, + baseTypeResult, + { method: 'del' }, + EvaluatorFlags.None + ); writeTypeCache(node.memberName, memberType.type); break; } case ParseNodeType.Index: { - const baseTypeResult = getTypeOfExpression(node.baseExpression, - undefined, EvaluatorFlags.DoNotSpecialize); - getTypeFromIndexWithBaseType(node, baseTypeResult.type, - { method: 'del' }, EvaluatorFlags.None); + const baseTypeResult = getTypeOfExpression( + node.baseExpression, + undefined, + EvaluatorFlags.DoNotSpecialize + ); + getTypeFromIndexWithBaseType(node, baseTypeResult.type, { method: 'del' }, EvaluatorFlags.None); writeTypeCache(node, UnboundType.create()); break; } @@ -2086,8 +2330,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } function getSpecializedReturnType(objType: ObjectType, memberName: string) { - const classMember = lookUpObjectMember(objType, memberName, - importLookup, ClassMemberLookupFlags.SkipInstanceVariables); + const classMember = lookUpObjectMember( + objType, + memberName, + importLookup, + ClassMemberLookupFlags.SkipInstanceVariables + ); if (!classMember) { return undefined; } @@ -2109,11 +2357,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // the method lookup occurs on a metaclass rather than // the object that derives from it. function getSpecializedReturnTypeForMetaclassMethod( - metaclass: ClassType, classType: ClassType, memberName: string) { - + metaclass: ClassType, + classType: ClassType, + memberName: string + ) { const classMember = lookUpObjectMember( - ObjectType.create(metaclass), memberName, importLookup, - ClassMemberLookupFlags.SkipInstanceVariables); + ObjectType.create(metaclass), + memberName, + importLookup, + ClassMemberLookupFlags.SkipInstanceVariables + ); if (!classMember) { return undefined; } @@ -2124,8 +2377,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (memberType.category === TypeCategory.Function) { - const methodType = bindFunctionToClassOrObject( - classType, memberType, true) as FunctionType; + const methodType = bindFunctionToClassOrObject(classType, memberType, true) as FunctionType; return getFunctionEffectiveReturnType(methodType); } @@ -2146,7 +2398,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const effectiveType = getEffectiveTypeOfSymbol(symbol); - const isSpecialBuiltIn = !!effectiveType && effectiveType.category === TypeCategory.Class && + const isSpecialBuiltIn = + !!effectiveType && + effectiveType.category === TypeCategory.Class && ClassType.isSpecialBuiltIn(effectiveType); let useCodeFlowAnalysis = !isSpecialBuiltIn; @@ -2170,8 +2424,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If the symbol is declared outside of our execution scope, use its effective // type. If it's declared inside our execution scope, it generally starts // as unbound at the start of the code flow. - const typeAtStart = symbolWithScope.isBeyondExecutionScope || !symbol.isInitiallyUnbound() ? - effectiveType : UnboundType.create(); + const typeAtStart = + symbolWithScope.isBeyondExecutionScope || !symbol.isInitiallyUnbound() + ? effectiveType + : UnboundType.create(); const codeFlowType = getFlowTypeOfReference(node, symbol.id, typeAtStart); if (codeFlowType) { type = codeFlowType; @@ -2194,16 +2450,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (isUnbound(type)) { - addError(`'${ name }' is unbound`, node); + addError(`'${name}' is unbound`, node); } else if (isPossiblyUnbound(type)) { - addError(`'${ name }' is possibly unbound`, node); + addError(`'${name}' is possibly unbound`, node); } setSymbolAccessed(fileInfo, symbol); } else { // Handle the special case of "reveal_type". if (name !== 'reveal_type') { - addError(`'${ name }' is not defined`, node); + addError(`'${name}' is not defined`, node); } type = UnknownType.create(); } @@ -2212,10 +2468,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } function getTypeFromMemberAccess(node: MemberAccessNode, flags: EvaluatorFlags): TypeResult { - const baseTypeResult = getTypeOfExpression(node.leftExpression, - undefined, EvaluatorFlags.DoNotSpecialize); - const memberType = getTypeFromMemberAccessWithBaseType( - node, baseTypeResult, { method: 'get' }, flags); + const baseTypeResult = getTypeOfExpression(node.leftExpression, undefined, EvaluatorFlags.DoNotSpecialize); + const memberType = getTypeFromMemberAccessWithBaseType(node, baseTypeResult, { method: 'get' }, flags); if (isCodeFlowSupportedForReference(node)) { // Before performing code fow analysis, update the cache to prevent @@ -2236,9 +2490,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return memberType; } - function getTypeFromMemberAccessWithBaseType(node: MemberAccessNode, - baseTypeResult: TypeResult, usage: EvaluatorUsage, flags: EvaluatorFlags): TypeResult { - + function getTypeFromMemberAccessWithBaseType( + node: MemberAccessNode, + baseTypeResult: TypeResult, + usage: EvaluatorUsage, + flags: EvaluatorFlags + ): TypeResult { const baseType = baseTypeResult.type; const memberName = node.memberName.value; const diag = new DiagnosticAddendum(); @@ -2252,19 +2509,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case TypeCategory.Class: { - type = getTypeFromClassMember(node.memberName, baseType, - node.memberName.value, usage, diag); + type = getTypeFromClassMember(node.memberName, baseType, node.memberName.value, usage, diag); break; } case TypeCategory.TypeVar: { - return getTypeFromMemberAccessWithBaseType(node, + return getTypeFromMemberAccessWithBaseType( + node, { type: specializeType(baseType, undefined), node }, usage, - EvaluatorFlags.None); + EvaluatorFlags.None + ); } case TypeCategory.Object: { @@ -2272,13 +2530,22 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (classFromTypeObject) { // Handle the case where the object is a 'Type' object, which // represents a class. - return getTypeFromMemberAccessWithBaseType(node, + return getTypeFromMemberAccessWithBaseType( + node, { type: classFromTypeObject, node: baseTypeResult.node }, - usage, flags); + usage, + flags + ); } - type = getTypeFromObjectMember(node.memberName, baseType, - node.memberName.value, usage, diag, MemberAccessFlags.None); + type = getTypeFromObjectMember( + node.memberName, + baseType, + node.memberName.value, + usage, + diag, + MemberAccessFlags.None + ); break; } @@ -2299,7 +2566,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { type = UnknownType.create(); } } else { - addError(`'${ memberName }' is not a known member of module`, node.memberName); + addError(`'${memberName}' is not a known member of module`, node.memberName); type = UnknownType.create(); } break; @@ -2311,20 +2578,24 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { addDiagnostic( getFileInfo(node).diagnosticSettings.reportOptionalMemberAccess, DiagnosticRule.reportOptionalMemberAccess, - `'${ memberName }' is not a known member of 'None'`, node.memberName); + `'${memberName}' is not a known member of 'None'`, + node.memberName + ); return undefined; } else if (subtype.category === TypeCategory.Unbound) { // Don't do anything if it's unbound. The error will already // be reported elsewhere. return undefined; } else { - const typeResult = getTypeFromMemberAccessWithBaseType(node, + const typeResult = getTypeFromMemberAccessWithBaseType( + node, { type: subtype, node }, usage, - EvaluatorFlags.None); + EvaluatorFlags.None + ); return typeResult.type; } }); @@ -2340,7 +2611,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } default: - diag.addMessage(`Unsupported type '${ printType(baseType) }'`); + diag.addMessage(`Unsupported type '${printType(baseType)}'`); break; } @@ -2353,9 +2624,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } addError( - `Cannot ${ operationName } member '${ memberName }' ` + - `for type '${ printType(baseType) }'` + diag.getString(), - node.memberName); + `Cannot ${operationName} member '${memberName}' ` + + `for type '${printType(baseType)}'` + + diag.getString(), + node.memberName + ); type = UnknownType.create(); } @@ -2396,9 +2669,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return undefined; } - function getTypeFromClassMemberName(errorNode: ExpressionNode, classType: ClassType, memberName: string, - usage: EvaluatorUsage, diag: DiagnosticAddendum, flags: MemberAccessFlags): ClassMemberLookup | undefined { - + function getTypeFromClassMemberName( + errorNode: ExpressionNode, + classType: ClassType, + memberName: string, + usage: EvaluatorUsage, + diag: DiagnosticAddendum, + flags: MemberAccessFlags + ): ClassMemberLookup | undefined { // If this is a special type (like "List") that has an alias // class (like "list"), switch to the alias, which defines // the members. @@ -2418,14 +2696,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Always look for a member with a declared type first. - let memberInfo = lookUpClassMember(classType, memberName, importLookup, - classLookupFlags | ClassMemberLookupFlags.DeclaredTypesOnly); + let memberInfo = lookUpClassMember( + classType, + memberName, + importLookup, + classLookupFlags | ClassMemberLookupFlags.DeclaredTypesOnly + ); // If we couldn't find a symbol with a declared type, use // a symbol with an inferred type. if (!memberInfo) { - memberInfo = lookUpClassMember(classType, memberName, - importLookup, classLookupFlags); + memberInfo = lookUpClassMember(classType, memberName, importLookup, classLookupFlags); } if (memberInfo) { @@ -2443,7 +2724,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (ClassType.isTypedDictClass(classType)) { const typedDecls = memberInfo.symbol.getTypedDeclarations(); if (typedDecls.length > 0 && typedDecls[0].type === DeclarationType.Variable) { - diag.addMessage(`Member '${ memberName }' is unknown`); + diag.addMessage(`Member '${memberName}' is unknown`); return undefined; } } @@ -2470,19 +2751,23 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const memberClassType = type.classType; - const accessMethod = lookUpClassMember(memberClassType, accessMethodName, - importLookup, ClassMemberLookupFlags.SkipInstanceVariables); + const accessMethod = lookUpClassMember( + memberClassType, + accessMethodName, + importLookup, + ClassMemberLookupFlags.SkipInstanceVariables + ); // Handle properties specially. if (ClassType.isPropertyClass(type.classType)) { if (usage.method === 'set') { if (!accessMethod) { - diag.addMessage(`Property '${ memberName }' has no defined setter`); + diag.addMessage(`Property '${memberName}' has no defined setter`); return undefined; } } else if (usage.method === 'del') { if (!accessMethod) { - diag.addMessage(`Property '${ memberName }' has no defined deleter`); + diag.addMessage(`Property '${memberName}' has no defined deleter`); return undefined; } } @@ -2493,18 +2778,23 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If it's an overloaded function, determine which overload to use. if (accessMethodType.category === TypeCategory.OverloadedFunction) { - const argList: FunctionArgument[] = [{ - argumentCategory: ArgumentCategory.Simple, - type: ObjectType.create(memberClassType) - }, { - argumentCategory: ArgumentCategory.Simple, - type: (flags & MemberAccessFlags.SkipInstanceMembers) ? - NoneType.create() : - ObjectType.create(classType) - }, { - argumentCategory: ArgumentCategory.Simple, - type: AnyType.create() - }]; + const argList: FunctionArgument[] = [ + { + argumentCategory: ArgumentCategory.Simple, + type: ObjectType.create(memberClassType) + }, + { + argumentCategory: ArgumentCategory.Simple, + type: + flags & MemberAccessFlags.SkipInstanceMembers + ? NoneType.create() + : ObjectType.create(classType) + }, + { + argumentCategory: ArgumentCategory.Simple, + type: AnyType.create() + } + ]; const overload = findOverloadedFunctionType(errorNode, argList, accessMethodType); if (overload) { @@ -2514,8 +2804,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (accessMethodType.category === TypeCategory.Function) { // Bind the accessor to the base object type. - accessMethodType = bindFunctionToClassOrObject(ObjectType.create(classType), - accessMethodType) as FunctionType; + accessMethodType = bindFunctionToClassOrObject( + ObjectType.create(classType), + accessMethodType + ) as FunctionType; if (usage.method === 'get') { type = getFunctionEffectiveReturnType(accessMethodType); @@ -2580,9 +2872,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Verify that the assigned type is compatible. if (!canAssignType(effectiveType, usage.setType!, diag.createAddendum())) { diag.addMessage( - `Expression of type '${ printType(usage.setType!) }'` + - ` cannot be assigned to member '${ memberName }'` + - ` of class '${ printObjectTypeForClass(classType) }'`); + `Expression of type '${printType(usage.setType!)}'` + + ` cannot be assigned to member '${memberName}'` + + ` of class '${printObjectTypeForClass(classType)}'` + ); return undefined; } } @@ -2598,10 +2891,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (usage.method === 'get') { // See if the class has a "__getattribute__" or "__getattr__" method. // If so, arbitrary members are supported. - const getAttribType = getTypeFromClassMember(errorNode, classType, - '__getattribute__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup | - MemberAccessFlags.SkipObjectBaseClass); + const getAttribType = getTypeFromClassMember( + errorNode, + classType, + '__getattribute__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass + ); if (getAttribType && getAttribType.category === TypeCategory.Function) { return { @@ -2610,9 +2907,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }; } - const getAttrType = getTypeFromClassMember(errorNode, classType, - '__getattr__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup); + const getAttrType = getTypeFromClassMember( + errorNode, + classType, + '__getattr__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup + ); if (getAttrType && getAttrType.category === TypeCategory.Function) { return { type: getFunctionEffectiveReturnType(getAttrType), @@ -2620,9 +2922,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }; } } else if (usage.method === 'set') { - const setAttrType = getTypeFromClassMember(errorNode, classType, - '__setattr__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass); + const setAttrType = getTypeFromClassMember( + errorNode, + classType, + '__setattr__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass + ); if (setAttrType) { // The type doesn't matter for a set usage. We just need // to return a defined type. @@ -2633,9 +2940,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } else { assert(usage.method === 'del'); - const delAttrType = getTypeFromClassMember(errorNode, classType, - '__detattr__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass); + const delAttrType = getTypeFromClassMember( + errorNode, + classType, + '__detattr__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass + ); if (delAttrType) { // The type doesn't matter for a delete usage. We just need // to return a defined type. @@ -2647,21 +2959,26 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - diag.addMessage(`Member '${ memberName }' is unknown`); + diag.addMessage(`Member '${memberName}' is unknown`); return undefined; } function getTypeFromIndex(node: IndexNode, expectedType?: Type, flags = EvaluatorFlags.None): TypeResult { - const baseTypeResult = getTypeOfExpression(node.baseExpression, - undefined, flags | EvaluatorFlags.DoNotSpecialize); + const baseTypeResult = getTypeOfExpression( + node.baseExpression, + undefined, + flags | EvaluatorFlags.DoNotSpecialize + ); - return getTypeFromIndexWithBaseType(node, baseTypeResult.type, - { method: 'get', expectedType }, flags); + return getTypeFromIndexWithBaseType(node, baseTypeResult.type, { method: 'get', expectedType }, flags); } - function getTypeFromIndexWithBaseType(node: IndexNode, baseType: Type, - usage: EvaluatorUsage, flags: EvaluatorFlags): TypeResult { - + function getTypeFromIndexWithBaseType( + node: IndexNode, + baseType: Type, + usage: EvaluatorUsage, + flags: EvaluatorFlags + ): TypeResult { // Handle the special case where we're specializing a // generic union of classes or callable. if (baseType.category === TypeCategory.Union || baseType.category === TypeCategory.Function) { @@ -2669,13 +2986,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let isUnionOfClasses = true; doForSubtypes(baseType, subtype => { - if (subtype.category === TypeCategory.Class || + if ( + subtype.category === TypeCategory.Class || subtype.category === TypeCategory.TypeVar || subtype.category === TypeCategory.Function || - subtype.category === TypeCategory.None) { - - addTypeVarsToListIfUnique(typeParameters, - getTypeVarArgumentsRecursive(subtype)); + subtype.category === TypeCategory.None + ) { + addTypeVarsToListIfUnique(typeParameters, getTypeVarArgumentsRecursive(subtype)); } else { isUnionOfClasses = false; } @@ -2683,8 +3000,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); if (isUnionOfClasses && typeParameters.length > 0) { - const typeArgs = getTypeArgs(node.items, flags).map( - t => convertClassToObject(t.type)); + const typeArgs = getTypeArgs(node.items, flags).map(t => convertClassToObject(t.type)); const typeVarMap = buildTypeVarMap(typeParameters, typeArgs); const type = specializeType(baseType, typeVarMap); return { type, node }; @@ -2721,8 +3037,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return typeArgs[0].type; } else { addError( - `Expected one type argument for 'InitVar' but got ${ typeArgs.length }`, - node.baseExpression); + `Expected one type argument for 'InitVar' but got ${typeArgs.length}`, + node.baseExpression + ); return UnknownType.create(); } } @@ -2749,15 +3066,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { getFileInfo(node).diagnosticSettings.reportOptionalSubscript, DiagnosticRule.reportOptionalSubscript, `Optional of type 'None' cannot be subscripted`, - node.baseExpression); + node.baseExpression + ); return UnknownType.create(); } if (!isUnbound(subtype)) { - addError( - `Object of type '${ printType(subtype) }' cannot be subscripted`, - node.baseExpression); + addError(`Object of type '${printType(subtype)}' cannot be subscripted`, node.baseExpression); } return UnknownType.create(); @@ -2800,22 +3116,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const entryName = subtype.literalValue as string; const entry = entries.get(entryName); if (!entry) { - diag.addMessage( - `'${ entryName }' is not a defined key in '${ printType(baseType) }'`); + diag.addMessage(`'${entryName}' is not a defined key in '${printType(baseType)}'`); return UnknownType.create(); } if (usage.method === 'set') { canAssignType(entry.valueType, usage.setType!, diag); } else if (usage.method === 'del' && entry.isRequired) { - addError( - `'${ entryName }' is a required key and cannot be deleted`, node); + addError(`'${entryName}' is a required key and cannot be deleted`, node); } return entry.valueType; } - diag.addMessage(`'${ printType(subtype) }' is not a string literal`); + diag.addMessage(`'${printType(subtype)}' is not a string literal`); return UnknownType.create(); }); @@ -2826,8 +3140,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } else if (usage.method === 'del') { operationName = 'delete'; } - addError(`Could not ${ operationName } item in TypedDict` + diag.getString(), - node); + addError(`Could not ${operationName} item in TypedDict` + diag.getString(), node); } return resultingType; @@ -2843,15 +3156,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { magicMethodName = '__delitem__'; } - const itemMethodType = getTypeFromObjectMember(node, - baseType, magicMethodName, { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup); + const itemMethodType = getTypeFromObjectMember( + node, + baseType, + magicMethodName, + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup + ); if (!itemMethodType) { addError( - `Object of type '${ printType(baseType) }' does not define ` + - `'${ magicMethodName }'`, - node.baseExpression); + `Object of type '${printType(baseType)}' does not define ` + `'${magicMethodName}'`, + node.baseExpression + ); return UnknownType.create(); } @@ -2866,19 +3184,23 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // the exact type by indexing into the tuple type array. const baseTypeClass = baseType.classType; - if (baseTypeClass.category === TypeCategory.Class && + if ( + baseTypeClass.category === TypeCategory.Class && ClassType.isBuiltIn(baseTypeClass, 'Tuple') && - baseTypeClass.typeArguments) { - - if (node.items.items[0].nodeType === ParseNodeType.Number && - node.items.items[0].isInteger && - !node.items.items[0].isImaginary) { - + baseTypeClass.typeArguments + ) { + if ( + node.items.items[0].nodeType === ParseNodeType.Number && + node.items.items[0].isInteger && + !node.items.items[0].isImaginary + ) { const numberNode = node.items.items[0]; - if (numberNode.isInteger && numberNode.value >= 0 && - numberNode.value < baseTypeClass.typeArguments.length) { - + if ( + numberNode.isInteger && + numberNode.value >= 0 && + numberNode.value < baseTypeClass.typeArguments.length + ) { return baseTypeClass.typeArguments[numberNode.value]; } } @@ -2888,17 +3210,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // isn't used in most cases, but it is supported by the language. const builtInTupleType = getBuiltInType(node, 'Tuple'); if (builtInTupleType.category === TypeCategory.Class) { - indexType = convertClassToObject( - ClassType.cloneForSpecialization(builtInTupleType, indexTypeList)); + indexType = convertClassToObject(ClassType.cloneForSpecialization(builtInTupleType, indexTypeList)); } else { indexType = UnknownType.create(); } } - const argList: FunctionArgument[] = [{ - argumentCategory: ArgumentCategory.Simple, - type: indexType - }]; + const argList: FunctionArgument[] = [ + { + argumentCategory: ArgumentCategory.Simple, + type: indexType + } + ]; if (usage.method === 'set') { argList.push({ @@ -2907,8 +3230,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); } - const callResult = validateCallArguments(node, argList, - itemMethodType, new TypeVarMap(), false); + const callResult = validateCallArguments(node, argList, itemMethodType, new TypeVarMap(), false); return callResult.returnType || UnknownType.create(); } @@ -2929,13 +3251,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (node.nodeType === ParseNodeType.List) { typeResult = { type: UnknownType.create(), - typeList: node.entries.map(entry => getTypeOfExpression( - entry, undefined, flags)), + typeList: node.entries.map(entry => getTypeOfExpression(entry, undefined, flags)), node }; } else { - typeResult = getTypeOfExpression(node, undefined, - flags | EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType); + typeResult = getTypeOfExpression( + node, + undefined, + flags | EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType + ); } return typeResult; @@ -2963,9 +3287,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - const entryTypeResults = node.expressions.map( - (expr, index) => getTypeOfExpression(expr, - index < expectedTypes.length ? expectedTypes[index] : undefined)); + const entryTypeResults = node.expressions.map((expr, index) => + getTypeOfExpression(expr, index < expectedTypes.length ? expectedTypes[index] : undefined) + ); let type: Type = UnknownType.create(); const builtInTupleType = getBuiltInType(node, 'Tuple'); @@ -2978,9 +3302,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // unpacked entries onto the new tuple. If it's not an upacked tuple // but some other iterator (e.g. a List), we won't know the number of // items, so we'll need to leave the Tuple open-ended. - if (typeResult.unpackedType.category === TypeCategory.Object && - ClassType.isBuiltIn(typeResult.unpackedType.classType, 'Tuple')) { - + if ( + typeResult.unpackedType.category === TypeCategory.Object && + ClassType.isBuiltIn(typeResult.unpackedType.classType, 'Tuple') + ) { const typeArgs = typeResult.unpackedType.classType.typeArguments; // If the Tuple wasn't specialized or has a "..." type parameter, we can't @@ -3002,18 +3327,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - type = convertClassToObject( - ClassType.cloneForSpecialization(builtInTupleType, tupleTypes)); + type = convertClassToObject(ClassType.cloneForSpecialization(builtInTupleType, tupleTypes)); } return { type, node }; } - function getTypeFromCall(node: CallNode, expectedType: Type | undefined, - flags: EvaluatorFlags): TypeResult { - - const baseTypeResult = getTypeOfExpression(node.leftExpression, - undefined, EvaluatorFlags.DoNotSpecialize); + function getTypeFromCall(node: CallNode, expectedType: Type | undefined, flags: EvaluatorFlags): TypeResult { + const baseTypeResult = getTypeOfExpression(node.leftExpression, undefined, EvaluatorFlags.DoNotSpecialize); // Handle the built-in "super" call specially. if (node.leftExpression.nodeType === ParseNodeType.Name && node.leftExpression.value === 'super') { @@ -3024,18 +3345,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Handle the special-case "reveal_type" call. - if (isAnyOrUnknown(baseTypeResult.type) && + if ( + isAnyOrUnknown(baseTypeResult.type) && node.leftExpression.nodeType === ParseNodeType.Name && node.leftExpression.value === 'reveal_type' && node.arguments.length === 1 && node.arguments[0].argumentCategory === ArgumentCategory.Simple && - node.arguments[0].name === undefined) { - + node.arguments[0].name === undefined + ) { const type = getTypeOfExpression(node.arguments[0].valueExpression).type; const exprString = ParseTreeUtils.printExpression(node.arguments[0].valueExpression); - addWarning( - `Type of '${ exprString }' is '${ printType(type) }'`, - node.arguments[0]); + addWarning(`Type of '${exprString}' is '${printType(type)}'`, node.arguments[0]); return { type: AnyType.create(), node }; } @@ -3049,14 +3369,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); return getTypeFromCallWithBaseType( - node, argList, baseTypeResult, expectedType, flags & ~EvaluatorFlags.DoNotSpecialize); + node, + argList, + baseTypeResult, + expectedType, + flags & ~EvaluatorFlags.DoNotSpecialize + ); } function getTypeFromSuperCall(node: CallNode): Type { if (node.arguments.length > 2) { - addError( - `Expecting no more than two arguments to super'`, - node.arguments[2]); + addError(`Expecting no more than two arguments to super'`, node.arguments[2]); } // Determine which class the "super" call is applied to. If @@ -3068,8 +3391,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!isAnyOrUnknown(targetClassType) && !(targetClassType.category === TypeCategory.Class)) { addError( `Expected class type as first argument to super() call but received ` + - `'${ printType(targetClassType) }'`, - node.arguments[0].valueExpression); + `'${printType(targetClassType)}'`, + node.arguments[0].valueExpression + ); } } else { const enclosingClass = ParseTreeUtils.getEnclosingClass(node); @@ -3077,9 +3401,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const classTypeInfo = getTypeOfClass(enclosingClass); targetClassType = classTypeInfo ? classTypeInfo.classType : UnknownType.create(); } else { - addError( - `Zero-argument form of super call is valid only within a class'`, - node.leftExpression); + addError(`Zero-argument form of super call is valid only within a class'`, node.leftExpression); targetClassType = UnknownType.create(); } } @@ -3112,8 +3434,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (reportError) { addError( - `Second argument to super() call must be object or class that derives from '${ printType(targetClassType) }'`, - node.arguments[1].valueExpression); + `Second argument to super() call must be object or class that derives from '${printType( + targetClassType + )}'`, + node.arguments[1].valueExpression + ); } } @@ -3123,8 +3448,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (parentNode.nodeType === ParseNodeType.MemberAccess) { const memberName = parentNode.memberName.value; const lookupResults = lookUpClassMember( - targetClassType, memberName, importLookup, - ClassMemberLookupFlags.SkipOriginalClass); + targetClassType, + memberName, + importLookup, + ClassMemberLookupFlags.SkipOriginalClass + ); if (lookupResults && lookupResults.classType.category === TypeCategory.Class) { return ObjectType.create(lookupResults.classType); } @@ -3145,10 +3473,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return UnknownType.create(); } - function getTypeFromCallWithBaseType(errorNode: ExpressionNode, - argList: FunctionArgument[], baseTypeResult: TypeResult, expectedType: Type | undefined, - flags: EvaluatorFlags): TypeResult { - + function getTypeFromCallWithBaseType( + errorNode: ExpressionNode, + argList: FunctionArgument[], + baseTypeResult: TypeResult, + expectedType: Type | undefined, + flags: EvaluatorFlags + ): TypeResult { let type: Type | undefined; let callType = baseTypeResult.type; const skipUnknownArgCheck = (flags & EvaluatorFlags.DoNotCheckForUnknownArgs) !== 0; @@ -3180,11 +3511,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { type = createTypeVarType(errorNode, argList); } else if (className === 'NamedTuple') { type = createNamedTupleType(errorNode, argList, true); - } else if (className === 'Protocol' || className === 'Generic' || - className === 'Callable' || className === 'Type') { - addError(`'${ className }' cannot be instantiated directly`, errorNode); - } else if (className === 'Enum' || className === 'IntEnum' || - className === 'Flag' || className === 'IntFlag') { + } else if ( + className === 'Protocol' || + className === 'Generic' || + className === 'Callable' || + className === 'Type' + ) { + addError(`'${className}' cannot be instantiated directly`, errorNode); + } else if ( + className === 'Enum' || + className === 'IntEnum' || + className === 'Flag' || + className === 'IntFlag' + ) { type = createEnumType(errorNode, callType, argList); } else if (className === 'TypedDict') { type = createTypedDictType(errorNode, callType, argList); @@ -3202,27 +3541,27 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { symbolTableKeys.forEach((symbolName, index) => { if (index === errorsToDisplay) { - diagAddendum.addMessage(`and ${ symbolTableKeys.length - errorsToDisplay } more...`); + diagAddendum.addMessage(`and ${symbolTableKeys.length - errorsToDisplay} more...`); } else if (index < errorsToDisplay) { const symbolWithClass = symbolTable.get(symbolName); if (symbolWithClass && symbolWithClass.classType.category === TypeCategory.Class) { const className = symbolWithClass.classType.details.name; - diagAddendum.addMessage(`'${ className }.${ symbolName }' is abstract`); + diagAddendum.addMessage(`'${className}.${symbolName}' is abstract`); } } }); addError( - `Cannot instantiate abstract class '${ callType.details.name }'` + - diagAddendum.getString(), - errorNode); + `Cannot instantiate abstract class '${callType.details.name}'` + diagAddendum.getString(), + errorNode + ); } // Assume this is a call to the constructor. if (!type) { - type = validateConstructorArguments(errorNode, argList, callType, - skipUnknownArgCheck, expectedType).returnType; + type = validateConstructorArguments(errorNode, argList, callType, skipUnknownArgCheck, expectedType) + .returnType; } break; } @@ -3235,19 +3574,24 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { getFileInfo(errorNode).diagnosticSettings.reportUntypedNamedTuple, DiagnosticRule.reportUntypedNamedTuple, `'namedtuple' provides no types for tuple entries. Use 'NamedTuple' instead.`, - errorNode); + errorNode + ); type = createNamedTupleType(errorNode, argList, false); } else if (callType.details.builtInName === 'NewType') { - const callResult = validateCallArguments(errorNode, argList, callType, - new TypeVarMap(), skipUnknownArgCheck); + const callResult = validateCallArguments( + errorNode, + argList, + callType, + new TypeVarMap(), + skipUnknownArgCheck + ); // If the call's arguments were validated, replace the // type with a new synthesized subclass. - type = callResult.argumentErrors ? callResult.returnType : - createNewType(errorNode, argList); + type = callResult.argumentErrors ? callResult.returnType : createNewType(errorNode, argList); } else { - type = validateCallArguments(errorNode, argList, callType, - new TypeVarMap(), skipUnknownArgCheck).returnType; + type = validateCallArguments(errorNode, argList, callType, new TypeVarMap(), skipUnknownArgCheck) + .returnType; if (callType.details.builtInName === '__import__') { // For the special __import__ type, we'll override the return type to be "Any". @@ -3272,20 +3616,29 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Verify that the cast is necessary. const castToType = getTypeForArgument(argList[0], true); const castFromType = getTypeForArgument(argList[1]); - if (castToType.category === TypeCategory.Class && castFromType.category === TypeCategory.Object) { + if ( + castToType.category === TypeCategory.Class && + castFromType.category === TypeCategory.Object + ) { if (isTypeSame(castToType, castFromType.classType)) { addDiagnostic( getFileInfo(errorNode).diagnosticSettings.reportUnnecessaryCast, DiagnosticRule.reportUnnecessaryCast, - `Unnecessary call to cast: type is already ${ printType(castFromType) }`, - errorNode); + `Unnecessary call to cast: type is already ${printType(castFromType)}`, + errorNode + ); } } type = convertClassToObject(castToType); } else { - type = validateCallArguments(errorNode, argList, functionType, - new TypeVarMap(), skipUnknownArgCheck).returnType; + type = validateCallArguments( + errorNode, + argList, + functionType, + new TypeVarMap(), + skipUnknownArgCheck + ).returnType; if (!type) { type = UnknownType.create(); } @@ -3294,10 +3647,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const exprString = ParseTreeUtils.printExpression(errorNode); const diagAddendum = new DiagnosticAddendum(); const argTypes = argList.map(t => printType(getTypeForArgument(t))); - diagAddendum.addMessage(`Argument types: (${ argTypes.join(', ') })`); - addError( - `No overloads for '${ exprString }' match parameters` + diagAddendum.getString(), - errorNode); + diagAddendum.addMessage(`Argument types: (${argTypes.join(', ')})`); + addError(`No overloads for '${exprString}' match parameters` + diagAddendum.getString(), errorNode); type = UnknownType.create(); } break; @@ -3310,16 +3661,31 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (isAnyOrUnknown(classFromTypeObject)) { type = classFromTypeObject; } else if (classFromTypeObject.category === TypeCategory.Class) { - type = validateConstructorArguments(errorNode, argList, - classFromTypeObject, skipUnknownArgCheck, expectedType).returnType; + type = validateConstructorArguments( + errorNode, + argList, + classFromTypeObject, + skipUnknownArgCheck, + expectedType + ).returnType; } } else { - const memberType = getTypeFromObjectMember(errorNode, - callType, '__call__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup); + const memberType = getTypeFromObjectMember( + errorNode, + callType, + '__call__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup + ); if (memberType) { - type = validateCallArguments(errorNode, argList, memberType, - new TypeVarMap(), skipUnknownArgCheck).returnType; + type = validateCallArguments( + errorNode, + argList, + memberType, + new TypeVarMap(), + skipUnknownArgCheck + ).returnType; if (!type) { type = UnknownType.create(); } @@ -3336,7 +3702,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { getFileInfo(errorNode).diagnosticSettings.reportOptionalCall, DiagnosticRule.reportOptionalCall, `Object of type 'None' cannot be called`, - errorNode); + errorNode + ); } else { const typeResult = getTypeFromCallWithBaseType( errorNode, @@ -3345,7 +3712,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { type: typeEntry, node: baseTypeResult.node }, - expectedType, flags); + expectedType, + flags + ); if (typeResult) { returnTypes.push(typeResult.type); } @@ -3369,9 +3738,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!type) { addError( - `'${ ParseTreeUtils.printExpression(errorNode) }' has type ` + - `'${ printType(callType) }' and is not callable`, - errorNode); + `'${ParseTreeUtils.printExpression(errorNode)}' has type ` + + `'${printType(callType)}' and is not callable`, + errorNode + ); type = UnknownType.create(); } @@ -3385,16 +3755,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return { type, node: baseTypeResult.node }; } - function findOverloadedFunctionType(errorNode: ExpressionNode, argList: FunctionArgument[], - callType: OverloadedFunctionType): FunctionType | undefined { - + function findOverloadedFunctionType( + errorNode: ExpressionNode, + argList: FunctionArgument[], + callType: OverloadedFunctionType + ): FunctionType | undefined { let validOverload: FunctionType | undefined; for (const overload of callType.overloads) { // Temporarily disable diagnostic output. useSpeculativeMode(() => { - const callResult = validateCallArguments(errorNode, argList, overload, - new TypeVarMap(), true); + const callResult = validateCallArguments(errorNode, argList, overload, new TypeVarMap(), true); if (!callResult.argumentErrors) { validOverload = overload; } @@ -3412,10 +3783,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If successful, it returns the resulting (specialized) object type that // is allocated by the constructor. If unsuccessful, it records diagnostic // information and returns undefined. - function validateConstructorArguments(errorNode: ExpressionNode, - argList: FunctionArgument[], type: ClassType, skipUnknownArgCheck: boolean, - expectedType?: Type): CallResult { - + function validateConstructorArguments( + errorNode: ExpressionNode, + argList: FunctionArgument[], + type: ClassType, + skipUnknownArgCheck: boolean, + expectedType?: Type + ): CallResult { let validatedTypes = false; let returnType: Type | undefined; let reportedErrors = false; @@ -3435,18 +3809,29 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // more specific type annotations, and we want to evaluate the arguments // in the context of these types. The __new__ method often uses generic // vargs and kwargs. - const initMethodType = getTypeFromObjectMember(errorNode, - ObjectType.create(type), '__init__', { method: 'get' }, + const initMethodType = getTypeFromObjectMember( + errorNode, + ObjectType.create(type), + '__init__', + { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass); + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass + ); if (initMethodType && !skipConstructorCheck(initMethodType)) { const typeVarMap = new TypeVarMap(); - const callResult = validateCallArguments(errorNode, argList, initMethodType, - typeVarMap, skipUnknownArgCheck); + const callResult = validateCallArguments( + errorNode, + argList, + initMethodType, + typeVarMap, + skipUnknownArgCheck + ); if (!callResult.argumentErrors) { const specializedClassType = applyExpectedTypeForConstructor( - specializeType(type, typeVarMap) as ClassType, expectedType); + specializeType(type, typeVarMap) as ClassType, + expectedType + ); returnType = ObjectType.create(specializedClassType); } else { reportedErrors = true; @@ -3459,25 +3844,35 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Don't report errors for __new__ if __init__ already generated errors. They're // probably going to be entirely redundant anyway. if (!reportedErrors) { - const constructorMethodInfo = getTypeFromClassMemberName(errorNode, - type, '__new__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup | - MemberAccessFlags.SkipObjectBaseClass); + const constructorMethodInfo = getTypeFromClassMemberName( + errorNode, + type, + '__new__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass + ); if (constructorMethodInfo && !skipConstructorCheck(constructorMethodInfo.type)) { - const constructorMethodType = bindFunctionToClassOrObject( - type, constructorMethodInfo.type, true); + const constructorMethodType = bindFunctionToClassOrObject(type, constructorMethodInfo.type, true); const typeVarMap = new TypeVarMap(); // Skip the unknown argument check if we've already checked for __init__. - const callResult = validateCallArguments(errorNode, argList, - constructorMethodType, typeVarMap, skipUnknownArgCheck); + const callResult = validateCallArguments( + errorNode, + argList, + constructorMethodType, + typeVarMap, + skipUnknownArgCheck + ); if (!callResult.argumentErrors) { reportedErrors = true; } if (!returnType) { const specializedClassType = applyExpectedTypeForConstructor( - specializeType(type, typeVarMap) as ClassType, expectedType); + specializeType(type, typeVarMap) as ClassType, + expectedType + ); returnType = ObjectType.create(specializedClassType); } validatedTypes = true; @@ -3485,8 +3880,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (!validatedTypes && argList.length > 0) { - addError( - `Expected no arguments to '${ type.details.name }' constructor`, errorNode); + addError(`Expected no arguments to '${type.details.name}' constructor`, errorNode); } else if (!returnType) { // There was no __new__ or __init__, so fall back on the // object.__new__ which takes no parameters. @@ -3521,10 +3915,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If it's the same generic class, see if we can assign the type arguments // without the variance rules that canAssignType uses. - if (ClassType.isSameGenericClass(type, expectedClass) && - expectedClass.typeArguments && type.typeArguments && - expectedClass.typeArguments.length === type.typeArguments.length) { - + if ( + ClassType.isSameGenericClass(type, expectedClass) && + expectedClass.typeArguments && + type.typeArguments && + expectedClass.typeArguments.length === type.typeArguments.length + ) { let isAssignable = true; expectedClass.typeArguments.forEach((expectedTypeArg, index) => { const typeTypeArg = type.typeArguments![index]; @@ -3545,9 +3941,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // list, specializes the call based on arg types, and returns the // specialized type of the return value. If it detects an error along // the way, it emits a diagnostic and returns undefined. - function validateCallArguments(errorNode: ExpressionNode, argList: FunctionArgument[], - callType: Type, typeVarMap: TypeVarMap, skipUnknownArgCheck: boolean): CallResult { - + function validateCallArguments( + errorNode: ExpressionNode, + argList: FunctionArgument[], + callType: Type, + typeVarMap: TypeVarMap, + skipUnknownArgCheck: boolean + ): CallResult { let callResult: CallResult = { argumentErrors: false }; switch (callType.category) { @@ -3560,50 +3960,58 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case TypeCategory.Function: { - callResult = validateFunctionArguments(errorNode, argList, callType, - typeVarMap, skipUnknownArgCheck); + callResult = validateFunctionArguments(errorNode, argList, callType, typeVarMap, skipUnknownArgCheck); break; } case TypeCategory.OverloadedFunction: { - const overloadedFunctionType = findOverloadedFunctionType( - errorNode, argList, callType); + const overloadedFunctionType = findOverloadedFunctionType(errorNode, argList, callType); if (overloadedFunctionType) { - callResult = validateFunctionArguments(errorNode, - argList, overloadedFunctionType, typeVarMap, skipUnknownArgCheck); + callResult = validateFunctionArguments( + errorNode, + argList, + overloadedFunctionType, + typeVarMap, + skipUnknownArgCheck + ); } else { const exprString = ParseTreeUtils.printExpression(errorNode); const diagAddendum = new DiagnosticAddendum(); const argTypes = argList.map(t => printType(getTypeForArgument(t))); - diagAddendum.addMessage(`Argument types: (${ argTypes.join(', ') })`); - addError( - `No overloads for '${ exprString }' match parameters` + diagAddendum.getString(), - errorNode); + diagAddendum.addMessage(`Argument types: (${argTypes.join(', ')})`); + addError(`No overloads for '${exprString}' match parameters` + diagAddendum.getString(), errorNode); } break; } case TypeCategory.Class: { if (!ClassType.isSpecialBuiltIn(callType)) { - callResult = validateConstructorArguments(errorNode, argList, - callType, skipUnknownArgCheck); + callResult = validateConstructorArguments(errorNode, argList, callType, skipUnknownArgCheck); } else { - addError( - `'${ callType.details.name }' cannot be instantiated`, - errorNode); + addError(`'${callType.details.name}' cannot be instantiated`, errorNode); } break; } case TypeCategory.Object: { - const memberType = getTypeFromObjectMember(errorNode, - callType, '__call__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup); + const memberType = getTypeFromObjectMember( + errorNode, + callType, + '__call__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup + ); if (memberType && memberType.category === TypeCategory.Function) { const callMethodType = stripFirstParameter(memberType); - callResult = validateCallArguments(errorNode, argList, callMethodType, - typeVarMap, skipUnknownArgCheck); + callResult = validateCallArguments( + errorNode, + argList, + callMethodType, + typeVarMap, + skipUnknownArgCheck + ); } break; } @@ -3617,10 +4025,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { getFileInfo(errorNode).diagnosticSettings.reportOptionalCall, DiagnosticRule.reportOptionalCall, `Object of type 'None' cannot be called`, - errorNode); + errorNode + ); } else { - const subtypeCallResult = validateCallArguments(errorNode, - argList, type, typeVarMap, skipUnknownArgCheck); + const subtypeCallResult = validateCallArguments( + errorNode, + argList, + type, + typeVarMap, + skipUnknownArgCheck + ); if (subtypeCallResult.returnType) { returnTypes.push(subtypeCallResult.returnType); } @@ -3641,16 +4055,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // list and reports any mismatches in types or counts. Returns the // specialized return type of the call. // This logic is based on PEP 3102: https://www.python.org/dev/peps/pep-3102/ - function validateFunctionArguments(errorNode: ExpressionNode, - argList: FunctionArgument[], type: FunctionType, typeVarMap: TypeVarMap, - skipUnknownArgCheck: boolean): CallResult { - + function validateFunctionArguments( + errorNode: ExpressionNode, + argList: FunctionArgument[], + type: FunctionType, + typeVarMap: TypeVarMap, + skipUnknownArgCheck: boolean + ): CallResult { let argIndex = 0; const typeParams = type.details.parameters; // The last parameter might be a var arg dictionary. If so, strip it off. - const varArgDictParam = typeParams.find( - param => param.category === ParameterCategory.VarArgDictionary); + const varArgDictParam = typeParams.find(param => param.category === ParameterCategory.VarArgDictionary); let reportedArgError = false; // Build a map of parameters by name. @@ -3667,18 +4083,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Is there a bare (nameless) "*" parameter? If so, it signifies the end // of the positional parameter list. let positionalParamCount = typeParams.findIndex( - param => param.category === ParameterCategory.VarArgList && !param.name); + param => param.category === ParameterCategory.VarArgList && !param.name + ); // Is there a positional-only "/" parameter? If so, it separates the // positional-only from positional or keyword parameters. const positionalOnlyIndex = typeParams.findIndex( - param => param.category === ParameterCategory.Simple && !param.name); + param => param.category === ParameterCategory.Simple && !param.name + ); // Is there a var-arg (named "*") parameter? If so, it is the last of // the positional parameters. if (positionalParamCount < 0) { - positionalParamCount = typeParams.findIndex( - param => param.category === ParameterCategory.VarArgList); + positionalParamCount = typeParams.findIndex(param => param.category === ParameterCategory.VarArgList); if (positionalParamCount >= 0) { positionalParamCount++; } @@ -3687,8 +4104,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Is there a keyword var-arg ("**") parameter? If so, it's not included // in the list of positional parameters. if (positionalParamCount < 0) { - positionalParamCount = typeParams.findIndex( - param => param.category === ParameterCategory.VarArgDictionary); + positionalParamCount = typeParams.findIndex(param => param.category === ParameterCategory.VarArgDictionary); } // If we didn't see any special cases, then all parameters are positional. @@ -3699,8 +4115,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Determine how many positional args are being passed before // we see a named arg. let positionalArgCount = argList.findIndex( - arg => arg.argumentCategory === ArgumentCategory.UnpackedDictionary || - arg.name !== undefined); + arg => arg.argumentCategory === ArgumentCategory.UnpackedDictionary || arg.name !== undefined + ); if (positionalArgCount < 0) { positionalArgCount = argList.length; } @@ -3730,9 +4146,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (argList[argIndex].argumentCategory !== ArgumentCategory.UnpackedList) { const adjustedCount = positionalParamCount; addError( - `Expected ${ adjustedCount } positional ` + - `${ adjustedCount === 1 ? 'argument' : 'arguments' }`, - argList[argIndex].valueExpression || errorNode); + `Expected ${adjustedCount} positional ` + `${adjustedCount === 1 ? 'argument' : 'arguments'}`, + argList[argIndex].valueExpression || errorNode + ); reportedArgError = true; } break; @@ -3743,8 +4159,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Assume the unpacked list fills the remaining positional args. if (argList[argIndex].valueExpression) { const listElementType = getTypeFromIterable( - getTypeForArgument(argList[argIndex]), false, - argList[argIndex].valueExpression!, false); + getTypeForArgument(argList[argIndex]), + false, + argList[argIndex].valueExpression!, + false + ); const funcArg: FunctionArgument = { argumentCategory: ArgumentCategory.Simple, type: listElementType @@ -3789,8 +4208,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!reportedArgError) { let foundUnpackedDictionaryArg = false; - const foundUnpackedListArg = argList.find( - arg => arg.argumentCategory === ArgumentCategory.UnpackedList) !== undefined; + const foundUnpackedListArg = + argList.find(arg => arg.argumentCategory === ArgumentCategory.UnpackedList) !== undefined; // Now consume any named parameters. while (argIndex < argList.length) { @@ -3808,14 +4227,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const paramEntry = paramMap.get(paramNameValue); if (paramEntry) { if (paramEntry.argsReceived > 0) { - addError( - `Parameter '${ paramNameValue }' is already assigned`, paramName); + addError(`Parameter '${paramNameValue}' is already assigned`, paramName); reportedArgError = true; } else { paramMap.get(paramName.value)!.argsReceived++; - const paramInfoIndex = typeParams.findIndex( - param => param.name === paramNameValue); + const paramInfoIndex = typeParams.findIndex(param => param.name === paramNameValue); assert(paramInfoIndex >= 0); const paramType = FunctionType.getEffectiveParameterType(type, paramInfoIndex); @@ -3836,8 +4253,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { paramName: paramNameValue }); } else { - addError( - `No parameter named '${ paramName.value }'`, paramName); + addError(`No parameter named '${paramName.value}'`, paramName); reportedArgError = true; } } @@ -3850,9 +4266,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // but have not yet received them. If we received a dictionary argument // (i.e. an arg starting with a "**") or a list argument (i.e. an arg // starting with a "*"), we will assume that all parameters are matched. - if (!foundUnpackedDictionaryArg && !foundUnpackedListArg && - !FunctionType.isDefaultParameterCheckDisabled(type)) { - + if ( + !foundUnpackedDictionaryArg && + !foundUnpackedListArg && + !FunctionType.isDefaultParameterCheckDisabled(type) + ) { const unassignedParams = [...paramMap.keys()].filter(name => { const entry = paramMap.get(name)!; return entry.argsReceived < entry.argsNeeded; @@ -3860,13 +4278,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (unassignedParams.length > 0) { addError( - `Argument missing for parameter${ unassignedParams.length === 1 ? '' : 's' } ` + - unassignedParams.map(p => `'${ p }'`).join(', '), errorNode); + `Argument missing for parameter${unassignedParams.length === 1 ? '' : 's'} ` + + unassignedParams.map(p => `'${p}'`).join(', '), + errorNode + ); reportedArgError = true; } } } - + // Special-case a few built-in calls that are often used for // casting or checking for unknown types. if (['cast', 'isinstance', 'issubclass'].some(name => name === type.details.builtInName)) { @@ -3910,21 +4330,21 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); } - const returnType = specializeType(getFunctionEffectiveReturnType( - type, validateArgTypeParams), typeVarMap); - + const returnType = specializeType(getFunctionEffectiveReturnType(type, validateArgTypeParams), typeVarMap); + return { argumentErrors: reportedArgError, returnType }; } - function validateArgType(argParam: ValidateArgTypeParams, typeVarMap: TypeVarMap, - skipUnknownCheck: boolean): boolean { - + function validateArgType( + argParam: ValidateArgTypeParams, + typeVarMap: TypeVarMap, + skipUnknownCheck: boolean + ): boolean { let argType: Type | undefined; if (argParam.argument.valueExpression) { - let expectedType: Type | undefined = specializeType( - argParam.paramType, typeVarMap); - + let expectedType: Type | undefined = specializeType(argParam.paramType, typeVarMap); + // If the expected type is unknown, don't use an expected type. Instead, // use default rules for evaluating the expression type. if (expectedType.category === TypeCategory.Unknown) { @@ -3943,21 +4363,25 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const diag = new DiagnosticAddendum(); if (!canAssignType(argParam.paramType, argType, diag.createAddendum(), typeVarMap)) { - const optionalParamName = argParam.paramName ? `'${ argParam.paramName }' ` : ''; + const optionalParamName = argParam.paramName ? `'${argParam.paramName}' ` : ''; addError( - `Argument of type '${ printType(argType) }'` + - ` cannot be assigned to parameter ${ optionalParamName }` + - `of type '${ printType(argParam.paramType) }'` + - diag.getString(), - argParam.errorNode); + `Argument of type '${printType(argType)}'` + + ` cannot be assigned to parameter ${optionalParamName}` + + `of type '${printType(argParam.paramType)}'` + + diag.getString(), + argParam.errorNode + ); return false; } else if (!skipUnknownCheck) { const simplifiedType = removeUnboundFromUnion(argType); const fileInfo = getFileInfo(argParam.errorNode); if (simplifiedType.category === TypeCategory.Unknown) { - addDiagnostic(fileInfo.diagnosticSettings.reportUnknownArgumentType, + addDiagnostic( + fileInfo.diagnosticSettings.reportUnknownArgumentType, DiagnosticRule.reportUnknownArgumentType, - `Type of argument is unknown`, argParam.errorNode); + `Type of argument is unknown`, + argParam.errorNode + ); } else if (containsUnknown(simplifiedType, true)) { // Don't report an error if the type is a partially-specialized // class. This comes up frequently in cases where a type is passed @@ -3968,11 +4392,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // arose due to bidirectional type matching. if (!containsUnknown(argParam.paramType) && simplifiedType.category !== TypeCategory.Class) { const diagAddendum = new DiagnosticAddendum(); - diagAddendum.addMessage(`Argument type is '${ printType(simplifiedType) }'`); - addDiagnostic(fileInfo.diagnosticSettings.reportUnknownArgumentType, + diagAddendum.addMessage(`Argument type is '${printType(simplifiedType)}'`); + addDiagnostic( + fileInfo.diagnosticSettings.reportUnknownArgumentType, DiagnosticRule.reportUnknownArgumentType, `Type of argument is partially unknown` + diagAddendum.getString(), - argParam.errorNode); + argParam.errorNode + ); } } } @@ -3991,8 +4417,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (firstArg.valueExpression && firstArg.valueExpression.nodeType === ParseNodeType.StringList) { typeVarName = firstArg.valueExpression.strings.map(s => s.value).join(''); } else { - addError('Expected name of type var as first parameter', - firstArg.valueExpression || errorNode); + addError('Expected name of type var as first parameter', firstArg.valueExpression || errorNode); } const typeVar = TypeVarType.create(typeVarName); @@ -4006,21 +4431,21 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (paramName) { if (paramNameMap.get(paramName)) { addError( - `Duplicate parameter name '${ paramName }' not allowed`, - argList[i].valueExpression || errorNode); + `Duplicate parameter name '${paramName}' not allowed`, + argList[i].valueExpression || errorNode + ); } if (paramName === 'bound') { if (typeVar.constraints.length > 0) { addError( `A TypeVar cannot be both bound and constrained`, - argList[i].valueExpression || errorNode); + argList[i].valueExpression || errorNode + ); } else { const argType = getTypeForArgument(argList[i], true); if (requiresSpecialization(argType)) { - addError( - `A TypeVar bound type cannot be generic`, - argList[i].valueExpression || errorNode); + addError(`A TypeVar bound type cannot be generic`, argList[i].valueExpression || errorNode); } typeVar.boundType = convertClassToObject(argType); } @@ -4029,7 +4454,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (typeVar.isContravariant) { addError( `A TypeVar cannot be both covariant and contravariant`, - argList[i].valueExpression!); + argList[i].valueExpression! + ); } else { typeVar.isCovariant = true; } @@ -4039,29 +4465,27 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (typeVar.isContravariant) { addError( `A TypeVar cannot be both covariant and contravariant`, - argList[i].valueExpression!); + argList[i].valueExpression! + ); } else { typeVar.isContravariant = true; } } } else { - addError( - `'${ paramName }' is unknown parameter to TypeVar`, - argList[i].valueExpression || errorNode); + addError(`'${paramName}' is unknown parameter to TypeVar`, argList[i].valueExpression || errorNode); } paramNameMap.set(paramName, paramName); } else { if (typeVar.boundType) { - addError( - `A TypeVar cannot be both bound and constrained`, - argList[i].valueExpression || errorNode); + addError(`A TypeVar cannot be both bound and constrained`, argList[i].valueExpression || errorNode); } else { const argType = getTypeForArgument(argList[i], true); if (requiresSpecialization(argType)) { addError( `A TypeVar constraint type cannot be generic`, - argList[i].valueExpression || errorNode); + argList[i].valueExpression || errorNode + ); } TypeVarType.addConstraint(typeVar, convertClassToObject(argType)); } @@ -4085,17 +4509,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Creates a new custom enum class with named values. - function createEnumType(errorNode: ExpressionNode, enumClass: ClassType, - argList: FunctionArgument[]): ClassType { - + function createEnumType(errorNode: ExpressionNode, enumClass: ClassType, argList: FunctionArgument[]): ClassType { let className = 'enum'; if (argList.length === 0) { addError('Expected enum class name as first parameter', errorNode); } else { const nameArg = argList[0]; if (nameArg.argumentCategory !== ArgumentCategory.Simple) { - addError('Expected enum class name as first parameter', - argList[0].valueExpression || errorNode); + addError('Expected enum class name as first parameter', argList[0].valueExpression || errorNode); } else if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) { className = nameArg.valueExpression.strings.map(s => s.value).join(''); } @@ -4105,20 +4526,26 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { classType.details.baseClasses.push(enumClass); const classFields = classType.details.fields; - classFields.set('__class__', Symbol.createWithType( - SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType)); + classFields.set( + '__class__', + Symbol.createWithType(SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType) + ); if (argList.length < 2) { addError('Expected enum item string as second parameter', errorNode); } else { const entriesArg = argList[1]; - if (entriesArg.argumentCategory !== ArgumentCategory.Simple || + if ( + entriesArg.argumentCategory !== ArgumentCategory.Simple || !entriesArg.valueExpression || - entriesArg.valueExpression.nodeType !== ParseNodeType.StringList) { - + entriesArg.valueExpression.nodeType !== ParseNodeType.StringList + ) { addError('Expected enum item string as second parameter', errorNode); } else { - const entries = entriesArg.valueExpression.strings.map(s => s.value).join('').split(' '); + const entries = entriesArg.valueExpression.strings + .map(s => s.value) + .join('') + .split(' '); entries.forEach(entryName => { entryName = entryName.trim(); if (entryName) { @@ -4136,8 +4563,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { node: stringNode as StringListNode, path: getFileInfo(errorNode).filePath, range: convertOffsetsToRange( - stringNode.start, TextRange.getEnd(stringNode), - getFileInfo(errorNode).lines) + stringNode.start, + TextRange.getEnd(stringNode), + getFileInfo(errorNode).lines + ) }; newSymbol.addDeclaration(declaration); classFields.set(entryName, newSymbol); @@ -4167,8 +4596,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const baseClass = getTypeForArgument(argList[1], true); if (baseClass.category === TypeCategory.Class) { - const classFlags = baseClass.details.flags & ~( - ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn); + const classFlags = + baseClass.details.flags & ~(ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn); const classType = ClassType.create(className, classFlags, errorNode.id); classType.details.baseClasses.push(baseClass); return classType; @@ -4180,19 +4609,22 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Creates a new custom TypedDict factory class. // Supports both typed and untyped variants. - function createTypedDictType(errorNode: ExpressionNode, typedDictClass: ClassType, - argList: FunctionArgument[]): ClassType { - + function createTypedDictType( + errorNode: ExpressionNode, + typedDictClass: ClassType, + argList: FunctionArgument[] + ): ClassType { let className = 'TypedDict'; if (argList.length === 0) { addError('Expected TypedDict class name as first parameter', errorNode); } else { const nameArg = argList[0]; - if (nameArg.argumentCategory !== ArgumentCategory.Simple || + if ( + nameArg.argumentCategory !== ArgumentCategory.Simple || !nameArg.valueExpression || - nameArg.valueExpression.nodeType !== ParseNodeType.StringList) { - addError('Expected TypedDict class name as first parameter', - argList[0].valueExpression || errorNode); + nameArg.valueExpression.nodeType !== ParseNodeType.StringList + ) { + addError('Expected TypedDict class name as first parameter', argList[0].valueExpression || errorNode); } else { className = nameArg.valueExpression.strings.map(s => s.value).join(''); } @@ -4202,15 +4634,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { classType.details.baseClasses.push(typedDictClass); if (argList.length >= 3) { - if (!argList[2].name || + if ( + !argList[2].name || argList[2].name.value !== 'total' || !argList[2].valueExpression || argList[2].valueExpression.nodeType !== ParseNodeType.Constant || - !(argList[2].valueExpression.constType === KeywordType.False || - argList[2].valueExpression.constType === KeywordType.True)) { - - addError(`Expected 'total' parameter to have a value of 'True' or 'False'`, - argList[2].valueExpression || errorNode); + !( + argList[2].valueExpression.constType === KeywordType.False || + argList[2].valueExpression.constType === KeywordType.True + ) + ) { + addError( + `Expected 'total' parameter to have a value of 'True' or 'False'`, + argList[2].valueExpression || errorNode + ); } else if (argList[2].valueExpression.constType === KeywordType.False) { classType.details.flags |= ClassTypeFlags.CanOmitDictValues; } @@ -4221,16 +4658,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const classFields = classType.details.fields; - classFields.set('__class__', Symbol.createWithType( - SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType)); + classFields.set( + '__class__', + Symbol.createWithType(SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType) + ); if (argList.length < 2) { addError('Expected dict as second parameter', errorNode); } else { const entriesArg = argList[1]; - if (entriesArg.argumentCategory !== ArgumentCategory.Simple || + if ( + entriesArg.argumentCategory !== ArgumentCategory.Simple || !entriesArg.valueExpression || - entriesArg.valueExpression.nodeType !== ParseNodeType.Dictionary) { + entriesArg.valueExpression.nodeType !== ParseNodeType.Dictionary + ) { addError('Expected dict as second parameter', errorNode); } else { const entryDict = entriesArg.valueExpression; @@ -4249,14 +4690,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const entryName = entry.keyExpression.strings.map(s => s.value).join(''); if (!entryName) { - addError( - 'Names within a TypedDict cannot be empty', entry.keyExpression); + addError('Names within a TypedDict cannot be empty', entry.keyExpression); return; } if (entryMap.get(entryName)) { - addError( - 'Names within a named tuple must be unique', entry.keyExpression); + addError('Names within a named tuple must be unique', entry.keyExpression); return; } @@ -4273,8 +4712,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { path: getFileInfo(errorNode).filePath, typeAnnotationNode: entry.valueExpression, range: convertOffsetsToRange( - entry.keyExpression.start, TextRange.getEnd(entry.keyExpression), - getFileInfo(errorNode).lines) + entry.keyExpression.start, + TextRange.getEnd(entry.keyExpression), + getFileInfo(errorNode).lines + ) }; newSymbol.addDeclaration(declaration); @@ -4290,18 +4731,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Creates a new custom tuple factory class with named values. // Supports both typed and untyped variants. - function createNamedTupleType(errorNode: ExpressionNode, argList: FunctionArgument[], - includesTypes: boolean): ClassType { - + function createNamedTupleType( + errorNode: ExpressionNode, + argList: FunctionArgument[], + includesTypes: boolean + ): ClassType { let className = 'namedtuple'; if (argList.length === 0) { - addError('Expected named tuple class name as first parameter', - errorNode); + addError('Expected named tuple class name as first parameter', errorNode); } else { const nameArg = argList[0]; if (nameArg.argumentCategory !== ArgumentCategory.Simple) { - addError('Expected named tuple class name as first parameter', - argList[0].valueExpression || errorNode); + addError('Expected named tuple class name as first parameter', argList[0].valueExpression || errorNode); } else if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) { className = nameArg.valueExpression.strings.map(s => s.value).join(''); } @@ -4312,13 +4753,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { classType.details.baseClasses.push(builtInNamedTuple); const classFields = classType.details.fields; - classFields.set('__class__', Symbol.createWithType( - SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType)); + classFields.set( + '__class__', + Symbol.createWithType(SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType) + ); const builtInTupleType = getBuiltInType(errorNode, 'Tuple'); if (builtInTupleType.category === TypeCategory.Class) { - const constructorType = FunctionType.create(FunctionTypeFlags.ConstructorMethod | - FunctionTypeFlags.SynthesizedMethod); + const constructorType = FunctionType.create( + FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod + ); constructorType.details.declaredReturnType = ObjectType.create(classType); if (ParseTreeUtils.isAssignmentToDefaultsFollowingNamedTuple(errorNode)) { constructorType.details.flags |= FunctionTypeFlags.DisableDefaultChecks; @@ -4345,10 +4789,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (entriesArg.argumentCategory !== ArgumentCategory.Simple) { addGenericGetAttribute = true; } else { - if (!includesTypes && entriesArg.valueExpression && - entriesArg.valueExpression.nodeType === ParseNodeType.StringList) { - - const entries = entriesArg.valueExpression.strings.map(s => s.value).join('').split(/[,\s]+/); + if ( + !includesTypes && + entriesArg.valueExpression && + entriesArg.valueExpression.nodeType === ParseNodeType.StringList + ) { + const entries = entriesArg.valueExpression.strings + .map(s => s.value) + .join('') + .split(/[,\s]+/); entries.forEach(entryName => { entryName = entryName.trim(); if (entryName) { @@ -4372,14 +4821,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { node: stringNode as StringListNode, path: getFileInfo(errorNode).filePath, range: convertOffsetsToRange( - stringNode.start, TextRange.getEnd(stringNode), - getFileInfo(errorNode).lines) + stringNode.start, + TextRange.getEnd(stringNode), + getFileInfo(errorNode).lines + ) }; newSymbol.addDeclaration(declaration); classFields.set(entryName, newSymbol); } }); - } else if (entriesArg.valueExpression && entriesArg.valueExpression.nodeType === ParseNodeType.List) { + } else if ( + entriesArg.valueExpression && + entriesArg.valueExpression.nodeType === ParseNodeType.List + ) { const entryList = entriesArg.valueExpression; const entryMap = new Map(); @@ -4394,14 +4848,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (entry.nodeType === ParseNodeType.Tuple && entry.expressions.length === 2) { entryNameNode = entry.expressions[0]; entryTypeNode = entry.expressions[1]; - const entryTypeInfo = getTypeOfExpression(entryTypeNode, undefined, - EvaluatorFlags.EvaluateStringLiteralAsType); + const entryTypeInfo = getTypeOfExpression( + entryTypeNode, + undefined, + EvaluatorFlags.EvaluateStringLiteralAsType + ); if (entryTypeInfo) { entryType = convertClassToObject(entryTypeInfo.type); } } else { - addError( - 'Expected two-entry tuple specifying entry name and type', entry); + addError('Expected two-entry tuple specifying entry name and type', entry); } } else { entryNameNode = entry; @@ -4411,21 +4867,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (entryNameNode && entryNameNode.nodeType === ParseNodeType.StringList) { entryName = entryNameNode.strings.map(s => s.value).join(''); if (!entryName) { - addError( - 'Names within a named tuple cannot be empty', entryNameNode); + addError('Names within a named tuple cannot be empty', entryNameNode); } } else { - addError( - 'Expected string literal for entry name', entryNameNode || entry); + addError('Expected string literal for entry name', entryNameNode || entry); } if (!entryName) { - entryName = `_${ index.toString() }`; + entryName = `_${index.toString()}`; } if (entryMap.has(entryName)) { - addError( - 'Names within a named tuple must be unique', entryNameNode || entry); + addError('Names within a named tuple must be unique', entryNameNode || entry); } // Record names in a map to detect duplicates. @@ -4451,8 +4904,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { path: getFileInfo(errorNode).filePath, typeAnnotationNode: entryTypeNode, range: convertOffsetsToRange( - entryNameNode.start, TextRange.getEnd(entryNameNode), - getFileInfo(errorNode).lines) + entryNameNode.start, + TextRange.getEnd(entryNameNode), + getFileInfo(errorNode).lines + ) }; newSymbol.addDeclaration(declaration); } @@ -4474,8 +4929,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // will handle property type checking. We may need to disable default // parameter processing for __new__ (see isAssignmentToDefaultsFollowingNamedTuple), // and we don't want to do it for __init__ as well. - const initType = FunctionType.create(FunctionTypeFlags.SynthesizedMethod | - FunctionTypeFlags.SkipConstructorCheck); + const initType = FunctionType.create( + FunctionTypeFlags.SynthesizedMethod | FunctionTypeFlags.SkipConstructorCheck + ); FunctionType.addParameter(initType, selfParameter); addDefaultFunctionParameters(initType); initType.details.declaredReturnType = NoneType.create(); @@ -4484,8 +4940,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { classFields.set('__init__', Symbol.createWithType(SymbolFlags.ClassMember, initType)); const keysItemType = FunctionType.create(FunctionTypeFlags.SynthesizedMethod); - keysItemType.details.declaredReturnType = getBuiltInObject(errorNode, 'list', - [getBuiltInObject(errorNode, 'str')]); + keysItemType.details.declaredReturnType = getBuiltInObject(errorNode, 'list', [ + getBuiltInObject(errorNode, 'str') + ]); classFields.set('keys', Symbol.createWithType(SymbolFlags.InstanceMember, keysItemType)); classFields.set('items', Symbol.createWithType(SymbolFlags.InstanceMember, keysItemType)); @@ -4515,10 +4972,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (node.constType === KeywordType.None) { type = NoneType.create(); - } else if (node.constType === KeywordType.True || - node.constType === KeywordType.False || - node.constType === KeywordType.Debug) { - + } else if ( + node.constType === KeywordType.True || + node.constType === KeywordType.False || + node.constType === KeywordType.Debug + ) { type = getBuiltInObject(node, 'bool'); // For True and False, we can create truthy and falsy @@ -4560,9 +5018,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { addDiagnostic( getFileInfo(node).diagnosticSettings.reportOptionalOperand, DiagnosticRule.reportOptionalOperand, - `Operator '${ ParseTreeUtils.printOperator(node.operator) }' not ` + - `supported for 'None' type`, - node.expression); + `Operator '${ParseTreeUtils.printOperator(node.operator)}' not ` + `supported for 'None' type`, + node.expression + ); exprType = removeNoneFromUnion(exprType); } } @@ -4578,14 +5036,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { type = exprType; } else { const magicMethodName = unaryOperatorMap[node.operator]; - type = getTypeFromMagicMethodReturn(exprType, [], - magicMethodName, node); + type = getTypeFromMagicMethodReturn(exprType, [], magicMethodName, node); } if (!type) { - addError(`Operator '${ ParseTreeUtils.printOperator(node.operator) }'` + - ` not supported for type '${ printType(exprType) }'`, - node); + addError( + `Operator '${ParseTreeUtils.printOperator(node.operator)}'` + + ` not supported for type '${printType(exprType)}'`, + node + ); type = UnknownType.create(); } } @@ -4600,9 +5059,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // we need to change the behavior to accommodate python's "chained // comparisons" feature. if (comparisonOperatorMap[node.operator]) { - if (node.leftExpression.nodeType === ParseNodeType.BinaryOperation && - comparisonOperatorMap[node.leftExpression.operator]) { - + if ( + node.leftExpression.nodeType === ParseNodeType.BinaryOperation && + comparisonOperatorMap[node.leftExpression.operator] + ) { leftExpression = node.leftExpression.rightExpression; } } @@ -4625,9 +5085,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { addDiagnostic( getFileInfo(node).diagnosticSettings.reportOptionalOperand, DiagnosticRule.reportOptionalOperand, - `Operator '${ ParseTreeUtils.printOperator(node.operator) }' not ` + - `supported for 'None' type`, - node.leftExpression); + `Operator '${ParseTreeUtils.printOperator(node.operator)}' not ` + `supported for 'None' type`, + node.leftExpression + ); } leftType = removeNoneFromUnion(leftType); } @@ -4682,9 +5142,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return doForSubtypes(rightType, rightSubtype => { if (isAnyOrUnknown(leftSubtype) || isAnyOrUnknown(rightSubtype)) { // If either type is "Unknown" (versus Any), propagate the Unknown. - if (leftSubtype.category === TypeCategory.Unknown || - rightSubtype.category === TypeCategory.Unknown) { - + if ( + leftSubtype.category === TypeCategory.Unknown || + rightSubtype.category === TypeCategory.Unknown + ) { return UnknownType.create(); } else { return AnyType.create(); @@ -4692,8 +5153,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const magicMethodName = operatorMap[node.operator][0]; - return getTypeFromMagicMethodReturn(leftSubtype, [rightSubtype], - magicMethodName, node); + return getTypeFromMagicMethodReturn(leftSubtype, [rightSubtype], magicMethodName, node); }); }); @@ -4707,9 +5167,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return type; } - function validateBinaryOperation(operator: OperatorType, leftType: Type, rightType: Type, - errorNode: ExpressionNode): Type { - + function validateBinaryOperation( + operator: OperatorType, + leftType: Type, + rightType: Type, + errorNode: ExpressionNode + ): Type { let type: Type | undefined; if (arithmeticOperatorMap[operator]) { @@ -4717,9 +5180,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return doForSubtypes(rightType, rightSubtype => { if (isAnyOrUnknown(leftSubtype) || isAnyOrUnknown(rightSubtype)) { // If either type is "Unknown" (versus Any), propagate the Unknown. - if (leftSubtype.category === TypeCategory.Unknown || - rightSubtype.category === TypeCategory.Unknown) { - + if ( + leftSubtype.category === TypeCategory.Unknown || + rightSubtype.category === TypeCategory.Unknown + ) { return UnknownType.create(); } else { return AnyType.create(); @@ -4727,15 +5191,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const magicMethodName = arithmeticOperatorMap[operator][0]; - const resultType = getTypeFromMagicMethodReturn(leftSubtype, [rightSubtype], - magicMethodName, errorNode); + const resultType = getTypeFromMagicMethodReturn( + leftSubtype, + [rightSubtype], + magicMethodName, + errorNode + ); if (resultType) { return resultType; } const altMagicMethodName = arithmeticOperatorMap[operator][1]; - return getTypeFromMagicMethodReturn(rightSubtype, [leftSubtype], - altMagicMethodName, errorNode); + return getTypeFromMagicMethodReturn(rightSubtype, [leftSubtype], altMagicMethodName, errorNode); }); }); } else if (bitwiseOperatorMap[operator]) { @@ -4743,9 +5210,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return doForSubtypes(rightType, rightSubtype => { if (isAnyOrUnknown(leftSubtype) || isAnyOrUnknown(rightSubtype)) { // If either type is "Unknown" (versus Any), propagate the Unknown. - if (leftSubtype.category === TypeCategory.Unknown || - rightSubtype.category === TypeCategory.Unknown) { - + if ( + leftSubtype.category === TypeCategory.Unknown || + rightSubtype.category === TypeCategory.Unknown + ) { return UnknownType.create(); } else { return AnyType.create(); @@ -4754,8 +5222,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Handle the general case. const magicMethodName = bitwiseOperatorMap[operator][0]; - return getTypeFromMagicMethodReturn(leftSubtype, [rightSubtype], - magicMethodName, errorNode); + return getTypeFromMagicMethodReturn(leftSubtype, [rightSubtype], magicMethodName, errorNode); }); }); } else if (comparisonOperatorMap[operator]) { @@ -4763,9 +5230,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return doForSubtypes(rightType, rightSubtype => { if (isAnyOrUnknown(leftSubtype) || isAnyOrUnknown(rightSubtype)) { // If either type is "Unknown" (versus Any), propagate the Unknown. - if (leftSubtype.category === TypeCategory.Unknown || - rightSubtype.category === TypeCategory.Unknown) { - + if ( + leftSubtype.category === TypeCategory.Unknown || + rightSubtype.category === TypeCategory.Unknown + ) { return UnknownType.create(); } else { return AnyType.create(); @@ -4773,15 +5241,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const magicMethodName = comparisonOperatorMap[operator][0]; - const resultType = getTypeFromMagicMethodReturn(leftSubtype, [rightSubtype], - magicMethodName, errorNode); + const resultType = getTypeFromMagicMethodReturn( + leftSubtype, + [rightSubtype], + magicMethodName, + errorNode + ); if (resultType) { return resultType; } const altMagicMethodName = comparisonOperatorMap[operator][1]; - return getTypeFromMagicMethodReturn(rightSubtype, [leftSubtype], - altMagicMethodName, errorNode); + return getTypeFromMagicMethodReturn(rightSubtype, [leftSubtype], altMagicMethodName, errorNode); }); }); } else if (booleanOperatorMap[operator]) { @@ -4806,25 +5277,36 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (!type || type.category === TypeCategory.Never) { - addError(`Operator '${ ParseTreeUtils.printOperator(operator) }' not ` + - `supported for types '${ printType(leftType) }' and '${ printType(rightType) }'`, - errorNode); + addError( + `Operator '${ParseTreeUtils.printOperator(operator)}' not ` + + `supported for types '${printType(leftType)}' and '${printType(rightType)}'`, + errorNode + ); type = UnknownType.create(); } return type; } - function getTypeFromMagicMethodReturn(objType: Type, args: Type[], - magicMethodName: string, errorNode: ExpressionNode): Type | undefined { - + function getTypeFromMagicMethodReturn( + objType: Type, + args: Type[], + magicMethodName: string, + errorNode: ExpressionNode + ): Type | undefined { let magicMethodSupported = true; // Create a helper lambda for object subtypes. const handleObjectSubtype = (subtype: ObjectType, bindToClassType?: ClassType) => { - const magicMethodType = getTypeFromObjectMember(errorNode, - subtype, magicMethodName, { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup, bindToClassType); + const magicMethodType = getTypeFromObjectMember( + errorNode, + subtype, + magicMethodName, + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup, + bindToClassType + ); if (magicMethodType) { const functionArgs = args.map(arg => { @@ -4837,8 +5319,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let callResult: CallResult | undefined; useSpeculativeMode(() => { - callResult = validateCallArguments(errorNode, functionArgs, - magicMethodType, new TypeVarMap(), true); + callResult = validateCallArguments( + errorNode, + functionArgs, + magicMethodType, + new TypeVarMap(), + true + ); }); if (callResult!.argumentErrors) { @@ -4920,9 +5407,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - const inferredEntryType = entryTypes.length > 0 ? - combineTypes(entryTypes.map(t => stripLiteralValue(t))) : - AnyType.create(); + const inferredEntryType = + entryTypes.length > 0 ? combineTypes(entryTypes.map(t => stripLiteralValue(t))) : AnyType.create(); const type = getBuiltInObject(node, 'set', [inferredEntryType]); @@ -4957,7 +5443,6 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { keyTypes.push(getTypeOfExpression(entryNode.keyExpression, expectedKeyType).type); valueTypes.push(getTypeOfExpression(entryNode.valueExpression, expectedValueType).type); addUnknown = false; - } else if (entryNode.nodeType === ParseNodeType.DictionaryExpandEntry) { const unexpandedType = getTypeOfExpression(entryNode.expandExpression).type; if (isAnyOrUnknown(unexpandedType)) { @@ -4978,8 +5463,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } } else if (entryNode.nodeType === ParseNodeType.ListComprehension) { - const dictEntryType = getElementTypeFromListComprehension( - node.entries[0] as ListComprehensionNode); + const dictEntryType = getElementTypeFromListComprehension(node.entries[0] as ListComprehensionNode); // The result should be a Tuple if (dictEntryType.category === TypeCategory.Object) { @@ -5008,9 +5492,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return undefined; } - if (ClassType.isTypedDictClass(subtype.classType) && - canAssignToTypedDict(subtype.classType, keyTypes, valueTypes)) { - + if ( + ClassType.isTypedDictClass(subtype.classType) && + canAssignToTypedDict(subtype.classType, keyTypes, valueTypes) + ) { return subtype; } @@ -5205,7 +5690,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // It's not clear what we should do with a union type. For now, // simply use the first function in the union. expectedFunctionType = expectedType.subtypes.find( - t => t.category === TypeCategory.Function) as FunctionType; + t => t.category === TypeCategory.Function + ) as FunctionType; } } @@ -5247,9 +5733,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return { type, node }; } - function reportPossibleUnknownAssignment(diagLevel: DiagnosticLevel, rule: string, - target: NameNode, type: Type, errorNode: ExpressionNode) { - + function reportPossibleUnknownAssignment( + diagLevel: DiagnosticLevel, + rule: string, + target: NameNode, + type: Type, + errorNode: ExpressionNode + ) { // Don't bother if the feature is disabled. if (diagLevel === 'none') { return; @@ -5263,14 +5753,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const simplifiedType = removeUnboundFromUnion(type); if (simplifiedType.category === TypeCategory.Unknown) { - addDiagnostic(diagLevel, rule, - `Type of '${ nameValue }' is unknown`, errorNode); + addDiagnostic(diagLevel, rule, `Type of '${nameValue}' is unknown`, errorNode); } else if (containsUnknown(simplifiedType)) { const diagAddendum = new DiagnosticAddendum(); - diagAddendum.addMessage(`Type of ${ nameValue } is '${ printType(simplifiedType) }'`); - addDiagnostic(diagLevel, rule, - `Type of '${ nameValue }' is partially unknown` + diagAddendum.getString(), - errorNode); + diagAddendum.addMessage(`Type of ${nameValue} is '${printType(simplifiedType)}'`); + addDiagnostic( + diagLevel, + rule, + `Type of '${nameValue}' is partially unknown` + diagAddendum.getString(), + errorNode + ); } } @@ -5280,10 +5772,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // "Execute" the list comprehensions from start to finish. for (const comprehension of node.comprehensions) { if (comprehension.nodeType === ParseNodeType.ListComprehensionFor) { - const iterableType = stripLiteralValue( - getTypeOfExpression(comprehension.iterableExpression).type); - const itemType = getTypeFromIterable(iterableType, !!comprehension.isAsync, - comprehension.iterableExpression, false); + const iterableType = stripLiteralValue(getTypeOfExpression(comprehension.iterableExpression).type); + const itemType = getTypeFromIterable( + iterableType, + !!comprehension.isAsync, + comprehension.iterableExpression, + false + ); const targetExpr = comprehension.targetExpression; assignTypeToExpression(targetExpr, itemType, comprehension.iterableExpression); @@ -5321,9 +5816,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const diag = new DiagnosticAddendum(); if (!canAssignType(optionalIntObject, exprType, diag)) { - addError( - `Index for slice operation must be an int value or None` + diag.getString(), - indexExpr); + addError(`Index for slice operation must be an int value or None` + diag.getString(), indexExpr); } }; @@ -5363,7 +5856,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { FunctionType.addParameter(functionType, { category: ParameterCategory.Simple, - name: `p${ index.toString() }`, + name: `p${index.toString()}`, isNameSynthesized: true, type: convertClassToObject(entry.type) }); @@ -5458,8 +5951,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (!type) { - addError(`Type arguments for Literal must be None, int, bool, str, or bytes value`, - item); + addError(`Type arguments for Literal must be None, int, bool, str, or bytes value`, item); type = UnknownType.create(); } @@ -5507,9 +5999,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Creates one of several "special" types that are defined in typing.pyi // but not declared in their entirety. This includes the likes of "Tuple", // "Dict", etc. - function createSpecialType(classType: ClassType, typeArgs: TypeResult[] | undefined, - paramLimit?: number, allowEllipsis = false): Type { - + function createSpecialType( + classType: ClassType, + typeArgs: TypeResult[] | undefined, + paramLimit?: number, + allowEllipsis = false + ): Type { if (typeArgs) { // Verify that we didn't receive any inappropriate ellipses or modules. typeArgs.forEach((typeArg, index) => { @@ -5526,16 +6021,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); } - let typeArgTypes = typeArgs ? typeArgs.map( - t => convertClassToObject(t.type)) : []; + let typeArgTypes = typeArgs ? typeArgs.map(t => convertClassToObject(t.type)) : []; // Make sure the argument list count is correct. if (paramLimit !== undefined) { if (typeArgs && typeArgTypes.length > paramLimit) { addError( - `Expected at most ${ paramLimit } type ` + - `${ paramLimit === 1 ? 'argument' : 'arguments' }`, - typeArgs[paramLimit].node); + `Expected at most ${paramLimit} type ` + `${paramLimit === 1 ? 'argument' : 'arguments'}`, + typeArgs[paramLimit].node + ); typeArgTypes = typeArgTypes.slice(0, paramLimit); } else if (typeArgTypes.length < paramLimit) { // Fill up the remainder of the slots with unknown types. @@ -5587,8 +6081,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { function createGenericType(errorNode: ParseNode, classType: ClassType, typeArgs?: TypeResult[]): Type { // Make sure there's at least one type arg. if (!typeArgs || typeArgs.length === 0) { - addError( - `'Generic' requires at least one type argument`, errorNode); + addError(`'Generic' requires at least one type argument`, errorNode); } // Make sure that all of the type args are typeVars and are unique. @@ -5596,13 +6089,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (typeArgs) { typeArgs.forEach(typeArg => { if (!(typeArg.type.category === TypeCategory.TypeVar)) { - addError( - `Type argument for 'Generic' must be a type variable`, typeArg.node); + addError(`Type argument for 'Generic' must be a type variable`, typeArg.node); } else { for (const typeVar of uniqueTypeVars) { if (typeVar === typeArg.type) { - addError( - `Type argument for 'Generic' must be unique`, typeArg.node); + addError(`Type argument for 'Generic' must be unique`, typeArg.node); break; } } @@ -5640,12 +6131,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return typeOfExpr; } - function createSpecialBuiltInClass(node: ParseNode, assignedName: string, - aliasMapEntry: AliasMapEntry): ClassType { - - const specialClassType = ClassType.create(assignedName, + function createSpecialBuiltInClass(node: ParseNode, assignedName: string, aliasMapEntry: AliasMapEntry): ClassType { + const specialClassType = ClassType.create( + assignedName, ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn, - node.id); + node.id + ); const baseClassName = aliasMapEntry.alias ? aliasMapEntry.alias : 'object'; @@ -5671,9 +6162,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - if (aliasClass && aliasClass.category === TypeCategory.Class && - specialClassType.category === TypeCategory.Class) { - + if ( + aliasClass && + aliasClass.category === TypeCategory.Class && + specialClassType.category === TypeCategory.Class + ) { specialClassType.details.baseClasses.push(aliasClass); if (aliasMapEntry.alias) { @@ -5701,15 +6194,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const assignedName = nameNode.value; const specialTypes: { [name: string]: AliasMapEntry } = { - 'Tuple': { alias: 'tuple', module: 'builtins' }, - 'Generic': { alias: '', module: 'builtins' }, - 'Protocol': { alias: '', module: 'builtins' }, - 'Callable': { alias: '', module: 'builtins' }, - 'Type': { alias: 'type', module: 'builtins' }, - 'ClassVar': { alias: '', module: 'builtins' }, - 'Final': { alias: '', module: 'builtins' }, - 'Literal': { alias: '', module: 'builtins' }, - 'TypedDict': { alias: '_TypedDict', module: 'self' } + Tuple: { alias: 'tuple', module: 'builtins' }, + Generic: { alias: '', module: 'builtins' }, + Protocol: { alias: '', module: 'builtins' }, + Callable: { alias: '', module: 'builtins' }, + Type: { alias: 'type', module: 'builtins' }, + ClassVar: { alias: '', module: 'builtins' }, + Final: { alias: '', module: 'builtins' }, + Literal: { alias: '', module: 'builtins' }, + TypedDict: { alias: '_TypedDict', module: 'self' } }; const aliasMapEntry = specialTypes[assignedName]; @@ -5735,20 +6228,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const specialTypes: { [name: string]: AliasMapEntry } = { - 'overload': { alias: '', module: 'builtins' }, - 'TypeVar': { alias: '', module: 'builtins' }, - '_promote': { alias: '', module: 'builtins' }, - 'no_type_check': { alias: '', module: 'builtins' }, - 'NoReturn': { alias: '', module: 'builtins' }, - 'Union': { alias: '', module: 'builtins' }, - 'Optional': { alias: '', module: 'builtins' }, - 'List': { alias: 'list', module: 'builtins' }, - 'Dict': { alias: 'dict', module: 'builtins' }, - 'DefaultDict': { alias: 'defaultdict', module: 'collections' }, - 'Set': { alias: 'set', module: 'builtins' }, - 'FrozenSet': { alias: 'frozenset', module: 'builtins' }, - 'Deque': { alias: 'deque', module: 'collections' }, - 'ChainMap': { alias: 'ChainMap', module: 'collections' } + overload: { alias: '', module: 'builtins' }, + TypeVar: { alias: '', module: 'builtins' }, + _promote: { alias: '', module: 'builtins' }, + no_type_check: { alias: '', module: 'builtins' }, + NoReturn: { alias: '', module: 'builtins' }, + Union: { alias: '', module: 'builtins' }, + Optional: { alias: '', module: 'builtins' }, + List: { alias: 'list', module: 'builtins' }, + Dict: { alias: 'dict', module: 'builtins' }, + DefaultDict: { alias: 'defaultdict', module: 'collections' }, + Set: { alias: 'set', module: 'builtins' }, + FrozenSet: { alias: 'frozenset', module: 'builtins' }, + Deque: { alias: 'deque', module: 'collections' }, + ChainMap: { alias: 'ChainMap', module: 'collections' } }; const aliasMapEntry = specialTypes[assignedName]; @@ -5800,7 +6293,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Determine if the RHS is a constant boolean expression. // If so, assign it a literal type. const constExprValue = evaluateStaticBoolExpression( - node.rightExpression, fileInfo.executionEnvironment); + node.rightExpression, + fileInfo.executionEnvironment + ); if (constExprValue !== undefined) { const boolType = getBuiltInObject(node, 'bool'); if (boolType.category === TypeCategory.Object) { @@ -5813,16 +6308,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const diagAddendum = new DiagnosticAddendum(); if (canAssignType(declaredType, srcType, diagAddendum)) { // Constrain the resulting type to match the declared type. - srcType = narrowDeclaredTypeBasedOnAssignedType( - declaredType, srcType); + srcType = narrowDeclaredTypeBasedOnAssignedType(declaredType, srcType); } } // If this is an enum, transform the type as required. rightHandType = srcType; if (node.leftExpression.nodeType === ParseNodeType.Name && !node.typeAnnotationComment) { - rightHandType = transformTypeForPossibleEnumClass( - node.leftExpression, rightHandType); + rightHandType = transformTypeForPossibleEnumClass(node.leftExpression, rightHandType); } } } @@ -5865,8 +6358,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - classType = ClassType.create(node.name.value, classFlags, - node.id, ParseTreeUtils.getDocString(node.suite.statements)); + classType = ClassType.create( + node.name.value, + classFlags, + node.id, + ParseTreeUtils.getDocString(node.suite.statements) + ); // Some classes refer to themselves within type arguments used within // base classes. We'll register the partially-constructed class type @@ -5915,7 +6412,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { argType = UnknownType.create(); } else { if (ClassType.isBuiltIn(argType, 'Protocol')) { - if (!fileInfo.isStubFile && fileInfo.executionEnvironment.pythonVersion < PythonVersion.V37) { + if ( + !fileInfo.isStubFile && + fileInfo.executionEnvironment.pythonVersion < PythonVersion.V37 + ) { addError(`Use of 'Protocol' requires Python 3.7 or newer`, arg.valueExpression); } } @@ -5945,8 +6445,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } else if (ClassType.isTypedDictClass(classType) && !ClassType.isTypedDictClass(argType)) { // TypedDict classes must derive only from other // TypedDict classes. - addError(`All base classes for TypedDict classes must ` + - 'als be TypedDict classes', arg); + addError(`All base classes for TypedDict classes must ` + 'als be TypedDict classes', arg); } // Validate that the class isn't deriving from itself, creating a @@ -5958,14 +6457,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - if (argType.category === TypeCategory.Unknown || - argType.category === TypeCategory.Union && argType.subtypes.some(t => t.category === TypeCategory.Unknown)) { - + if ( + argType.category === TypeCategory.Unknown || + (argType.category === TypeCategory.Union && + argType.subtypes.some(t => t.category === TypeCategory.Unknown)) + ) { addDiagnostic( fileInfo.diagnosticSettings.reportUntypedBaseClass, DiagnosticRule.reportUntypedBaseClass, `Base class type is unknown, obscuring type of derived class`, - arg); + arg + ); } if (isMetaclass) { @@ -5995,8 +6497,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (ClassType.isFinal(argType)) { const className = printObjectTypeForClass(argType); addError( - `Base class '${ className }' is marked final and cannot be subclassed`, - arg.valueExpression); + `Base class '${className}' is marked final and cannot be subclassed`, + arg.valueExpression + ); } } } @@ -6019,7 +6522,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (ClassType.isTypedDictClass(classType)) { // PEP 589 specifies that the parameter must be either True or False. const constArgValue = evaluateStaticBoolExpression( - arg.valueExpression, fileInfo.executionEnvironment); + arg.valueExpression, + fileInfo.executionEnvironment + ); if (constArgValue === undefined) { addError('Value for total parameter must be True or False', arg.valueExpression); } else if (!constArgValue) { @@ -6065,8 +6570,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { for (let i = node.decorators.length - 1; i >= 0; i--) { const decorator = node.decorators[i]; - const newDecoratedType = applyClassDecorator(decoratedType, - classType, decorator); + const newDecoratedType = applyClassDecorator(decoratedType, classType, decorator); if (newDecoratedType.category === TypeCategory.Unknown) { // Report this error only on the first unknown type. if (!foundUnknown) { @@ -6074,7 +6578,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { fileInfo.diagnosticSettings.reportUntypedClassDecorator, DiagnosticRule.reportUntypedClassDecorator, `Untyped class decorator obscures type of class, ignoring decorator`, - node.decorators[i].leftExpression); + node.decorators[i].leftExpression + ); foundUnknown = true; } @@ -6089,8 +6594,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!skipSynthesizedInit) { // See if there's already a non-synthesized __init__ method. // We shouldn't override it. - const initSymbol = lookUpClassMember(classType, '__init__', - importLookup, ClassMemberLookupFlags.SkipBaseClasses); + const initSymbol = lookUpClassMember( + classType, + '__init__', + importLookup, + ClassMemberLookupFlags.SkipBaseClasses + ); if (initSymbol) { const initSymbolType = getTypeOfMember(initSymbol); if (initSymbolType.category === TypeCategory.Function) { @@ -6115,9 +6624,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return { classType, decoratedType }; } - function applyClassDecorator(inputClassType: Type, originalClassType: ClassType, - decoratorNode: DecoratorNode): Type { - + function applyClassDecorator( + inputClassType: Type, + originalClassType: ClassType, + decoratorNode: DecoratorNode + ): Type { const decoratorType = getTypeOfExpression(decoratorNode.leftExpression).type; // Is this a @dataclass? @@ -6133,7 +6644,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (arg.valueExpression) { const fileInfo = getFileInfo(decoratorNode); const value = evaluateStaticBoolExpression( - arg.valueExpression, fileInfo.executionEnvironment); + arg.valueExpression, + fileInfo.executionEnvironment + ); if (!value) { skipSynthesizeInit = true; } @@ -6195,8 +6708,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { functionFlags |= FunctionTypeFlags.Async; } - functionType = FunctionType.create(functionFlags, - ParseTreeUtils.getDocString(node.suite.statements)); + functionType = FunctionType.create(functionFlags, ParseTreeUtils.getDocString(node.suite.statements)); if (fileInfo.isBuiltInStubFile || fileInfo.isTypingStubFile) { // Stash away the name of the function since we need to handle @@ -6252,8 +6764,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let defaultValueType: Type | undefined; if (param.defaultValue) { - defaultValueType = getTypeOfExpression(param.defaultValue, annotatedType, - EvaluatorFlags.ConvertEllipsisToAny).type; + defaultValueType = getTypeOfExpression( + param.defaultValue, + annotatedType, + EvaluatorFlags.ConvertEllipsisToAny + ).type; } if (param.typeAnnotation && annotatedType) { @@ -6264,10 +6779,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!canAssignType(concreteAnnotatedType, defaultValueType, diagAddendum)) { const diag = addError( - `Value of type '${ printType(defaultValueType) }' cannot` + - ` be assigned to parameter of type '${ printType(annotatedType) }'` + - diagAddendum.getString(), - param.defaultValue); + `Value of type '${printType(defaultValueType)}' cannot` + + ` be assigned to parameter of type '${printType(annotatedType)}'` + + diagAddendum.getString(), + param.defaultValue + ); if (isNoneWithoutOptional) { const addOptionalAction: AddMissingOptionalToParamAction = { @@ -6294,8 +6810,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { FunctionType.addParameter(functionType, functionParam); if (param.name) { - const variadicParamType = transformVariadicParamType(node, - param.category, functionParam.type); + const variadicParamType = transformVariadicParamType(node, param.category, functionParam.type); paramTypes.push(variadicParamType); } else { paramTypes.push(functionParam.type); @@ -6307,8 +6822,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // provide a type if it's an instance, class or constructor method. if (functionType.details.parameters.length > 0 && !node.parameters[0].typeAnnotation) { if (containingClassType) { - const inferredParamType = inferFirstParamType( - functionType.details.flags, containingClassType); + const inferredParamType = inferFirstParamType(functionType.details.flags, containingClassType); if (inferredParamType) { functionType.details.parameters[0].type = inferredParamType; paramTypes[0] = inferredParamType; @@ -6329,8 +6843,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); // If it's an async function, wrap the return type in an Awaitable or Generator. - const preDecoratedType = node.isAsync ? - createAwaitableFunction(node, functionType) : functionType; + const preDecoratedType = node.isAsync ? createAwaitableFunction(node, functionType) : functionType; // Apply all of the decorators in reverse order. decoratedType = preDecoratedType; @@ -6346,7 +6859,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { fileInfo.diagnosticSettings.reportUntypedFunctionDecorator, DiagnosticRule.reportUntypedFunctionDecorator, `Untyped function decorator obscures type of function, ignoring decorator`, - node.decorators[i].leftExpression); + node.decorators[i].leftExpression + ); foundUnknown = true; } @@ -6438,8 +6952,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } for (const decoratorNode of node.decorators) { - const decoratorType = getTypeOfExpression(decoratorNode.leftExpression, - undefined, EvaluatorFlags.DoNotSpecialize).type; + const decoratorType = getTypeOfExpression( + decoratorNode.leftExpression, + undefined, + EvaluatorFlags.DoNotSpecialize + ).type; if (decoratorType.category === TypeCategory.Function) { if (decoratorType.details.builtInName === 'abstractmethod') { if (isInClass) { @@ -6466,16 +6983,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Transforms the input function type into an output type based on the // decorator function described by the decoratorNode. - function applyFunctionDecorator(inputFunctionType: Type, - originalFunctionType: FunctionType, decoratorNode: DecoratorNode): Type { - - const decoratorType = getTypeOfExpression(decoratorNode.leftExpression, - undefined, EvaluatorFlags.DoNotSpecialize).type; + function applyFunctionDecorator( + inputFunctionType: Type, + originalFunctionType: FunctionType, + decoratorNode: DecoratorNode + ): Type { + const decoratorType = getTypeOfExpression( + decoratorNode.leftExpression, + undefined, + EvaluatorFlags.DoNotSpecialize + ).type; // Special-case the "overload" because it has no definition. - if (decoratorType.category === TypeCategory.Class && - ClassType.isSpecialBuiltIn(decoratorType, 'overload')) { - + if (decoratorType.category === TypeCategory.Class && ClassType.isSpecialBuiltIn(decoratorType, 'overload')) { if (inputFunctionType.category === TypeCategory.Function) { inputFunctionType.details.flags |= FunctionTypeFlags.Overloaded; return inputFunctionType; @@ -6515,8 +7035,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Handle properties and subclasses of properties specially. if (ClassType.isPropertyClass(decoratorType)) { if (inputFunctionType.category === TypeCategory.Function) { - return createProperty(decoratorType.details.name, - inputFunctionType, decoratorNode.id); + return createProperty(decoratorType.details.name, inputFunctionType, decoratorNode.id); } } } @@ -6577,8 +7096,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const classType = (prop as ObjectType).classType; - const propertyClass = ClassType.create(classType.details.name, - classType.details.flags, classType.details.typeSourceId); + const propertyClass = ClassType.create( + classType.details.name, + classType.details.flags, + classType.details.typeSourceId + ); const propertyObject = ObjectType.create(propertyClass); // Clone the symbol table of the old class type. @@ -6607,10 +7129,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); setFunction.details.declaredReturnType = NoneType.create(); let setParamType: Type = UnknownType.create(); - if (fset.details.parameters.length >= 2 && - fset.details.parameters[1].category === ParameterCategory.Simple && - fset.details.parameters[1].name) { - + if ( + fset.details.parameters.length >= 2 && + fset.details.parameters[1].category === ParameterCategory.Simple && + fset.details.parameters[1].name + ) { setParamType = fset.details.parameters[1].type; } setFunction.details.parameters.push({ @@ -6630,8 +7153,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const classType = (prop as ObjectType).classType; - const propertyClass = ClassType.create(classType.details.name, - classType.details.flags, classType.details.typeSourceId); + const propertyClass = ClassType.create( + classType.details.name, + classType.details.flags, + classType.details.typeSourceId + ); const propertyObject = ObjectType.create(propertyClass); // Clone the symbol table of the old class type. @@ -6717,7 +7243,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (functionType.details.declaredReturnType) { awaitableFunctionType.details.declaredReturnType = createAwaitableReturnType( - node, functionType.details.declaredReturnType); + node, + functionType.details.declaredReturnType + ); } // Note that the inferred type, once lazily computed, needs to wrap the @@ -6746,12 +7274,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { typeArgs.push(generatorTypeArgs[1]); } awaitableReturnType = ObjectType.create( - ClassType.cloneForSpecialization(asyncGeneratorType, typeArgs)); + ClassType.cloneForSpecialization(asyncGeneratorType, typeArgs) + ); } - - } else if (['AsyncGenerator', 'AsyncIterator', 'AsyncIterable'].some( - name => name === classType.details.name)) { - + } else if ( + ['AsyncGenerator', 'AsyncIterator', 'AsyncIterable'].some(name => name === classType.details.name) + ) { // If it's already an AsyncGenerator, AsyncIterator or AsyncIterable, // leave it as is. awaitableReturnType = returnType; @@ -6762,8 +7290,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!awaitableReturnType) { const awaitableType = getTypingType(node, 'Awaitable'); if (awaitableType && awaitableType.category === TypeCategory.Class) { - awaitableReturnType = ObjectType.create( - ClassType.cloneForSpecialization(awaitableType, [returnType])); + awaitableReturnType = ObjectType.create(ClassType.cloneForSpecialization(awaitableType, [returnType])); } else { awaitableReturnType = UnknownType.create(); } @@ -6811,7 +7338,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const generatorType = getTypingType(node, 'Generator'); if (generatorType && generatorType.category === TypeCategory.Class) { inferredReturnType = ObjectType.create( - ClassType.cloneForSpecialization(generatorType, [inferredReturnType])); + ClassType.cloneForSpecialization(generatorType, [inferredReturnType]) + ); } else { inferredReturnType = UnknownType.create(); } @@ -6879,8 +7407,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const iteratorType = getTypeOfExpression(node.iterableExpression).type; - const iteratedType = getTypeFromIterable( - iteratorType, !!node.isAsync, node.iterableExpression, !node.isAsync); + const iteratedType = getTypeFromIterable(iteratorType, !!node.isAsync, node.iterableExpression, !node.isAsync); assignTypeToExpression(node.targetExpression, iteratedType, node.targetExpression); @@ -6907,8 +7434,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (exceptionType.category === TypeCategory.Object) { - const iterableType = getTypeFromIterable( - exceptionType, false, errorNode, false); + const iterableType = getTypeFromIterable(exceptionType, false, errorNode, false); return doForSubtypes(iterableType, subtype => { if (isAnyOrUnknown(subtype)) { @@ -6954,8 +7480,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } let exprType = getTypeOfExpression(node.expression).type; - const isAsync = node.parent && node.parent.nodeType === ParseNodeType.With && - !!node.parent.isAsync; + const isAsync = node.parent && node.parent.nodeType === ParseNodeType.With && !!node.parent.isAsync; if (isOptionalType(exprType)) { const fileInfo = getFileInfo(node); @@ -6963,7 +7488,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { fileInfo.diagnosticSettings.reportOptionalContextManager, DiagnosticRule.reportOptionalContextManager, `Object of type 'None' cannot be used with 'with'`, - node.expression); + node.expression + ); exprType = removeNoneFromUnion(exprType); } @@ -6975,8 +7501,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const diag = new DiagnosticAddendum(); if (subtype.category === TypeCategory.Object) { - const memberType = getTypeFromObjectMember(node.expression, - subtype, enterMethodName, { method: 'get' }, diag, MemberAccessFlags.None); + const memberType = getTypeFromObjectMember( + node.expression, + subtype, + enterMethodName, + { method: 'get' }, + diag, + MemberAccessFlags.None + ); if (memberType) { let memberReturnType: Type; @@ -6995,9 +7527,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - addError(`Type ${ printType(subtype) } cannot be used ` + - `with 'with' because it does not implement '${ enterMethodName }'` + diag.getString(), - node.expression); + addError( + `Type ${printType(subtype)} cannot be used ` + + `with 'with' because it does not implement '${enterMethodName}'` + + diag.getString(), + node.expression + ); return UnknownType.create(); }); @@ -7029,8 +7564,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Look up the symbol to find the alias declaration. - let symbolType = getAliasedSymbolTypeForName(node, symbolNameNode.value) || - UnknownType.create(); + let symbolType = getAliasedSymbolTypeForName(node, symbolNameNode.value) || UnknownType.create(); // Is there a cached module type associated with this node? If so, use // it instead of the type we just created. @@ -7071,13 +7605,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Handle PEP 562 support for module-level __getattr__ function, // introduced in Python 3.7. - if (fileInfo.executionEnvironment.pythonVersion < PythonVersion.V37 || - !importLookupInfo.symbolTable.get('__getattr__')) { - - addError( - `'${ node.name.value }' is unknown import symbol`, - node.name - ); + if ( + fileInfo.executionEnvironment.pythonVersion < PythonVersion.V37 || + !importLookupInfo.symbolTable.get('__getattr__') + ) { + addError(`'${node.name.value}' is unknown import symbol`, node.name); } } } @@ -7095,8 +7627,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return undefined; } - const aliasDecl = symbolWithScope.symbol.getDeclarations().find( - decl => decl.type === DeclarationType.Alias); + const aliasDecl = symbolWithScope.symbol.getDeclarations().find(decl => decl.type === DeclarationType.Alias); if (!aliasDecl) { return undefined; } @@ -7117,7 +7648,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let curNode: ParseNode | undefined = node; function isContextual(node: ParseNode) { - return node.nodeType === ParseNodeType.Call || + return ( + node.nodeType === ParseNodeType.Call || node.nodeType === ParseNodeType.Dictionary || node.nodeType === ParseNodeType.FormatString || node.nodeType === ParseNodeType.List || @@ -7129,7 +7661,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { node.nodeType === ParseNodeType.Unpack || node.nodeType === ParseNodeType.DictionaryKeyEntry || node.nodeType === ParseNodeType.DictionaryExpandEntry || - node.nodeType === ParseNodeType.ListComprehension; + node.nodeType === ParseNodeType.ListComprehension + ); } // Scan up the parse tree until we find a non-expression, looking for @@ -7170,9 +7703,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (parent.nodeType === ParseNodeType.Return && parent.returnExpression) { const enclosingFunctionNode = ParseTreeUtils.getEnclosingFunction(node); - const declaredReturnType = enclosingFunctionNode ? - getFunctionDeclaredReturnType(enclosingFunctionNode) : - undefined; + const declaredReturnType = enclosingFunctionNode + ? getFunctionDeclaredReturnType(enclosingFunctionNode) + : undefined; getTypeOfExpression(parent.returnExpression, declaredReturnType, EvaluatorFlags.None); return; } @@ -7180,8 +7713,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If the parent is an expression, we'll evaluate it to provide // the context for its child. If it's not, we'll evaluate the // child directly without any context. - const nodeToEvaluate = isExpressionNode(parent) && parent.nodeType !== ParseNodeType.Error ? - parent as ExpressionNode : lastContextualExpression; + const nodeToEvaluate = + isExpressionNode(parent) && parent.nodeType !== ParseNodeType.Error + ? (parent as ExpressionNode) + : lastContextualExpression; getTypeOfExpression(nodeToEvaluate); } @@ -7436,10 +7971,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If the function has yield expressions, it's a generator, and // we'll assume the yield statements are reachable. Also, don't // infer a "no return" type for abstract methods. - if (!functionType.details.declaration.yieldExpressions && - !FunctionType.isAbstractMethod(functionType) && - !FunctionType.isStubDefinition(functionType)) { - + if ( + !functionType.details.declaration.yieldExpressions && + !FunctionType.isAbstractMethod(functionType) && + !FunctionType.isStubDefinition(functionType) + ) { callIsNoReturn = !isAfterNodeReachable(functionType.details.declaration.node); } } @@ -7455,9 +7991,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Attempts to determine the type of the reference expression at the // point in the code. If the code flow analysis has nothing to say // about that expression, it return undefined. - function getFlowTypeOfReference(reference: NameNode | MemberAccessNode, - targetSymbolId: number, initialType: Type | undefined): Type | undefined { - + function getFlowTypeOfReference( + reference: NameNode | MemberAccessNode, + targetSymbolId: number, + initialType: Type | undefined + ): Type | undefined { // See if this execution scope requires code flow for this reference expression. const referenceKey = createKeyForReference(reference); const executionScope = ParseTreeUtils.getExecutionScopeNode(reference); @@ -7497,11 +8035,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { function createCodeFlowAnalyzer(): CodeFlowAnalyzer { const flowNodeTypeCacheSet = new Map>(); - function getTypeFromCodeFlow(reference: NameNode | MemberAccessNode, - targetSymbolId: number, initialType: Type | undefined): FlowNodeTypeResult { - + function getTypeFromCodeFlow( + reference: NameNode | MemberAccessNode, + targetSymbolId: number, + initialType: Type | undefined + ): FlowNodeTypeResult { const flowNode = AnalyzerNodeInfo.getFlowNode(reference); - const referenceKey = createKeyForReference(reference) + `.${ targetSymbolId.toString() }`; + const referenceKey = createKeyForReference(reference) + `.${targetSymbolId.toString()}`; let flowNodeTypeCache = flowNodeTypeCacheSet.get(referenceKey); if (!flowNodeTypeCache) { flowNodeTypeCache = new Map(); @@ -7509,19 +8049,23 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Caches the type of the flow node in our local cache, keyed by the flow node ID. - function setCacheEntry(flowNode: FlowNode, type: Type | undefined, - isIncomplete: boolean): FlowNodeTypeResult { - + function setCacheEntry( + flowNode: FlowNode, + type: Type | undefined, + isIncomplete: boolean + ): FlowNodeTypeResult { // For speculative or incomplete types, we'll create a separate // object. For non-speculative and complete types, we'll store // the type directly. const entry: PartialType | Type | undefined = - (isSpeculativeMode() || isIncomplete) ? { - category: partialTypeCategory, - type, - speculativeModeId, - isIncomplete - } : type; + isSpeculativeMode() || isIncomplete + ? { + category: partialTypeCategory, + type, + speculativeModeId, + isIncomplete + } + : type; flowNodeTypeCache!.set(flowNode.id, entry); @@ -7569,9 +8113,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { let nodeForCacheLookup: ParseNode = flowNode.node; const parentNode = flowNode.node.parent; if (parentNode) { - if (parentNode.nodeType === ParseNodeType.Function || - parentNode.nodeType === ParseNodeType.Class) { - + if (parentNode.nodeType === ParseNodeType.Function || parentNode.nodeType === ParseNodeType.Class) { nodeForCacheLookup = parentNode; } } @@ -7594,9 +8136,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If this flow has no knowledge of the target expression, it returns undefined. // If the start flow node for this scope is reachable, the typeAtStart value is // returned. - function getTypeFromFlowNode(flowNode: FlowNode, reference: NameNode | MemberAccessNode, - targetSymbolId: number, initialType: Type | undefined): FlowNodeTypeResult { - + function getTypeFromFlowNode( + flowNode: FlowNode, + reference: NameNode | MemberAccessNode, + targetSymbolId: number, + initialType: Type | undefined + ): FlowNodeTypeResult { let curFlowNode = flowNode; while (true) { @@ -7632,9 +8177,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Are we targeting the same symbol? We need to do this extra check because the same // symbol name might refer to different symbols in different scopes (e.g. a list // comprehension introduces a new scope). - if (targetSymbolId === assignmentFlowNode.targetSymbolId && - ParseTreeUtils.isMatchingExpression(reference, assignmentFlowNode.node)) { - + if ( + targetSymbolId === assignmentFlowNode.targetSymbolId && + ParseTreeUtils.isMatchingExpression(reference, assignmentFlowNode.node) + ) { // Is this a special "unbind" assignment? If so, // we can handle it immediately without any further evaluation. if (curFlowNode.flags & FlowFlags.Unbind) { @@ -7679,8 +8225,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } labelNode.antecedents.map((antecedent, index) => { - const flowTypeResult = getTypeFromFlowNode(antecedent, reference, - targetSymbolId, initialType); + const flowTypeResult = getTypeFromFlowNode( + antecedent, + reference, + targetSymbolId, + initialType + ); if (flowTypeResult.isIncomplete) { sawIncomplete = true; @@ -7709,24 +8259,30 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // For branch labels, don't write back the result if it's incomplete. - return sawIncomplete ? { type: effectiveType, isIncomplete: true } : - setCacheEntry(curFlowNode, effectiveType, false); + return sawIncomplete + ? { type: effectiveType, isIncomplete: true } + : setCacheEntry(curFlowNode, effectiveType, false); } if (curFlowNode.flags & (FlowFlags.TrueCondition | FlowFlags.FalseCondition)) { const conditionalFlowNode = curFlowNode as FlowCondition; const typeNarrowingCallback = getTypeNarrowingCallback(reference, conditionalFlowNode); if (typeNarrowingCallback) { - const flowTypeResult = getTypeFromFlowNode(conditionalFlowNode.antecedent, - reference, targetSymbolId, initialType); + const flowTypeResult = getTypeFromFlowNode( + conditionalFlowNode.antecedent, + reference, + targetSymbolId, + initialType + ); let flowType = flowTypeResult.type; if (flowType) { flowType = typeNarrowingCallback(flowType); } // If the type is incomplete, don't write back to the cache. - return flowTypeResult.isIncomplete ? { type: flowType, isIncomplete: true } : - setCacheEntry(curFlowNode, flowType, false); + return flowTypeResult.isIncomplete + ? { type: flowType, isIncomplete: true } + : setCacheEntry(curFlowNode, flowType, false); } curFlowNode = conditionalFlowNode.antecedent; @@ -7746,13 +8302,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const postFinallyFlowNode = curFlowNode as FlowPostFinally; const wasGateClosed = postFinallyFlowNode.preFinallyGate.isGateClosed; postFinallyFlowNode.preFinallyGate.isGateClosed = true; - const flowTypeResult = getTypeFromFlowNode(postFinallyFlowNode.antecedent, - reference, targetSymbolId, initialType); + const flowTypeResult = getTypeFromFlowNode( + postFinallyFlowNode.antecedent, + reference, + targetSymbolId, + initialType + ); postFinallyFlowNode.preFinallyGate.isGateClosed = wasGateClosed; // If the type is incomplete, don't write back to the cache. - return flowTypeResult.isIncomplete ? flowTypeResult : - setCacheEntry(curFlowNode, flowTypeResult.type, false); + return flowTypeResult.isIncomplete + ? flowTypeResult + : setCacheEntry(curFlowNode, flowTypeResult.type, false); } if (curFlowNode.flags & FlowFlags.Start) { @@ -7909,7 +8470,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // can be used to narrow the type described by the target expression. // If the specified flow node is not associated with the target expression, // it returns undefined. - function getTypeNarrowingCallback(reference: ExpressionNode, flowNode: FlowCondition): TypeNarrowingCallback | undefined { + function getTypeNarrowingCallback( + reference: ExpressionNode, + flowNode: FlowCondition + ): TypeNarrowingCallback | undefined { let testExpression = flowNode.expression; const isPositiveTest = !!(flowNode.flags & FlowFlags.TrueCondition); @@ -7922,22 +8486,24 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (testExpression.nodeType === ParseNodeType.BinaryOperation) { - const isOrIsNotOperator = testExpression.operator === OperatorType.Is || - testExpression.operator === OperatorType.IsNot; - const equalsOrNotEqualsOperator = testExpression.operator === OperatorType.Equals || - testExpression.operator === OperatorType.NotEquals; + const isOrIsNotOperator = + testExpression.operator === OperatorType.Is || testExpression.operator === OperatorType.IsNot; + const equalsOrNotEqualsOperator = + testExpression.operator === OperatorType.Equals || testExpression.operator === OperatorType.NotEquals; if (isOrIsNotOperator || equalsOrNotEqualsOperator) { // Invert the "isPositiveTest" value if this is an "is not" operation. - const adjIsPositiveTest = (testExpression.operator === OperatorType.Is || - testExpression.operator === OperatorType.Equals) ? - isPositiveTest : !isPositiveTest; + const adjIsPositiveTest = + testExpression.operator === OperatorType.Is || testExpression.operator === OperatorType.Equals + ? isPositiveTest + : !isPositiveTest; // Look for "X is None", "X is not None", "X == None", and "X != None". // These are commonly-used patterns used in control flow. - if (testExpression.rightExpression.nodeType === ParseNodeType.Constant && - testExpression.rightExpression.constType === KeywordType.None) { - + if ( + testExpression.rightExpression.nodeType === ParseNodeType.Constant && + testExpression.rightExpression.constType === KeywordType.None + ) { if (ParseTreeUtils.isMatchingExpression(reference, testExpression.leftExpression)) { // Narrow the type by filtering on "None". return (type: Type) => { @@ -7971,11 +8537,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Look for "type(X) is Y" or "type(X) is not Y". if (isOrIsNotOperator && testExpression.leftExpression.nodeType === ParseNodeType.Call) { const callType = getTypeOfExpression(testExpression.leftExpression.leftExpression).type; - if (callType.category === TypeCategory.Class && + if ( + callType.category === TypeCategory.Class && ClassType.isBuiltIn(callType, 'type') && testExpression.leftExpression.arguments.length === 1 && - testExpression.leftExpression.arguments[0].argumentCategory === ArgumentCategory.Simple) { - + testExpression.leftExpression.arguments[0].argumentCategory === ArgumentCategory.Simple + ) { const arg0Expr = testExpression.leftExpression.arguments[0].valueExpression; if (ParseTreeUtils.isMatchingExpression(reference, arg0Expr)) { const classType = getTypeOfExpression(testExpression.rightExpression).type; @@ -8007,10 +8574,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (testExpression.nodeType === ParseNodeType.Call) { if (testExpression.leftExpression.nodeType === ParseNodeType.Name) { // Look for "isinstance(X, Y)" or "issubclass(X, Y)". - if ((testExpression.leftExpression.value === 'isinstance' || + if ( + (testExpression.leftExpression.value === 'isinstance' || testExpression.leftExpression.value === 'issubclass') && - testExpression.arguments.length === 2) { - + testExpression.arguments.length === 2 + ) { // Make sure the first parameter is a supported expression type // and the second parameter is a valid class type or a tuple // of valid class types. @@ -8026,9 +8594,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }; } } - } else if (testExpression.leftExpression.value === 'callable' && - testExpression.arguments.length === 1) { - + } else if ( + testExpression.leftExpression.value === 'callable' && + testExpression.arguments.length === 1 + ) { const arg0Expr = testExpression.arguments[0].valueExpression; if (ParseTreeUtils.isMatchingExpression(reference, arg0Expr)) { return (type: Type) => { @@ -8096,9 +8665,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // type of expression "x" is "Mammal" and the test expression is // "isinstance(x, Cow)", (assuming "Cow" is a subclass of "Mammal"), // we can conclude that x must be constrained to "Cow". - function narrowTypeForIsInstance(type: Type, classTypeList: ClassType[], - isInstanceCheck: boolean, isPositiveTest: boolean): Type { - + function narrowTypeForIsInstance( + type: Type, + classTypeList: ClassType[], + isInstanceCheck: boolean, + isPositiveTest: boolean + ): Type { const effectiveType = doForSubtypes(type, subtype => { return transformTypeObjectToClass(subtype); }); @@ -8106,7 +8678,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Filters the varType by the parameters of the isinstance // and returns the list of types the varType could be after // applying the filter. - const filterType = (varType: ClassType): (ObjectType[] | ClassType[]) => { + const filterType = (varType: ClassType): ObjectType[] | ClassType[] => { const filteredTypes: ClassType[] = []; let foundSuperclass = false; @@ -8204,11 +8776,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // It's a Type object, which is a class. return isPositiveTest ? subtype : undefined; } - + // See if the object is callable. - const callMemberType = getTypeFromObjectMember(errorNode, - subtype, '__call__', { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup); + const callMemberType = getTypeFromObjectMember( + errorNode, + subtype, + '__call__', + { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup + ); if (!callMemberType) { return isPositiveTest ? undefined : subtype; } else { @@ -8223,16 +8800,17 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } }); - } // Specializes the specified (potentially generic) class type using // the specified type arguments, reporting errors as appropriate. // Returns the specialized type and a boolean indicating whether // the type indicates a class type (true) or an object type (false). - function createSpecializedClassType(classType: ClassType, typeArgs: TypeResult[] | undefined, - errorNode: ParseNode): Type { - + function createSpecializedClassType( + classType: ClassType, + typeArgs: TypeResult[] | undefined, + errorNode: ParseNode + ): Type { // Handle the special-case classes that are not defined // in the type stubs. if (ClassType.isSpecialBuiltIn(classType)) { @@ -8301,13 +8879,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (typeArgs && typeArgCount > typeParameters.length) { if (typeParameters.length === 0) { - addError(`Expected no type arguments`, - typeArgs[typeParameters.length].node); + addError(`Expected no type arguments`, typeArgs[typeParameters.length].node); } else { addError( - `Expected at most ${ typeParameters.length } ` + - `type ${ typeParameters.length === 1 ? 'argument' : 'arguments' } `, - typeArgs[typeParameters.length].node); + `Expected at most ${typeParameters.length} ` + + `type ${typeParameters.length === 1 ? 'argument' : 'arguments'} `, + typeArgs[typeParameters.length].node + ); } typeArgCount = typeParameters.length; } @@ -8324,8 +8902,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Fill in any missing type arguments with Any. - const typeArgTypes = typeArgs ? typeArgs.map( - t => convertClassToObject(t.type)) : []; + const typeArgTypes = typeArgs ? typeArgs.map(t => convertClassToObject(t.type)) : []; const typeParams = ClassType.getTypeParameters(classType); for (let i = typeArgTypes.length; i < typeParams.length; i++) { typeArgTypes.push(getConcreteTypeFromTypeVar(typeParams[i])); @@ -8335,10 +8912,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (index < typeArgCount) { const diag = new DiagnosticAddendum(); if (!canAssignToTypeVar(typeParameters[index], typeArgType, diag)) { - addError(`Type '${ printType(typeArgType) }' ` + - `cannot be assigned to type variable '${ typeParameters[index].name }'` + - diag.getString(), - typeArgs![index].node); + addError( + `Type '${printType(typeArgType)}' ` + + `cannot be assigned to type variable '${typeParameters[index].name}'` + + diag.getString(), + typeArgs![index].node + ); } } }); @@ -8355,8 +8934,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If there was no defined type provided, there should always // be a value expression from which we can retrieve the type. - return getTypeOfExpression(arg.valueExpression!, undefined, - expectingType ? EvaluatorFlags.EvaluateStringLiteralAsType : EvaluatorFlags.None).type; + return getTypeOfExpression( + arg.valueExpression!, + undefined, + expectingType ? EvaluatorFlags.EvaluateStringLiteralAsType : EvaluatorFlags.None + ).type; } function getBuiltInType(node: ParseNode, name: string): Type { @@ -8444,15 +9026,16 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If the node is part of a "from X import Y as Z" statement and the node // is the "Y" (non-aliased) name, don't return any declarations for it // because this name isn't in the symbol table. - if (node.parent && node.parent.nodeType === ParseNodeType.ImportFromAs && - node.parent.alias && node === node.parent.name) { - + if ( + node.parent && + node.parent.nodeType === ParseNodeType.ImportFromAs && + node.parent.alias && + node === node.parent.name + ) { return undefined; } - if (node.parent && node.parent.nodeType === ParseNodeType.MemberAccess && - node === node.parent.memberName) { - + if (node.parent && node.parent.nodeType === ParseNodeType.MemberAccess && node === node.parent.memberName) { const baseType = getType(node.parent.leftExpression); if (baseType) { const memberName = node.parent.memberName.value; @@ -8462,8 +9045,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (subtype.category === TypeCategory.Class) { // Try to find a member that has a declared type. If so, that // overrides any inferred types. - let member = lookUpClassMember(subtype, memberName, - importLookup, ClassMemberLookupFlags.DeclaredTypesOnly); + let member = lookUpClassMember( + subtype, + memberName, + importLookup, + ClassMemberLookupFlags.DeclaredTypesOnly + ); if (!member) { member = lookUpClassMember(subtype, memberName, importLookup); } @@ -8473,8 +9060,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } else if (subtype.category === TypeCategory.Object) { // Try to find a member that has a declared type. If so, that // overrides any inferred types. - let member = lookUpObjectMember(subtype, memberName, - importLookup, ClassMemberLookupFlags.DeclaredTypesOnly); + let member = lookUpObjectMember( + subtype, + memberName, + importLookup, + ClassMemberLookupFlags.DeclaredTypesOnly + ); if (!member) { member = lookUpObjectMember(subtype, memberName, importLookup); } @@ -8529,10 +9120,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { declarations.push(paramDecl); } } else if (baseType.category === TypeCategory.Class) { - const initMethodType = getTypeFromObjectMember(argNode.parent.leftExpression, - ObjectType.create(baseType), '__init__', { method: 'get' }, + const initMethodType = getTypeFromObjectMember( + argNode.parent.leftExpression, + ObjectType.create(baseType), + '__init__', + { method: 'get' }, new DiagnosticAddendum(), - MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass); + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass + ); if (initMethodType && initMethodType.category === TypeCategory.Function) { const paramDecl = getDeclarationFromFunctionNamedParameter(initMethodType, paramName); if (paramDecl) { @@ -8650,8 +9245,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return UnknownType.create(); } - function applyLoaderActionsToModuleType(moduleType: ModuleType, - loaderActions: ModuleLoaderActions, importLookup: ImportLookup): Type { + function applyLoaderActionsToModuleType( + moduleType: ModuleType, + loaderActions: ModuleLoaderActions, + importLookup: ImportLookup + ): Type { if (loaderActions.path) { const lookupResults = importLookup(loaderActions.path); if (lookupResults) { @@ -8666,11 +9264,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { loaderActions.implicitImports.forEach((implicitImport, name) => { // Recursively apply loader actions. const importedModuleType = ModuleType.create(); - const symbolType = applyLoaderActionsToModuleType(importedModuleType, - implicitImport, importLookup); + const symbolType = applyLoaderActionsToModuleType(importedModuleType, implicitImport, importLookup); - const importedModuleSymbol = Symbol.createWithType( - SymbolFlags.None, symbolType); + const importedModuleSymbol = Symbol.createWithType(SymbolFlags.None, symbolType); moduleType.loaderFields.set(name, importedModuleSymbol); }); } @@ -8688,12 +9284,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (resolvedDecl.symbolName) { if (resolvedDecl.submoduleFallback) { return applyLoaderActionsToModuleType( - moduleType, resolvedDecl.symbolName && resolvedDecl.submoduleFallback ? - resolvedDecl.submoduleFallback : resolvedDecl, importLookup); + moduleType, + resolvedDecl.symbolName && resolvedDecl.submoduleFallback + ? resolvedDecl.submoduleFallback + : resolvedDecl, + importLookup + ); } } else { - return applyLoaderActionsToModuleType( - moduleType, resolvedDecl, importLookup); + return applyLoaderActionsToModuleType(moduleType, resolvedDecl, importLookup); } } @@ -8757,9 +9356,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - const symbol: Symbol | undefined = lookupResult ? - lookupResult.symbolTable.get(curDeclaration.symbolName) : - undefined; + const symbol: Symbol | undefined = lookupResult + ? lookupResult.symbolTable.get(curDeclaration.symbolName) + : undefined; if (!symbol) { if (curDeclaration.submoduleFallback) { return resolveAliasDeclaration(curDeclaration.submoduleFallback); @@ -8929,9 +9528,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If the type is partially unknown and the function has one or more unannotated // params, try to analyze the function with the provided argument types and // attempt to do a better job at inference. - if (containsUnknown(returnType) && FunctionType.hasUnannotatedParams(type) && - !FunctionType.isStubDefinition(type) && args) { - + if ( + containsUnknown(returnType) && + FunctionType.hasUnannotatedParams(type) && + !FunctionType.isStubDefinition(type) && + args + ) { const contextualReturnType = getFunctionInferredReturnTypeUsingArguments(type, args); if (contextualReturnType) { returnType = contextualReturnType; @@ -8941,9 +9543,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return returnType; } - function getFunctionInferredReturnTypeUsingArguments(type: FunctionType, - args: ValidateArgTypeParams[]): Type | undefined { - + function getFunctionInferredReturnTypeUsingArguments( + type: FunctionType, + args: ValidateArgTypeParams[] + ): Type | undefined { let contextualReturnType: Type | undefined; if (!type.details.declaration) { @@ -9000,9 +9603,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { paramType = getTypeOfExpression(param.defaultValue).type; allArgTypesAreUnknown = false; } else if (index === 0) { - if (FunctionType.isInstanceMethod(functionType.functionType) || - FunctionType.isInstanceMethod(functionType.functionType)) { - + if ( + FunctionType.isInstanceMethod(functionType.functionType) || + FunctionType.isInstanceMethod(functionType.functionType) + ) { if (functionType.functionType.details.parameters.length > 0) { if (functionNode.parameters[0].name) { paramType = functionType.functionType.details.parameters[0].type; @@ -9063,15 +9667,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { function getTypeOfMember(member: ClassMember): Type { if (member.classType.category === TypeCategory.Class) { - return partiallySpecializeType( - getEffectiveTypeOfSymbol(member.symbol), member.classType); + return partiallySpecializeType(getEffectiveTypeOfSymbol(member.symbol), member.classType); } return UnknownType.create(); } - function canAssignClassToProtocol(destType: ClassType, srcType: ClassType, diag: DiagnosticAddendum, - typeVarMap: TypeVarMap | undefined, recursionCount: number): boolean { - + function canAssignClassToProtocol( + destType: ClassType, + srcType: ClassType, + diag: DiagnosticAddendum, + typeVarMap: TypeVarMap | undefined, + recursionCount: number + ): boolean { const destClassFields = destType.details.fields; // Some protocol definitions include recursive references to themselves. @@ -9091,20 +9698,29 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (symbol.isClassMember() && !symbol.isIgnoredForProtocolMatch()) { const memberInfo = lookUpClassMember(srcType, name, importLookup); if (!memberInfo) { - diag.addMessage(`'${ name }' is not present`); + diag.addMessage(`'${name}' is not present`); typesAreConsistent = false; } else { const declaredType = getDeclaredTypeOfSymbol(symbol); if (declaredType) { const srcMemberType = specializeType( - getTypeOfMember(memberInfo), srcClassTypeVarMap, false, - recursionCount + 1); + getTypeOfMember(memberInfo), + srcClassTypeVarMap, + false, + recursionCount + 1 + ); - if (!canAssignType(declaredType, srcMemberType, - diag.createAddendum(), genericDestTypeVarMap, CanAssignFlags.Default, - recursionCount + 1)) { - - diag.addMessage(`'${ name }' is an incompatible type`); + if ( + !canAssignType( + declaredType, + srcMemberType, + diag.createAddendum(), + genericDestTypeVarMap, + CanAssignFlags.Default, + recursionCount + 1 + ) + ) { + diag.addMessage(`'${name}' is an incompatible type`); typesAreConsistent = false; } } @@ -9114,10 +9730,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Now handle base classes of the dest protocol. destType.details.baseClasses.forEach(baseClass => { - if (baseClass.category === TypeCategory.Class && + if ( + baseClass.category === TypeCategory.Class && !ClassType.isBuiltIn(baseClass, 'object') && - !ClassType.isBuiltIn(baseClass, 'Protocol')) { - + !ClassType.isBuiltIn(baseClass, 'Protocol') + ) { const specializedBaseClass = specializeForBaseClass(destType, baseClass, recursionCount + 1); if (!canAssignClassToProtocol(specializedBaseClass, srcType, diag, typeVarMap, recursionCount + 1)) { typesAreConsistent = false; @@ -9129,8 +9746,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (typesAreConsistent && destType.details.typeParameters.length > 0) { // Create a specialized version of the protocol defined by the dest and // make sure the resulting type args can be assigned. - const specializedSrcProtocol = specializeType(genericDestType, genericDestTypeVarMap, - false, recursionCount + 1) as ClassType; + const specializedSrcProtocol = specializeType( + genericDestType, + genericDestTypeVarMap, + false, + recursionCount + 1 + ) as ClassType; if (!verifyTypeArgumentsAssignable(destType, specializedSrcProtocol, diag, typeVarMap, recursionCount)) { typesAreConsistent = false; } @@ -9139,9 +9760,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return typesAreConsistent; } - function canAssignTypedDict(destType: ClassType, srcType: ClassType, diag: DiagnosticAddendum, - recursionCount: number) { - + function canAssignTypedDict( + destType: ClassType, + srcType: ClassType, + diag: DiagnosticAddendum, + recursionCount: number + ) { let typesAreConsistent = true; const destEntries = getTypedDictMembersForClass(destType); const srcEntries = getTypedDictMembersForClass(srcType); @@ -9149,19 +9773,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { destEntries.forEach((destEntry, name) => { const srcEntry = srcEntries.get(name); if (!srcEntry) { - diag.addMessage(`'${ name }' is missing from ${ printType(srcType) }`); + diag.addMessage(`'${name}' is missing from ${printType(srcType)}`); typesAreConsistent = false; } else { if (destEntry.isRequired && !srcEntry.isRequired) { - diag.addMessage(`'${ name }' is required in ${ printType(destType) }`); + diag.addMessage(`'${name}' is required in ${printType(destType)}`); typesAreConsistent = false; } else if (!destEntry.isRequired && srcEntry.isRequired) { - diag.addMessage(`'${ name }' is not required in ${ printType(destType) }`); + diag.addMessage(`'${name}' is not required in ${printType(destType)}`); typesAreConsistent = false; } if (!isTypeSame(destEntry.valueType, srcEntry.valueType, recursionCount + 1)) { - diag.addMessage(`'${ name }' is an incompatible type`); + diag.addMessage(`'${name}' is an incompatible type`); typesAreConsistent = false; } } @@ -9170,10 +9794,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return typesAreConsistent; } - function canAssignClass(destType: ClassType, srcType: ClassType, diag: DiagnosticAddendum, - typeVarMap: TypeVarMap | undefined, flags: CanAssignFlags, recursionCount: number, - reportErrorsUsingObjType: boolean): boolean { - + function canAssignClass( + destType: ClassType, + srcType: ClassType, + diag: DiagnosticAddendum, + typeVarMap: TypeVarMap | undefined, + flags: CanAssignFlags, + recursionCount: number, + reportErrorsUsingObjType: boolean + ): boolean { // Is it a structural type (i.e. a protocol)? If so, we need to // perform a member-by-member check. if (ClassType.isProtocol(destType)) { @@ -9197,10 +9826,12 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (fgetDest && fgetSrc) { const fgetDestType = getDeclaredTypeOfSymbol(fgetDest); const fgetSrcType = getDeclaredTypeOfSymbol(fgetSrc); - if (fgetDestType && fgetSrcType && - fgetDestType.category === TypeCategory.Function && - fgetSrcType.category === TypeCategory.Function) { - + if ( + fgetDestType && + fgetSrcType && + fgetDestType.category === TypeCategory.Function && + fgetSrcType.category === TypeCategory.Function + ) { const fgetDestReturnType = getFunctionEffectiveReturnType(fgetDestType); const fgetSrcReturnType = getFunctionEffectiveReturnType(fgetSrcType); if (!canAssignType(fgetDestReturnType, fgetSrcReturnType, diag)) { @@ -9234,22 +9865,25 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (ClassType.isDerivedFrom(srcType, destType, inheritanceChain)) { assert(inheritanceChain.length > 0); - return canAssignClassWithTypeArgs(destType, srcType, inheritanceChain, - diag, typeVarMap, recursionCount + 1); + return canAssignClassWithTypeArgs( + destType, + srcType, + inheritanceChain, + diag, + typeVarMap, + recursionCount + 1 + ); } } const destErrorType = reportErrorsUsingObjType ? ObjectType.create(destType) : destType; const srcErrorType = reportErrorsUsingObjType ? ObjectType.create(srcType) : srcType; - diag.addMessage(`'${ printType(srcErrorType) }' is incompatible with ` + - `'${ printType(destErrorType) }'`); + diag.addMessage(`'${printType(srcErrorType)}' is incompatible with ` + `'${printType(destErrorType)}'`); return false; } // Determines the specialized base class type that srcType derives from. - function specializeForBaseClass(srcType: ClassType, baseClass: ClassType, - recursionCount: number): ClassType { - + function specializeForBaseClass(srcType: ClassType, baseClass: ClassType, recursionCount: number): ClassType { const typeParams = ClassType.getTypeParameters(baseClass); // If there are no type parameters for the specified base class, @@ -9266,10 +9900,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Determines whether the specified type can be assigned to the // specified inheritance chain, taking into account its type arguments. - function canAssignClassWithTypeArgs(destType: ClassType, srcType: ClassType, - inheritanceChain: InheritanceChain, diag: DiagnosticAddendum, - typeVarMap: TypeVarMap | undefined, recursionCount: number): boolean { - + function canAssignClassWithTypeArgs( + destType: ClassType, + srcType: ClassType, + inheritanceChain: InheritanceChain, + diag: DiagnosticAddendum, + typeVarMap: TypeVarMap | undefined, + recursionCount: number + ): boolean { let curSrcType = srcType; for (let ancestorIndex = inheritanceChain.length - 1; ancestorIndex >= 0; ancestorIndex--) { @@ -9317,18 +9955,24 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const expectedDestType = isDestHomogenousTuple ? destTypeArgs[0] : destTypeArgs[i]; const expectedSrcType = isSrcHomogeneousType ? srcTypeArgs[0] : srcTypeArgs[i]; - if (!canAssignType(expectedDestType, expectedSrcType, - diag.createAddendum(), typeVarMap, CanAssignFlags.Default, - recursionCount + 1)) { - - diag.addMessage(`Tuple entry ${ i + 1 } is incorrect type`); + if ( + !canAssignType( + expectedDestType, + expectedSrcType, + diag.createAddendum(), + typeVarMap, + CanAssignFlags.Default, + recursionCount + 1 + ) + ) { + diag.addMessage(`Tuple entry ${i + 1} is incorrect type`); return false; } } } else { diag.addMessage( - `Tuple size mismatch: expected ${ destArgCount }` + - ` but got ${ srcTypeArgs.length }`); + `Tuple size mismatch: expected ${destArgCount}` + ` but got ${srcTypeArgs.length}` + ); return false; } } @@ -9366,9 +10010,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return true; } - function verifyTypeArgumentsAssignable(destType: ClassType, srcType: ClassType, - diag: DiagnosticAddendum, typeVarMap: TypeVarMap | undefined, recursionCount: number) { - + function verifyTypeArgumentsAssignable( + destType: ClassType, + srcType: ClassType, + diag: DiagnosticAddendum, + typeVarMap: TypeVarMap | undefined, + recursionCount: number + ) { assert(ClassType.isSameGenericClass(destType, srcType)); const destTypeParams = ClassType.getTypeParameters(destType); @@ -9384,28 +10032,48 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // In most cases, the number of type args should match the number // of type arguments, but there are a few special cases where this // isn't true (e.g. assigning a Tuple[X, Y, Z] to a tuple[W]). - const destArgIndex = srcArgIndex >= destTypeArgs.length ? - destTypeArgs.length - 1 : srcArgIndex; + const destArgIndex = srcArgIndex >= destTypeArgs.length ? destTypeArgs.length - 1 : srcArgIndex; const destTypeArg = destTypeArgs[destArgIndex]; - const destTypeParam = destArgIndex < destTypeParams.length ? - destTypeParams[destArgIndex] : undefined; + const destTypeParam = + destArgIndex < destTypeParams.length ? destTypeParams[destArgIndex] : undefined; if (!destTypeParam || destTypeParam.isCovariant) { - if (!canAssignType(destTypeArg, srcTypeArg, diag.createAddendum(), - typeVarMap, CanAssignFlags.Default, recursionCount + 1)) { - + if ( + !canAssignType( + destTypeArg, + srcTypeArg, + diag.createAddendum(), + typeVarMap, + CanAssignFlags.Default, + recursionCount + 1 + ) + ) { return false; } } else if (destTypeParam.isContravariant) { - if (!canAssignType(srcTypeArg, destTypeArg, diag.createAddendum(), - typeVarMap, CanAssignFlags.ReverseTypeVarMatching, recursionCount + 1)) { - + if ( + !canAssignType( + srcTypeArg, + destTypeArg, + diag.createAddendum(), + typeVarMap, + CanAssignFlags.ReverseTypeVarMatching, + recursionCount + 1 + ) + ) { return false; } } else { - if (!canAssignType(destTypeArg, srcTypeArg, diag.createAddendum(), - typeVarMap, CanAssignFlags.EnforceInvariance, recursionCount + 1)) { - + if ( + !canAssignType( + destTypeArg, + srcTypeArg, + diag.createAddendum(), + typeVarMap, + CanAssignFlags.EnforceInvariance, + recursionCount + 1 + ) + ) { return false; } } @@ -9422,10 +10090,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // produce the narrowest type that meets all of the requirements. If the type var map // has been "locked", it simply validates that the srcType is compatible (with no attempt // to widen or narrow). - function assignTypeToTypeVar(destType: TypeVarType, srcType: Type, canNarrowType: boolean, - diag: DiagnosticAddendum, typeVarMap: TypeVarMap, flags = CanAssignFlags.Default, - recursionCount = 0): boolean { - + function assignTypeToTypeVar( + destType: TypeVarType, + srcType: Type, + canNarrowType: boolean, + diag: DiagnosticAddendum, + typeVarMap: TypeVarMap, + flags = CanAssignFlags.Default, + recursionCount = 0 + ): boolean { const curTypeVarMapping = typeVarMap.get(destType.name); // Handle the constrained case. @@ -9436,15 +10109,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); if (!constrainedType) { - diag.addMessage(`Type '${ printType(srcType) }' is not compatible with ` + - `constraints imposed by TypeVar '${ destType.name }'`); + diag.addMessage( + `Type '${printType(srcType)}' is not compatible with ` + + `constraints imposed by TypeVar '${destType.name}'` + ); return false; } if (curTypeVarMapping) { if (!isTypeSame(curTypeVarMapping, constrainedType)) { - diag.addMessage(`Type '${ printType(srcType) }' is not compatible with ` + - `constrained type '${ printType(curTypeVarMapping) }'`); + diag.addMessage( + `Type '${printType(srcType)}' is not compatible with ` + + `constrained type '${printType(curTypeVarMapping)}'` + ); return false; } } else { @@ -9456,7 +10133,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return true; } - + // Handle the unconstrained (but possibly bound) case. let updatedType = srcType; const curTypeIsNarrowable = typeVarMap.isNarrowable(destType.name) && !typeVarMap.isLocked(); @@ -9466,26 +10143,33 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const diagAddendum = new DiagnosticAddendum(); if (canNarrowType) { // Handle the narrowing case (used for contravariant type matching). - if (curTypeIsNarrowable && canAssignType(srcType, curTypeVarMapping, diagAddendum, - typeVarMap, flags, recursionCount + 1)) { - + if ( + curTypeIsNarrowable && + canAssignType(srcType, curTypeVarMapping, diagAddendum, typeVarMap, flags, recursionCount + 1) + ) { // No need to narrow. Stick with the existing type unless it's an Unknown, // in which case we'll try to replace it with a known type. if (!isAnyOrUnknown(curTypeVarMapping) && srcType.category !== TypeCategory.Unknown) { updatedType = curTypeVarMapping; } - } else if (!canAssignType(curTypeVarMapping, srcType, new DiagnosticAddendum(), - typeVarMap, flags, recursionCount + 1)) { - - diag.addMessage(`Type '${ printType(srcType) }' cannot be assigned to ` + - `type '${ printType(curTypeVarMapping) }'`); + } else if ( + !canAssignType( + curTypeVarMapping, + srcType, + new DiagnosticAddendum(), + typeVarMap, + flags, + recursionCount + 1 + ) + ) { + diag.addMessage( + `Type '${printType(srcType)}' cannot be assigned to ` + `type '${printType(curTypeVarMapping)}'` + ); return false; } } else { // Handle the widen case. - if (canAssignType(curTypeVarMapping, srcType, diagAddendum, - typeVarMap, flags, recursionCount + 1)) { - + if (canAssignType(curTypeVarMapping, srcType, diagAddendum, typeVarMap, flags, recursionCount + 1)) { if (curTypeIsNarrowable) { // The new srcType is narrower than the current type, but the current // type is allowed to be narrowed, so replace the current type with @@ -9499,14 +10183,23 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } else { if (typeVarMap.isLocked()) { - diag.addMessage(`Type '${ printType(curTypeVarMapping) }' cannot be assigned to ` + - `type '${ printType(srcType) }'`); + diag.addMessage( + `Type '${printType(curTypeVarMapping)}' cannot be assigned to ` + + `type '${printType(srcType)}'` + ); return false; } - if (!canAssignType(srcType, curTypeVarMapping, new DiagnosticAddendum(), - typeVarMap, flags, recursionCount + 1)) { - + if ( + !canAssignType( + srcType, + curTypeVarMapping, + new DiagnosticAddendum(), + typeVarMap, + flags, + recursionCount + 1 + ) + ) { // Create a union, widening the type. updatedType = combineTypes([curTypeVarMapping, srcType]); } @@ -9516,11 +10209,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If there's a bound type, make sure the source is derived from it. if (destType.boundType) { - if (!canAssignType(destType.boundType, updatedType, diag.createAddendum(), - undefined, flags, recursionCount + 1)) { - - diag.addMessage(`Type '${ printType(updatedType) }' is not compatible with ` + - `bound type '${ printType(destType.boundType) }' for TypeVar '${ destType.name }'`); + if ( + !canAssignType( + destType.boundType, + updatedType, + diag.createAddendum(), + undefined, + flags, + recursionCount + 1 + ) + ) { + diag.addMessage( + `Type '${printType(updatedType)}' is not compatible with ` + + `bound type '${printType(destType.boundType)}' for TypeVar '${destType.name}'` + ); return false; } } @@ -9537,10 +10239,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // matched against existing type variables in the map. If a type variable // in the dest type is not in the type map already, it is assigned a type // and added to the map. - function canAssignType(destType: Type, srcType: Type, diag: DiagnosticAddendum, - typeVarMap?: TypeVarMap, flags = CanAssignFlags.Default, - recursionCount = 0): boolean { - + function canAssignType( + destType: Type, + srcType: Type, + diag: DiagnosticAddendum, + typeVarMap?: TypeVarMap, + flags = CanAssignFlags.Default, + recursionCount = 0 + ): boolean { if (recursionCount > maxTypeRecursionCount) { return true; } @@ -9551,8 +10257,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If the source or dest is unbound, allow the assignment. The // error will be reported elsewhere. - if (destType.category === TypeCategory.Unbound || - srcType.category === TypeCategory.Unbound) { + if (destType.category === TypeCategory.Unbound || srcType.category === TypeCategory.Unbound) { return true; } @@ -9566,9 +10271,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // TypeVar that we are attempting to match. if (destType.category === TypeCategory.TypeVar) { if (typeVarMap) { - if (!assignTypeToTypeVar(destType, srcType, false, diag, - typeVarMap, flags, recursionCount + 1)) { - + if (!assignTypeToTypeVar(destType, srcType, false, diag, typeVarMap, flags, recursionCount + 1)) { return false; } } @@ -9585,8 +10288,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If it's an ellipsis type, convert it to a regular "Any" // type. These are functionally equivalent, but "Any" looks // better in the text representation. - const typeVarSubstitution = isEllipsisType(srcType) ? - AnyType.create() : srcType; + const typeVarSubstitution = isEllipsisType(srcType) ? AnyType.create() : srcType; setTypeArgumentsRecursive(destType, typeVarSubstitution, typeVarMap); } return true; @@ -9598,9 +10300,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // are cases where this can occur (e.g. when we swap the src and dest // types because they are contravariant). if (reverseTypeVarMatching && typeVarMap) { - if (!assignTypeToTypeVar(srcType, destType, true, diag, - typeVarMap, flags, recursionCount + 1)) { - + if (!assignTypeToTypeVar(srcType, destType, true, diag, typeVarMap, flags, recursionCount + 1)) { return false; } @@ -9608,8 +10308,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } const specializedSrcType = getConcreteTypeFromTypeVar(srcType); - return canAssignType(destType, specializedSrcType, diag, - undefined, flags, recursionCount + 1); + return canAssignType(destType, specializedSrcType, diag, undefined, flags, recursionCount + 1); } if (recursionCount > maxTypeRecursionCount) { @@ -9620,8 +10319,9 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (flags & CanAssignFlags.EnforceInvariance) { if (srcType.category === TypeCategory.Union || destType.category === TypeCategory.Union) { if (!isTypeSame(srcType, destType)) { - diag.addMessage(`Type '${ printType(srcType) }' cannot be assigned to ` + - `type '${ printType(destType) }'`); + diag.addMessage( + `Type '${printType(srcType)}' cannot be assigned to ` + `type '${printType(destType)}'` + ); return false; } @@ -9634,11 +10334,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // For union sources, all of the types need to be assignable to the dest. srcType.subtypes.forEach(t => { - if (!canAssignType(destType, t, diag.createAddendum(), typeVarMap, - flags, recursionCount + 1)) { - - diag.addMessage(`Type '${ printType(t) }' cannot be assigned to ` + - `type '${ printType(destType) }'`); + if (!canAssignType(destType, t, diag.createAddendum(), typeVarMap, flags, recursionCount + 1)) { + diag.addMessage(`Type '${printType(t)}' cannot be assigned to ` + `type '${printType(destType)}'`); isIncompatible = true; } }); @@ -9676,25 +10373,27 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } // Is the src a specialized "Type" object? - if (srcType.category === TypeCategory.Object && - ClassType.isBuiltIn(srcType.classType, 'Type')) { - + if (srcType.category === TypeCategory.Object && ClassType.isBuiltIn(srcType.classType, 'Type')) { const srcTypeArgs = srcType.classType.typeArguments; if (srcTypeArgs && srcTypeArgs.length >= 1) { if (isAnyOrUnknown(srcTypeArgs[0])) { return true; } else if (srcTypeArgs[0].category === TypeCategory.Object) { - return canAssignType(destType, - srcTypeArgs[0].classType, diag.createAddendum(), typeVarMap, - flags, recursionCount + 1); + return canAssignType( + destType, + srcTypeArgs[0].classType, + diag.createAddendum(), + typeVarMap, + flags, + recursionCount + 1 + ); } } } if (destType.category === TypeCategory.Class) { if (srcType.category === TypeCategory.Class) { - return canAssignClass(destType, srcType, diag, - typeVarMap, flags, recursionCount + 1, false); + return canAssignClass(destType, srcType, diag, typeVarMap, flags, recursionCount + 1, false); } } @@ -9703,10 +10402,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Is the dest a generic "type" object? if (ClassType.isBuiltIn(destClassType, 'type')) { - if (srcType.category === TypeCategory.Class || + if ( + srcType.category === TypeCategory.Class || srcType.category === TypeCategory.Function || - srcType.category === TypeCategory.OverloadedFunction) { - + srcType.category === TypeCategory.OverloadedFunction + ) { return true; } } @@ -9718,18 +10418,36 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (isAnyOrUnknown(destTypeArgs[0])) { return true; } else if (destTypeArgs[0].category === TypeCategory.Object) { - return canAssignType(destTypeArgs[0].classType, srcType, diag.createAddendum(), - typeVarMap, flags, recursionCount + 1); + return canAssignType( + destTypeArgs[0].classType, + srcType, + diag.createAddendum(), + typeVarMap, + flags, + recursionCount + 1 + ); } else if (destTypeArgs[0].category === TypeCategory.TypeVar) { if (srcType.category === TypeCategory.Class) { - return canAssignType(destTypeArgs[0], ObjectType.create(srcType), - diag.createAddendum(), typeVarMap, flags, recursionCount + 1); - } else if (srcType.category === TypeCategory.Function || - srcType.category === TypeCategory.OverloadedFunction) { - - return canAssignType(destTypeArgs[0], - srcType, diag.createAddendum(), typeVarMap, - flags, recursionCount + 1); + return canAssignType( + destTypeArgs[0], + ObjectType.create(srcType), + diag.createAddendum(), + typeVarMap, + flags, + recursionCount + 1 + ); + } else if ( + srcType.category === TypeCategory.Function || + srcType.category === TypeCategory.OverloadedFunction + ) { + return canAssignType( + destTypeArgs[0], + srcType, + diag.createAddendum(), + typeVarMap, + flags, + recursionCount + 1 + ); } } } @@ -9740,16 +10458,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (destLiteral !== undefined) { const srcLiteral = srcType.literalValue; if (srcLiteral !== destLiteral) { - diag.addMessage(`'${ srcLiteral ? printLiteralType(srcType) : printType(srcType) }' ` + - `cannot be assigned to '${ printLiteralType(destType) }'`); + diag.addMessage( + `'${srcLiteral ? printLiteralType(srcType) : printType(srcType)}' ` + + `cannot be assigned to '${printLiteralType(destType)}'` + ); return false; } } - if (!canAssignClass(destClassType, srcType.classType, diag, - typeVarMap, flags, recursionCount + 1, true)) { - + if ( + !canAssignClass(destClassType, srcType.classType, diag, typeVarMap, flags, recursionCount + 1, true) + ) { return false; } @@ -9758,18 +10478,23 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Is the destination a callback protocol (defined in PEP 544)? const callbackType = getCallbackProtocolType(destType); if (callbackType) { - if (!canAssignFunction(callbackType, srcType, diag.createAddendum(), - typeVarMap, recursionCount + 1, true)) { - + if ( + !canAssignFunction( + callbackType, + srcType, + diag.createAddendum(), + typeVarMap, + recursionCount + 1, + true + ) + ) { return false; } return true; } // All functions are assignable to "object". - if (ClassType.isBuiltIn(destType.classType) && - destType.classType.details.name === 'object') { - + if (ClassType.isBuiltIn(destType.classType) && destType.classType.details.name === 'object') { return true; } } else if (srcType.category === TypeCategory.Module) { @@ -9798,8 +10523,15 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - return canAssignClass(destClassType, metaclass, diag, - typeVarMap, flags, recursionCount + 1, false); + return canAssignClass( + destClassType, + metaclass, + diag, + typeVarMap, + flags, + recursionCount + 1, + false + ); } } } @@ -9814,13 +10546,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // make a copy of the existing one if it's specified. const overloads = srcType.overloads; const overloadIndex = overloads.findIndex(overload => { - const typeVarMapClone = typeVarMap ? - typeVarMap.clone() : undefined; - return canAssignType(destType, overload, diag.createAddendum(), - typeVarMapClone, flags, recursionCount + 1); + const typeVarMapClone = typeVarMap ? typeVarMap.clone() : undefined; + return canAssignType( + destType, + overload, + diag.createAddendum(), + typeVarMapClone, + flags, + recursionCount + 1 + ); }); if (overloadIndex < 0) { - diag.addMessage(`No overloaded function matches type '${ printType(destType) }'.`); + diag.addMessage(`No overloaded function matches type '${printType(destType)}'.`); return false; } srcFunction = overloads[overloadIndex]; @@ -9837,12 +10574,18 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } else if (srcType.category === TypeCategory.Class) { // Synthesize a function that represents the constructor for this class. const constructorFunction = FunctionType.create( - FunctionTypeFlags.StaticMethod | FunctionTypeFlags.ConstructorMethod | - FunctionTypeFlags.SynthesizedMethod); + FunctionTypeFlags.StaticMethod | + FunctionTypeFlags.ConstructorMethod | + FunctionTypeFlags.SynthesizedMethod + ); constructorFunction.details.declaredReturnType = ObjectType.create(srcType); - const newMemberInfo = lookUpClassMember(srcType, '__new__', importLookup, - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass); + const newMemberInfo = lookUpClassMember( + srcType, + '__new__', + importLookup, + ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + ); const memberType = newMemberInfo ? getTypeOfMember(newMemberInfo) : undefined; if (memberType && memberType.category === TypeCategory.Function) { memberType.details.parameters.forEach((param, index) => { @@ -9859,8 +10602,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } if (srcFunction) { - return canAssignFunction(destType, srcFunction, diag.createAddendum(), - typeVarMap, recursionCount + 1, false); + return canAssignFunction( + destType, + srcFunction, + diag.createAddendum(), + typeVarMap, + recursionCount + 1, + false + ); } } @@ -9879,8 +10628,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return false; } - diag.addMessage(`'${ printType(srcType) }' ` + - `cannot be assigned to '${ printType(destType) }'`); + diag.addMessage(`'${printType(srcType)}' ` + `cannot be assigned to '${printType(destType)}'`); return false; } @@ -9902,10 +10650,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return undefined; } - function canAssignFunction(destType: FunctionType, srcType: FunctionType, - diag: DiagnosticAddendum, typeVarMap: TypeVarMap | undefined, recursionCount: number, - checkNamedParams: boolean): boolean { - + function canAssignFunction( + destType: FunctionType, + srcType: FunctionType, + diag: DiagnosticAddendum, + typeVarMap: TypeVarMap | undefined, + recursionCount: number, + checkNamedParams: boolean + ): boolean { let canAssign = true; const srcParamCount = srcType.details.parameters.length; @@ -9919,8 +10671,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const paramDiag = diag.createAddendum(); // If the dest or source involve var-args, no need to continue matching. - if (srcParam.category !== ParameterCategory.Simple || - destParam.category !== ParameterCategory.Simple) { + if (srcParam.category !== ParameterCategory.Simple || destParam.category !== ParameterCategory.Simple) { break; } @@ -9928,20 +10679,34 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const destParamType = FunctionType.getEffectiveParameterType(destType, paramIndex); // Call canAssignType once to perform any typeVarMap population. - canAssignType(srcParamType, destParamType, paramDiag.createAddendum(), - typeVarMap, CanAssignFlags.ReverseTypeVarMatching, recursionCount + 1); + canAssignType( + srcParamType, + destParamType, + paramDiag.createAddendum(), + typeVarMap, + CanAssignFlags.ReverseTypeVarMatching, + recursionCount + 1 + ); // Make sure we can assign the specialized dest type to the // source type. - const specializedDestParamType = specializeType( - destParamType, typeVarMap, false, recursionCount + 1); + const specializedDestParamType = specializeType(destParamType, typeVarMap, false, recursionCount + 1); - if (!canAssignType(srcParamType, specializedDestParamType, paramDiag.createAddendum(), - undefined, CanAssignFlags.Default, recursionCount + 1)) { - - paramDiag.addMessage(`Parameter ${ paramIndex + 1 } of type ` + - `'${ printType(specializedDestParamType) }' cannot be assigned to type ` + - `'${ printType(srcParamType) }'`); + if ( + !canAssignType( + srcParamType, + specializedDestParamType, + paramDiag.createAddendum(), + undefined, + CanAssignFlags.Default, + recursionCount + 1 + ) + ) { + paramDiag.addMessage( + `Parameter ${paramIndex + 1} of type ` + + `'${printType(specializedDestParamType)}' cannot be assigned to type ` + + `'${printType(srcParamType)}'` + ); canAssign = false; } } @@ -9949,10 +10714,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const srcParams = srcType.details.parameters; const destParams = destType.details.parameters; - const srcHasVarArgs = srcParams.find( - param => param.category !== ParameterCategory.Simple) !== undefined; - const destHasVarArgs = destParams.find( - param => param.category !== ParameterCategory.Simple) !== undefined; + const srcHasVarArgs = srcParams.find(param => param.category !== ParameterCategory.Simple) !== undefined; + const destHasVarArgs = destParams.find(param => param.category !== ParameterCategory.Simple) !== undefined; if (checkNamedParams) { // Handle matching of named (keyword) parameters. @@ -9976,17 +10739,30 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const destParam = destParamMap.get(param.name); const paramDiag = diag.createAddendum(); if (!destParam) { - paramDiag.addMessage(`Named parameter '${ param.name }' is missing in destination`); + paramDiag.addMessage(`Named parameter '${param.name}' is missing in destination`); canAssign = false; } else { const specializedDestParamType = specializeType( - destParam.type, typeVarMap, false, recursionCount + 1); - if (!canAssignType(param.type, specializedDestParamType, paramDiag.createAddendum(), - undefined, CanAssignFlags.Default, recursionCount + 1)) { - - paramDiag.addMessage(`Named parameter '${ param.name }' of type ` + - `'${ printType(specializedDestParamType) }' cannot be assigned to type ` + - `'${ printType(param.type) }'`); + destParam.type, + typeVarMap, + false, + recursionCount + 1 + ); + if ( + !canAssignType( + param.type, + specializedDestParamType, + paramDiag.createAddendum(), + undefined, + CanAssignFlags.Default, + recursionCount + 1 + ) + ) { + paramDiag.addMessage( + `Named parameter '${param.name}' of type ` + + `'${printType(specializedDestParamType)}' cannot be assigned to type ` + + `'${printType(param.type)}'` + ); canAssign = false; } destParamMap.delete(param.name); @@ -10000,7 +10776,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // See if there are any unmatched named parameters. destParamMap.forEach((_, paramName) => { const paramDiag = diag.createAddendum(); - paramDiag.addMessage(`Named parameter '${ paramName }' is missing in source`); + paramDiag.addMessage(`Named parameter '${paramName}' is missing in source`); canAssign = false; }); } @@ -10010,18 +10786,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // with values. Plus, the number of source params must be enough to // accept all of the dest arguments. if (!srcHasVarArgs && !destHasVarArgs) { - const nonDefaultSrcParamCount = srcParams.filter( - param => !param.hasDefault).length; + const nonDefaultSrcParamCount = srcParams.filter(param => !param.hasDefault).length; if (destParamCount < nonDefaultSrcParamCount) { - diag.addMessage(`Function accepts too few parameters. Expected ` + - `${ nonDefaultSrcParamCount } but got ${ destParamCount }.`); + diag.addMessage( + `Function accepts too few parameters. Expected ` + + `${nonDefaultSrcParamCount} but got ${destParamCount}.` + ); canAssign = false; } if (destParamCount > srcParamCount) { - diag.addMessage(`Function accepts too many parameters. Expected ` + - `${ srcParamCount } but got ${ destParamCount }.`); + diag.addMessage( + `Function accepts too many parameters. Expected ` + `${srcParamCount} but got ${destParamCount}.` + ); canAssign = false; } } @@ -10031,10 +10809,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (!isAnyOrUnknown(destReturnType)) { const srcReturnType = getFunctionEffectiveReturnType(srcType); - if (!canAssignType(destReturnType, srcReturnType, diag.createAddendum(), - typeVarMap, CanAssignFlags.Default, recursionCount + 1)) { - diag.addMessage(`Function return type '${ printType(srcReturnType) }' ` + - `is not compatible with type '${ printType(destReturnType) }'.`); + if ( + !canAssignType( + destReturnType, + srcReturnType, + diag.createAddendum(), + typeVarMap, + CanAssignFlags.Default, + recursionCount + 1 + ) + ) { + diag.addMessage( + `Function return type '${printType(srcReturnType)}' ` + + `is not compatible with type '${printType(destReturnType)}'.` + ); canAssign = false; } } @@ -10056,7 +10844,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return subtype; } } - + if (!canAssignType(subtype, assignedType, diagAddendum)) { return undefined; } @@ -10081,7 +10869,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If we're overriding a non-method with a method, report it as an error. // This occurs when a non-property overrides a property. if (baseMethod.category !== TypeCategory.Function) { - diag.addMessage(`Base class defines type as ${ printType(baseMethod) }`); + diag.addMessage(`Base class defines type as ${printType(baseMethod)}`); return false; } @@ -10090,8 +10878,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const overrideParams = overrideMethod.details.parameters; if (baseParams.length !== overrideParams.length) { - diag.addMessage(`Parameter count mismatch: base method has ` + - `${ baseParams.length }, but override has ${ overrideParams.length }`); + diag.addMessage( + `Parameter count mismatch: base method has ` + + `${baseParams.length}, but override has ${overrideParams.length}` + ); return false; } @@ -10101,18 +10891,22 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const overrideParam = overrideParams[i]; if (baseParam.name !== overrideParam.name) { - diag.addMessage(`Parameter ${ i + 1 } name mismatch: ` + - `base parameter is named '${ baseParam.name || '*' }, ` + - `override parameter is named '${ overrideParam.name || '*' }'`); + diag.addMessage( + `Parameter ${i + 1} name mismatch: ` + + `base parameter is named '${baseParam.name || '*'}, ` + + `override parameter is named '${overrideParam.name || '*'}'` + ); canOverride = false; } else { const baseParamType = FunctionType.getEffectiveParameterType(baseMethod, i); const overrideParamType = FunctionType.getEffectiveParameterType(overrideMethod, i); if (!canAssignType(baseParamType, overrideParamType, diag.createAddendum())) { - diag.addMessage(`Parameter ${ i + 1 } type mismatch: ` + - `base method parameter is type '${ printType(baseParamType) }, ` + - `override is type '${ printType(overrideParamType) }'`); + diag.addMessage( + `Parameter ${i + 1} type mismatch: ` + + `base method parameter is type '${printType(baseParamType)}, ` + + `override is type '${printType(overrideParamType)}'` + ); canOverride = false; } } @@ -10121,9 +10915,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const baseReturnType = getFunctionEffectiveReturnType(baseMethod); const overrideReturnType = getFunctionEffectiveReturnType(overrideMethod); if (!canAssignType(baseReturnType, overrideReturnType, diag.createAddendum())) { - diag.addMessage(`Return type mismatch: ` + - `base method returns type '${ printType(baseReturnType) }', ` + - `override is type '${ printType(overrideReturnType) }'`); + diag.addMessage( + `Return type mismatch: ` + + `base method returns type '${printType(baseReturnType)}', ` + + `override is type '${printType(overrideReturnType)}'` + ); canOverride = false; } @@ -10133,9 +10929,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Validates that the specified source type matches the constraints // of the type variable. - function canAssignToTypeVar(destType: TypeVarType, srcType: Type, diag: DiagnosticAddendum, - flags = CanAssignFlags.Default, recursionCount = 0): boolean { - + function canAssignToTypeVar( + destType: TypeVarType, + srcType: Type, + diag: DiagnosticAddendum, + flags = CanAssignFlags.Default, + recursionCount = 0 + ): boolean { if (recursionCount > maxTypeRecursionCount) { return true; } @@ -10159,11 +10959,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // If there's a bound type, make sure the source is derived from it. const boundType = destType.boundType; if (boundType) { - if (!canAssignType(boundType, effectiveSrcType, diag.createAddendum(), - undefined, flags, recursionCount + 1)) { - - diag.addMessage(`Type '${ printType(effectiveSrcType) }' is not compatible with ` + - `bound type '${ printType(boundType) }' for TypeVar '${ destType.name }'`); + if ( + !canAssignType(boundType, effectiveSrcType, diag.createAddendum(), undefined, flags, recursionCount + 1) + ) { + diag.addMessage( + `Type '${printType(effectiveSrcType)}' is not compatible with ` + + `bound type '${printType(boundType)}' for TypeVar '${destType.name}'` + ); return false; } } @@ -10180,9 +10982,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return true; } else if (effectiveSrcType.category === TypeCategory.Union) { // Does it match at least one of the constraints? - if (effectiveSrcType.subtypes.find( - t => isSameWithoutLiteralValue(constraint, t))) { - + if (effectiveSrcType.subtypes.find(t => isSameWithoutLiteralValue(constraint, t))) { return true; } } else if (isSameWithoutLiteralValue(constraint, effectiveSrcType)) { @@ -10190,8 +10990,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } } - diag.addMessage(`Type '${ printType(effectiveSrcType) }' is not compatible with ` + - `constraints imposed by TypeVar '${ destType.name }'`); + diag.addMessage( + `Type '${printType(effectiveSrcType)}' is not compatible with ` + + `constraints imposed by TypeVar '${destType.name}'` + ); return false; } @@ -10210,9 +11012,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return abstractMethodCount > 0; } - function getAbstractMethodsRecursive(classType: ClassType, - symbolTable: Map, recursiveCount = 0) { - + function getAbstractMethodsRecursive( + classType: ClassType, + symbolTable: Map, + recursiveCount = 0 + ) { // Protect against infinite recursion. if (recursiveCount > maxTypeRecursionCount) { return; @@ -10235,7 +11039,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const decl = getLastTypedDeclaredForSymbol(symbol); if (symbol.isClassMember() && decl && decl.type === DeclarationType.Function) { const functionFlags = getFunctionFlagsFromDecorators(decl.node, true); - + if (!symbolTable.has(symbolName)) { const isAbstract = !!(functionFlags & FunctionTypeFlags.AbstractMethod); symbolTable.set(symbolName, { @@ -10260,9 +11064,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Determines whether the specified keys and values can be assigned to // a typed dictionary class. The caller should have already validated // that the class is indeed a typed dict. - function canAssignToTypedDict(classType: ClassType, keyTypes: Type[], - valueTypes: Type[]): boolean { - + function canAssignToTypedDict(classType: ClassType, keyTypes: Type[], valueTypes: Type[]): boolean { assert(ClassType.isTypedDictClass(classType)); assert(keyTypes.length === valueTypes.length); @@ -10272,10 +11074,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const diag = new DiagnosticAddendum(); keyTypes.forEach((keyType, index) => { - if (keyType.category !== TypeCategory.Object || + if ( + keyType.category !== TypeCategory.Object || !ClassType.isBuiltIn(keyType.classType, 'str') || - !keyType.literalValue) { - + !keyType.literalValue + ) { isMatch = false; } else { const keyValue = keyType.literalValue as string; @@ -10308,35 +11111,31 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return isMatch; } - function getTypedDictMembersForClass(classType: ClassType) { const entries = new Map(); getTypedDictMembersForClassRecursive(classType, entries); return entries; } - - function getTypedDictMembersForClassRecursive(classType: ClassType, - keyMap: Map, recursionCount = 0) { - + function getTypedDictMembersForClassRecursive( + classType: ClassType, + keyMap: Map, + recursionCount = 0 + ) { assert(ClassType.isTypedDictClass(classType)); if (recursionCount > maxTypeRecursionCount) { return; } classType.details.baseClasses.forEach(baseClassType => { - if (baseClassType.category === TypeCategory.Class && - ClassType.isTypedDictClass(baseClassType)) { - - getTypedDictMembersForClassRecursive(baseClassType, - keyMap, recursionCount + 1); + if (baseClassType.category === TypeCategory.Class && ClassType.isTypedDictClass(baseClassType)) { + getTypedDictMembersForClassRecursive(baseClassType, keyMap, recursionCount + 1); } }); // Add any new typed dict entries from this class. classType.details.fields.forEach((symbol, name) => { if (!symbol.isIgnoredForProtocolMatch()) { - // Only variables (not functions, classes, etc.) are considered. const lastDecl = getLastTypedDeclaredForSymbol(symbol); if (lastDecl && lastDecl.type === DeclarationType.Variable) { @@ -10356,9 +11155,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // class member even if it's not marked as such. That's needed to // special-case the __new__ magic method when it's invoked as a // constructor (as opposed to by name). - function bindFunctionToClassOrObject(baseType: ClassType | ObjectType | undefined, - memberType: Type, treatAsClassMember = false): Type { - + function bindFunctionToClassOrObject( + baseType: ClassType | ObjectType | undefined, + memberType: Type, + treatAsClassMember = false + ): Type { if (memberType.category === TypeCategory.Function) { // If the caller specified no base type, always strip the // first parameter. This is used in cases like constructors. @@ -10366,20 +11167,21 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return stripFirstParameter(memberType); } else if (FunctionType.isInstanceMethod(memberType) && !treatAsClassMember) { if (baseType.category === TypeCategory.Object) { - return partiallySpecializeFunctionForBoundClassOrObject( - baseType, memberType); + return partiallySpecializeFunctionForBoundClassOrObject(baseType, memberType); } } else if (FunctionType.isClassMethod(memberType) || treatAsClassMember) { return partiallySpecializeFunctionForBoundClassOrObject( baseType.category === TypeCategory.Class ? baseType : baseType.classType, - memberType); + memberType + ); } } else if (memberType.category === TypeCategory.OverloadedFunction) { const newOverloadType = OverloadedFunctionType.create(); memberType.overloads.forEach(overload => { - OverloadedFunctionType.addOverload(newOverloadType, - bindFunctionToClassOrObject(baseType, overload, - treatAsClassMember) as FunctionType); + OverloadedFunctionType.addOverload( + newOverloadType, + bindFunctionToClassOrObject(baseType, overload, treatAsClassMember) as FunctionType + ); }); return newOverloadType; @@ -10389,15 +11191,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } function partiallySpecializeFunctionForBoundClassOrObject( - baseType: ClassType | ObjectType, memberType: FunctionType): Type { - + baseType: ClassType | ObjectType, + memberType: FunctionType + ): Type { const classType = baseType.category === TypeCategory.Class ? baseType : baseType.classType; // If the class has already been specialized (fully or partially), use its // existing type arg mappings. If it hasn't, use a fresh type arg map. - const typeVarMap = classType.typeArguments ? - buildTypeVarMapFromSpecializedClass(classType) : - new TypeVarMap(); + const typeVarMap = classType.typeArguments ? buildTypeVarMapFromSpecializedClass(classType) : new TypeVarMap(); if (memberType.details.parameters.length > 0) { const firstParam = memberType.details.parameters[0]; @@ -10406,8 +11207,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { canAssignType(firstParam.type, baseType, new DiagnosticAddendum(), typeVarMap); } - const specializedFunction = specializeType( - memberType, typeVarMap, false) as FunctionType; + const specializedFunction = specializeType(memberType, typeVarMap, false) as FunctionType; return stripFirstParameter(specializedFunction); } @@ -10418,9 +11218,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (type.typeArguments) { // Handle Tuple[()] as a special case. if (type.typeArguments.length > 0) { - objName += '[' + type.typeArguments.map(typeArg => { - return printType(typeArg, recursionCount + 1); - }).join(', ') + ']'; + objName += + '[' + + type.typeArguments + .map(typeArg => { + return printType(typeArg, recursionCount + 1); + }) + .join(', ') + + ']'; } else { if (ClassType.isBuiltIn(type, 'Tuple')) { objName += '[()]'; @@ -10430,9 +11235,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const typeParams = ClassType.getTypeParameters(type); if (typeParams.length > 0) { - objName += '[' + typeParams.map(typeArg => { - return printType(typeArg, recursionCount + 1); - }).join(', ') + ']'; + objName += + '[' + + typeParams + .map(typeArg => { + return printType(typeArg, recursionCount + 1); + }) + .join(', ') + + ']'; } } @@ -10455,11 +11265,11 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (param.category === ParameterCategory.Simple) { if (param.name) { const paramType = FunctionType.getEffectiveParameterType(type, index); - const paramTypeString = recursionCount < maxTypeRecursionCount ? - printType(paramType, recursionCount + 1) : ''; + const paramTypeString = + recursionCount < maxTypeRecursionCount ? printType(paramType, recursionCount + 1) : ''; paramString += ': ' + paramTypeString; } else { - paramString += '/' + paramString += '/'; } } @@ -10475,8 +11285,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { }); const returnType = getFunctionEffectiveReturnType(type); - const returnTypeString = recursionCount < maxTypeRecursionCount ? - printType(returnType, recursionCount + 1) : ''; + const returnTypeString = + recursionCount < maxTypeRecursionCount ? printType(returnType, recursionCount + 1) : ''; return [paramTypeStrings, returnTypeString]; } @@ -10499,8 +11309,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { } case TypeCategory.Class: { - return 'Type[' + printObjectTypeForClass(type, - recursionCount + 1) + ']'; + return 'Type[' + printObjectTypeForClass(type, recursionCount + 1) + ']'; } case TypeCategory.Object: { @@ -10525,14 +11334,13 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { case TypeCategory.Function: { const parts = printFunctionParts(type, recursionCount); - return `(${ parts[0].join(', ') }) -> ${ parts[1] }`; + return `(${parts[0].join(', ')}) -> ${parts[1]}`; } case TypeCategory.OverloadedFunction: { const overloadedType = type; - const overloads = overloadedType.overloads.map(overload => - printType(overload, recursionCount + 1)); - return `Overload[${ overloads.join(', ') }]`; + const overloads = overloadedType.overloads.map(overload => printType(overload, recursionCount + 1)); + return `Overload[${overloads.join(', ')}]`; } case TypeCategory.Union: { @@ -10548,8 +11356,8 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { subtypes = []; subtypes = subtypes.concat(...unionType.subtypes); - const isLiteral = (type: Type) => type.category === TypeCategory.Object && - type.literalValue !== undefined; + const isLiteral = (type: Type) => + type.category === TypeCategory.Object && type.literalValue !== undefined; const subtypeStrings: string[] = []; while (subtypes.length > 0) { @@ -10560,7 +11368,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const literals = subtypes.filter(t => isLiteral(t)); literals.unshift(subtype); const literalValues = literals.map(t => printLiteralValue(t as ObjectType)); - subtypeStrings.push(`Literal[${ literalValues.join(', ') }]`); + subtypeStrings.push(`Literal[${literalValues.join(', ')}]`); // Remove the items we've handled. if (literals.length > 1) { @@ -10575,7 +11383,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return subtypeStrings[0]; } - return `Union[${ subtypeStrings.join(', ') }]`; + return `Union[${subtypeStrings.join(', ')}]`; } case TypeCategory.TypeVar: { @@ -10587,7 +11395,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { if (recursionCount > 0) { return typeName; } - const params: string[] = [`'${ typeName }'`]; + const params: string[] = [`'${typeName}'`]; for (const constraint of typeVarType.constraints) { params.push(printType(constraint, recursionCount + 1)); } @@ -10625,16 +11433,20 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { // Determine the offset within the file where the string // literal's contents begin. - const valueOffset = node.strings[0].start + - node.strings[0].token.prefixLength + - node.strings[0].token.quoteMarkLength; + const valueOffset = + node.strings[0].start + node.strings[0].token.prefixLength + node.strings[0].token.quoteMarkLength; const parseOptions = new ParseOptions(); parseOptions.isStubFile = fileInfo.isStubFile; parseOptions.pythonVersion = fileInfo.executionEnvironment.pythonVersion; - const parseResults = parser.parseTextExpression(fileInfo.fileContents, - valueOffset, textValue.length, parseOptions, true); + const parseResults = parser.parseTextExpression( + fileInfo.fileContents, + valueOffset, + textValue.length, + parseOptions, + true + ); if (parseResults.parseTree) { parseResults.diagnostics.forEach(diag => { diff --git a/server/src/analyzer/typeStubWriter.ts b/server/src/analyzer/typeStubWriter.ts index 3f8a7edde..03091485e 100644 --- a/server/src/analyzer/typeStubWriter.ts +++ b/server/src/analyzer/typeStubWriter.ts @@ -1,19 +1,38 @@ /* -* typeStubWriter.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic to emit a type stub file for a corresponding parsed -* and analyzed python source file. -*/ + * typeStubWriter.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic to emit a type stub file for a corresponding parsed + * and analyzed python source file. + */ import { - ArgumentCategory, ArgumentNode, AssignmentNode, AugmentedAssignmentNode, - ClassNode, DecoratorNode, ExpressionNode, ForNode, FunctionNode, IfNode, - ImportFromNode, ImportNode, ModuleNameNode, NameNode, ParameterCategory, ParameterNode, - ParseNode, ParseNodeType, StatementListNode, StringNode, TryNode, - TypeAnnotationNode, WhileNode, WithNode + ArgumentCategory, + ArgumentNode, + AssignmentNode, + AugmentedAssignmentNode, + ClassNode, + DecoratorNode, + ExpressionNode, + ForNode, + FunctionNode, + IfNode, + ImportFromNode, + ImportNode, + ModuleNameNode, + NameNode, + ParameterCategory, + ParameterNode, + ParseNode, + ParseNodeType, + StatementListNode, + StringNode, + TryNode, + TypeAnnotationNode, + WhileNode, + WithNode } from '../parser/parseNodes'; import * as AnalyzerNodeInfo from './analyzerNodeInfo'; import * as ParseTreeUtils from './parseTreeUtils'; @@ -27,15 +46,13 @@ import { ClassType, isNoneOrNever, TypeCategory } from './types'; import * as TypeUtils from './typeUtils'; class TrackedImport { - constructor(public importName: string) { } + constructor(public importName: string) {} isAccessed = false; } class TrackedImportAs extends TrackedImport { - constructor(importName: string, public alias: string | undefined, - public symbol: Symbol) { - + constructor(importName: string, public alias: string | undefined, public symbol: Symbol) { super(importName); } } @@ -54,9 +71,7 @@ class TrackedImportFrom extends TrackedImport { super(importName); } - addSymbol(symbol: Symbol | undefined, name: string, - alias: string | undefined, isAccessed = false) { - + addSymbol(symbol: Symbol | undefined, name: string, alias: string | undefined, isAccessed = false) { if (!this.symbols.find(s => s.name === name)) { this.symbols.push({ symbol, @@ -69,10 +84,7 @@ class TrackedImportFrom extends TrackedImport { } class ImportSymbolWalker extends ParseTreeWalker { - constructor( - private _accessedImportedSymbols: Map, - private _treatStringsAsSymbols: boolean) { - + constructor(private _accessedImportedSymbols: Map, private _treatStringsAsSymbols: boolean) { super(); } @@ -115,8 +127,7 @@ export class TypeStubWriter extends ParseTreeWalker { private _trackedImportFrom = new Map(); private _accessedImportedSymbols = new Map(); - constructor(private _typingsPath: string, private _sourceFile: SourceFile, - private _evaluator: TypeEvaluator) { + constructor(private _typingsPath: string, private _sourceFile: SourceFile, private _evaluator: TypeEvaluator) { super(); // As a heuristic, we'll include all of the import statements @@ -149,16 +160,18 @@ export class TypeStubWriter extends ParseTreeWalker { this._emittedSuite = true; this._emitDocString = true; this._emitDecorators(node.decorators); - let line = `class ${ className }`; + let line = `class ${className}`; if (node.arguments.length > 0) { - line += `(${ node.arguments.map(arg => { - let argString = ''; - if (arg.name) { - argString = arg.name.value + '='; - } - argString += this._printExpression(arg.valueExpression); - return argString; - }).join(', ') })`; + line += `(${node.arguments + .map(arg => { + let argString = ''; + if (arg.name) { + argString = arg.name.value + '='; + } + argString += this._printExpression(arg.valueExpression); + return argString; + }) + .join(', ')})`; } line += ':'; this._emitLine(line); @@ -184,8 +197,8 @@ export class TypeStubWriter extends ParseTreeWalker { this._emitDocString = true; this._emitDecorators(node.decorators); let line = node.isAsync ? 'async ' : ''; - line += `def ${ functionName }`; - line += `(${ node.parameters.map(param => this._printParameter(param)).join(', ') })`; + line += `def ${functionName}`; + line += `(${node.parameters.map(param => this._printParameter(param)).join(', ')})`; if (node.returnTypeAnnotation) { line += ' -> ' + this._printExpression(node.returnTypeAnnotation, true); @@ -357,14 +370,18 @@ export class TypeStubWriter extends ParseTreeWalker { node.list.forEach(imp => { const moduleName = this._printModuleName(imp.module); if (!this._trackedImportAs.has(moduleName)) { - const symbolName = imp.alias ? imp.alias.value : - (imp.module.nameParts.length > 0 ? - imp.module.nameParts[0].value : ''); + const symbolName = imp.alias + ? imp.alias.value + : imp.module.nameParts.length > 0 + ? imp.module.nameParts[0].value + : ''; const symbolInfo = currentScope.lookUpSymbolRecursive(symbolName); if (symbolInfo) { - const trackedImportAs = new TrackedImportAs(moduleName, + const trackedImportAs = new TrackedImportAs( + moduleName, imp.alias ? imp.alias.value : undefined, - symbolInfo.symbol); + symbolInfo.symbol + ); this._trackedImportAs.set(moduleName, trackedImportAs); } } @@ -385,18 +402,20 @@ export class TypeStubWriter extends ParseTreeWalker { const moduleName = this._printModuleName(node.module); let trackedImportFrom = this._trackedImportFrom.get(moduleName); if (!trackedImportFrom) { - trackedImportFrom = new TrackedImportFrom(moduleName, - node.isWildcardImport, node); + trackedImportFrom = new TrackedImportFrom(moduleName, node.isWildcardImport, node); this._trackedImportFrom.set(moduleName, trackedImportFrom); } node.imports.forEach(imp => { - const symbolName = imp.alias ? - imp.alias.value : imp.name.value; + const symbolName = imp.alias ? imp.alias.value : imp.name.value; const symbolInfo = currentScope.lookUpSymbolRecursive(symbolName); if (symbolInfo) { - trackedImportFrom!.addSymbol(symbolInfo.symbol, imp.name.value, - imp.alias ? imp.alias.value : undefined, false); + trackedImportFrom!.addSymbol( + symbolInfo.symbol, + imp.name.value, + imp.alias ? imp.alias.value : undefined, + false + ); } }); } @@ -445,18 +464,22 @@ export class TypeStubWriter extends ParseTreeWalker { decorators.forEach(decorator => { let line = '@' + this._printExpression(decorator.leftExpression); if (decorator.arguments) { - line += `(${ decorator.arguments.map( - arg => this._printArgument(arg)).join(', ') })`; + line += `(${decorator.arguments.map(arg => this._printArgument(arg)).join(', ')})`; } this._emitLine(line); }); } private _printHeaderDocString() { - return '"""' + this._lineEnd + - 'This type stub file was generated by pyright.' + this._lineEnd + - '"""' + this._lineEnd + - this._lineEnd; + return ( + '"""' + + this._lineEnd + + 'This type stub file was generated by pyright.' + + this._lineEnd + + '"""' + + this._lineEnd + + this._lineEnd + ); // this._emitLine(''); // this._emitLine('from typing import Any, Optional'); // this._emitLine('from typing import Any, List, Dict, Optional, Tuple, Type, Union'); @@ -555,17 +578,14 @@ export class TypeStubWriter extends ParseTreeWalker { return line + this._printExpression(node.valueExpression); } - private _printExpression(node: ExpressionNode, isType = false, - treatStringsAsSymbols = false): string { - - const importSymbolWalker = new ImportSymbolWalker( - this._accessedImportedSymbols, - treatStringsAsSymbols); + private _printExpression(node: ExpressionNode, isType = false, treatStringsAsSymbols = false): string { + const importSymbolWalker = new ImportSymbolWalker(this._accessedImportedSymbols, treatStringsAsSymbols); importSymbolWalker.analyze(node); - return ParseTreeUtils.printExpression(node, - isType ? ParseTreeUtils.PrintExpressionFlags.ForwardDeclarations : - ParseTreeUtils.PrintExpressionFlags.None); + return ParseTreeUtils.printExpression( + node, + isType ? ParseTreeUtils.PrintExpressionFlags.ForwardDeclarations : ParseTreeUtils.PrintExpressionFlags.None + ); } private _printTrackedImports() { @@ -579,9 +599,9 @@ export class TypeStubWriter extends ParseTreeWalker { } if (imp.isAccessed || this._includeAllImports) { - importStr += `import ${ imp.importName }`; + importStr += `import ${imp.importName}`; if (imp.alias) { - importStr += ` as ${ imp.alias }`; + importStr += ` as ${imp.alias}`; } importStr += this._lineEnd; lineEmitted = true; @@ -597,13 +617,13 @@ export class TypeStubWriter extends ParseTreeWalker { }); if (imp.isWildcardImport) { - importStr += `from ${ imp.importName } import *` + this._lineEnd; + importStr += `from ${imp.importName} import *` + this._lineEnd; lineEmitted = true; } - const sortedSymbols = imp.symbols. - filter(s => s.isAccessed || this._includeAllImports). - sort((a, b) => { + const sortedSymbols = imp.symbols + .filter(s => s.isAccessed || this._includeAllImports) + .sort((a, b) => { if (a.name < b.name) { return -1; } else if (a.name > b.name) { @@ -613,15 +633,17 @@ export class TypeStubWriter extends ParseTreeWalker { }); if (sortedSymbols.length > 0) { - importStr += `from ${ imp.importName } import `; + importStr += `from ${imp.importName} import `; - importStr += sortedSymbols.map(symbol => { - let symStr = symbol.name; - if (symbol.alias) { - symStr += ' as ' + symbol.alias; - } - return symStr; - }).join(', '); + importStr += sortedSymbols + .map(symbol => { + let symStr = symbol.name; + if (symbol.alias) { + symStr += ' as ' + symbol.alias; + } + return symStr; + }) + .join(', '); importStr += this._lineEnd; lineEmitted = true; diff --git a/server/src/analyzer/typeUtils.ts b/server/src/analyzer/typeUtils.ts index a8abcd53c..56f45b181 100644 --- a/server/src/analyzer/typeUtils.ts +++ b/server/src/analyzer/typeUtils.ts @@ -1,21 +1,36 @@ /* -* typeUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Collection of functions that operate on Type objects. -*/ + * typeUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Collection of functions that operate on Type objects. + */ import { ParameterCategory } from '../parser/parseNodes'; import { ImportLookup } from './analyzerFileInfo'; import { DeclarationType } from './declaration'; import { Symbol, SymbolFlags, SymbolTable } from './symbol'; import { isTypedDictMemberAccessedThroughIndex } from './symbolUtils'; -import { AnyType, ClassType, combineTypes, FunctionType, isAnyOrUnknown, isNoneOrNever, - isTypeSame, maxTypeRecursionCount, ModuleType, NeverType, ObjectType, - OverloadedFunctionType, SpecializedFunctionTypes, Type, TypeCategory, - TypeVarType, UnknownType } from './types'; +import { + AnyType, + ClassType, + combineTypes, + FunctionType, + isAnyOrUnknown, + isNoneOrNever, + isTypeSame, + maxTypeRecursionCount, + ModuleType, + NeverType, + ObjectType, + OverloadedFunctionType, + SpecializedFunctionTypes, + Type, + TypeCategory, + TypeVarType, + UnknownType +} from './types'; import { TypeVarMap } from './typeVarMap'; export interface ClassMember { @@ -89,7 +104,7 @@ export function isOptionalType(type: Type): boolean { // Calls a callback for each subtype and combines the results // into a final type. -export function doForSubtypes(type: Type, callback: (type: Type) => (Type | undefined)): Type { +export function doForSubtypes(type: Type, callback: (type: Type) => Type | undefined): Type { if (type.category === TypeCategory.Union) { const newTypes: Type[] = []; @@ -146,17 +161,16 @@ export function stripLiteralTypeArgsValue(type: Type, recursionCount = 0): Type if (type.category === TypeCategory.Class) { if (type.typeArguments) { - const strippedTypeArgs = type.typeArguments.map( - t => stripLiteralTypeArgsValue(stripLiteralValue(t), recursionCount + 1)); - return ClassType.cloneForSpecialization(type, strippedTypeArgs, - type.skipAbstractClassTest); + const strippedTypeArgs = type.typeArguments.map(t => + stripLiteralTypeArgsValue(stripLiteralValue(t), recursionCount + 1) + ); + return ClassType.cloneForSpecialization(type, strippedTypeArgs, type.skipAbstractClassTest); } } if (type.category === TypeCategory.Object) { if (type.classType.typeArguments) { - type = ObjectType.create( - stripLiteralTypeArgsValue(type.classType, recursionCount + 1) as ClassType); + type = ObjectType.create(stripLiteralTypeArgsValue(type.classType, recursionCount + 1) as ClassType); } return type; @@ -171,12 +185,12 @@ export function stripLiteralTypeArgsValue(type: Type, recursionCount = 0): Type if (type.category === TypeCategory.Function) { if (type.specializedTypes) { const strippedSpecializedTypes: SpecializedFunctionTypes = { - parameterTypes: type.specializedTypes.parameterTypes.map( - t => stripLiteralTypeArgsValue(stripLiteralValue(t), recursionCount + 1)), - returnType: type.specializedTypes.returnType ? - stripLiteralTypeArgsValue(stripLiteralValue(type.specializedTypes.returnType), - recursionCount + 1) : - undefined + parameterTypes: type.specializedTypes.parameterTypes.map(t => + stripLiteralTypeArgsValue(stripLiteralValue(t), recursionCount + 1) + ), + returnType: type.specializedTypes.returnType + ? stripLiteralTypeArgsValue(stripLiteralValue(type.specializedTypes.returnType), recursionCount + 1) + : undefined }; type = FunctionType.cloneForSpecialization(type, strippedSpecializedTypes); } @@ -187,7 +201,8 @@ export function stripLiteralTypeArgsValue(type: Type, recursionCount = 0): Type if (type.category === TypeCategory.OverloadedFunction) { const strippedOverload = OverloadedFunctionType.create(); strippedOverload.overloads = type.overloads.map( - t => stripLiteralTypeArgsValue(t, recursionCount + 1) as FunctionType); + t => stripLiteralTypeArgsValue(t, recursionCount + 1) as FunctionType + ); return strippedOverload; } @@ -297,8 +312,7 @@ export function isEllipsisType(type: Type): boolean { return true; } - return (type.category === TypeCategory.Class && - ClassType.isBuiltIn(type, 'ellipsis')); + return type.category === TypeCategory.Class && ClassType.isBuiltIn(type, 'ellipsis'); } export function isNoReturnType(type: Type): boolean { @@ -312,8 +326,7 @@ export function isNoReturnType(type: Type): boolean { } export function isProperty(type: Type): boolean { - return type.category === TypeCategory.Object && - ClassType.isPropertyClass(type.classType); + return type.category === TypeCategory.Object && ClassType.isPropertyClass(type.classType); } // Partially specializes a type within the context of a specified @@ -335,9 +348,12 @@ export function partiallySpecializeType(type: Type, contextClassType: ClassType) // provided or makeConcrete is true, type variables are replaced // with a concrete type derived from the type variable if there // is no corresponding definition in the typeVarMap. -export function specializeType(type: Type, typeVarMap: TypeVarMap | undefined, - makeConcrete = false, recursionLevel = 0): Type { - +export function specializeType( + type: Type, + typeVarMap: TypeVarMap | undefined, + makeConcrete = false, + recursionLevel = 0 +): Type { // Prevent infinite recursion in case a type refers to itself. if (recursionLevel > 100) { return AnyType.create(); @@ -379,16 +395,14 @@ export function specializeType(type: Type, typeVarMap: TypeVarMap | undefined, if (type.category === TypeCategory.Union) { const subtypes: Type[] = []; type.subtypes.forEach(typeEntry => { - subtypes.push(specializeType(typeEntry, typeVarMap, - makeConcrete, recursionLevel + 1)); + subtypes.push(specializeType(typeEntry, typeVarMap, makeConcrete, recursionLevel + 1)); }); return combineTypes(subtypes); } if (type.category === TypeCategory.Object) { - const classType = _specializeClassType(type.classType, - typeVarMap, makeConcrete, recursionLevel + 1); + const classType = _specializeClassType(type.classType, typeVarMap, makeConcrete, recursionLevel + 1); // Handle the "Type" special class. if (ClassType.isBuiltIn(classType, 'Type')) { @@ -396,8 +410,7 @@ export function specializeType(type: Type, typeVarMap: TypeVarMap | undefined, if (typeArgs && typeArgs.length >= 1) { const firstTypeArg = typeArgs[0]; if (firstTypeArg.category === TypeCategory.Object) { - return specializeType(firstTypeArg.classType, typeVarMap, - makeConcrete, recursionLevel + 1); + return specializeType(firstTypeArg.classType, typeVarMap, makeConcrete, recursionLevel + 1); } else if (firstTypeArg.category === TypeCategory.TypeVar) { if (typeVarMap) { const replacementType = typeVarMap.get(firstTypeArg.name); @@ -418,26 +431,26 @@ export function specializeType(type: Type, typeVarMap: TypeVarMap | undefined, } if (type.category === TypeCategory.Class) { - return _specializeClassType(type, typeVarMap, - makeConcrete, recursionLevel + 1); + return _specializeClassType(type, typeVarMap, makeConcrete, recursionLevel + 1); } if (type.category === TypeCategory.Function) { - return _specializeFunctionType(type, typeVarMap, - makeConcrete, recursionLevel + 1); + return _specializeFunctionType(type, typeVarMap, makeConcrete, recursionLevel + 1); } if (type.category === TypeCategory.OverloadedFunction) { - return _specializeOverloadedFunctionType(type, typeVarMap, - makeConcrete, recursionLevel + 1); + return _specializeOverloadedFunctionType(type, typeVarMap, makeConcrete, recursionLevel + 1); } return type; } -export function lookUpObjectMember(objectType: Type, memberName: string, importLookup: ImportLookup, - flags = ClassMemberLookupFlags.Default): ClassMember | undefined { - +export function lookUpObjectMember( + objectType: Type, + memberName: string, + importLookup: ImportLookup, + flags = ClassMemberLookupFlags.Default +): ClassMember | undefined { if (objectType.category === TypeCategory.Object) { return lookUpClassMember(objectType.classType, memberName, importLookup, flags); } @@ -453,9 +466,12 @@ export function lookUpObjectMember(objectType: Type, memberName: string, importL // ClassB[str] which inherits from Dict[_T1, int], a search for '__iter__' // would return a class type of Dict[str, int] and a symbolType of // (self) -> Iterator[str]. -export function lookUpClassMember(classType: Type, memberName: string, importLookup: ImportLookup, - flags = ClassMemberLookupFlags.Default): ClassMember | undefined { - +export function lookUpClassMember( + classType: Type, + memberName: string, + importLookup: ImportLookup, + flags = ClassMemberLookupFlags.Default +): ClassMember | undefined { const declaredTypesOnly = (flags & ClassMemberLookupFlags.DeclaredTypesOnly) !== 0; if (classType.category === TypeCategory.Class) { @@ -515,7 +531,10 @@ export function lookUpClassMember(classType: Type, memberName: string, importLoo // Recursively perform search. const methodType = lookUpClassMember( partiallySpecializeType(baseClass, classType), - memberName, importLookup, flags & ~ClassMemberLookupFlags.SkipOriginalClass); + memberName, + importLookup, + flags & ~ClassMemberLookupFlags.SkipOriginalClass + ); if (methodType) { return methodType; } @@ -592,8 +611,7 @@ export function getTypeVarArgumentsRecursive(type: Type): TypeVarType[] { const combinedList: TypeVarType[] = []; if (classType.typeArguments) { classType.typeArguments.forEach(typeArg => { - addTypeVarsToListIfUnique(combinedList, - getTypeVarArgumentsRecursive(typeArg)); + addTypeVarsToListIfUnique(combinedList, getTypeVarArgumentsRecursive(typeArg)); }); } @@ -609,21 +627,18 @@ export function getTypeVarArgumentsRecursive(type: Type): TypeVarType[] { } else if (type.category === TypeCategory.Union) { const combinedList: TypeVarType[] = []; for (const subtype of type.subtypes) { - addTypeVarsToListIfUnique(combinedList, - getTypeVarArgumentsRecursive(subtype)); + addTypeVarsToListIfUnique(combinedList, getTypeVarArgumentsRecursive(subtype)); } return combinedList; } else if (type.category === TypeCategory.Function) { const combinedList: TypeVarType[] = []; type.details.parameters.forEach(param => { - addTypeVarsToListIfUnique(combinedList, - getTypeVarArgumentsRecursive(param.type)); + addTypeVarsToListIfUnique(combinedList, getTypeVarArgumentsRecursive(param.type)); }); if (type.details.declaredReturnType) { - addTypeVarsToListIfUnique(combinedList, - getTypeVarArgumentsRecursive(type.details.declaredReturnType)); + addTypeVarsToListIfUnique(combinedList, getTypeVarArgumentsRecursive(type.details.declaredReturnType)); } return combinedList; @@ -654,9 +669,7 @@ export function stripFirstParameter(type: FunctionType): FunctionType { // Recursively finds all of the type arguments and sets them // to the specified srcType. -export function setTypeArgumentsRecursive(destType: Type, srcType: Type, - typeVarMap: TypeVarMap, recursionCount = 0) { - +export function setTypeArgumentsRecursive(destType: Type, srcType: Type, typeVarMap: TypeVarMap, recursionCount = 0) { if (typeVarMap.isLocked()) { return; } @@ -686,16 +699,24 @@ export function setTypeArgumentsRecursive(destType: Type, srcType: Type, setTypeArgumentsRecursive(paramType, srcType, typeVarMap, recursionCount + 1); }); if (destType.specializedTypes.returnType) { - setTypeArgumentsRecursive(destType.specializedTypes.returnType, srcType, - typeVarMap, recursionCount + 1); + setTypeArgumentsRecursive( + destType.specializedTypes.returnType, + srcType, + typeVarMap, + recursionCount + 1 + ); } } else { destType.details.parameters.forEach(param => { setTypeArgumentsRecursive(param.type, srcType, typeVarMap, recursionCount + 1); }); if (destType.details.declaredReturnType) { - setTypeArgumentsRecursive(destType.details.declaredReturnType, srcType, - typeVarMap, recursionCount + 1); + setTypeArgumentsRecursive( + destType.details.declaredReturnType, + srcType, + typeVarMap, + recursionCount + 1 + ); } } break; @@ -836,9 +857,11 @@ export function removeTruthinessFromType(type: Type, importLookup: ImportLookup) // Looks up the specified symbol name within the base classes // of a specified class. -export function getSymbolFromBaseClasses(classType: ClassType, name: string, - recursionCount = 0): SymbolWithClass | undefined { - +export function getSymbolFromBaseClasses( + classType: ClassType, + name: string, + recursionCount = 0 +): SymbolWithClass | undefined { if (recursionCount > maxTypeRecursionCount) { return undefined; } @@ -867,19 +890,14 @@ export function getSymbolFromBaseClasses(classType: ClassType, name: string, } // Returns the declared yield type if provided, or undefined otherwise. -export function getDeclaredGeneratorYieldType(functionType: FunctionType, - iteratorType: Type): Type | undefined { - +export function getDeclaredGeneratorYieldType(functionType: FunctionType, iteratorType: Type): Type | undefined { const returnType = FunctionType.getSpecializedReturnType(functionType); if (returnType) { const generatorTypeArgs = _getGeneratorReturnTypeArgs(returnType); - if (generatorTypeArgs && generatorTypeArgs.length >= 1 && - iteratorType.category === TypeCategory.Class) { - + if (generatorTypeArgs && generatorTypeArgs.length >= 1 && iteratorType.category === TypeCategory.Class) { // The yield type is the first type arg. Wrap it in an iterator. - return ObjectType.create(ClassType.cloneForSpecialization( - iteratorType, [generatorTypeArgs[0]])); + return ObjectType.create(ClassType.cloneForSpecialization(iteratorType, [generatorTypeArgs[0]])); } // If the return type isn't a Generator, assume that it's the @@ -936,9 +954,7 @@ export function convertClassToObject(type: Type): Type { }); } -export function getMembersForClass(classType: ClassType, symbolTable: SymbolTable, - includeInstanceVars: boolean) { - +export function getMembersForClass(classType: ClassType, symbolTable: SymbolTable, includeInstanceVars: boolean) { _getMembersForClassRecursive(classType, symbolTable, includeInstanceVars); } @@ -957,9 +973,7 @@ export function getMembersForModule(moduleType: ModuleType, symbolTable: SymbolT }); } -export function containsUnknown(type: Type, allowUnknownTypeArgsForClasses = false, - recursionCount = 0): boolean { - +export function containsUnknown(type: Type, allowUnknownTypeArgsForClasses = false, recursionCount = 0): boolean { if (recursionCount > maxTypeRecursionCount) { return false; } @@ -971,9 +985,7 @@ export function containsUnknown(type: Type, allowUnknownTypeArgsForClasses = fal // See if a union contains an unknown type. if (type.category === TypeCategory.Union) { for (const subtype of type.subtypes) { - if (containsUnknown(subtype, allowUnknownTypeArgsForClasses, - recursionCount + 1)) { - + if (containsUnknown(subtype, allowUnknownTypeArgsForClasses, recursionCount + 1)) { return true; } } @@ -989,9 +1001,7 @@ export function containsUnknown(type: Type, allowUnknownTypeArgsForClasses = fal if (type.category === TypeCategory.Class) { if (type.typeArguments && !allowUnknownTypeArgsForClasses) { for (const argType of type.typeArguments) { - if (containsUnknown(argType, allowUnknownTypeArgsForClasses, - recursionCount + 1)) { - + if (containsUnknown(argType, allowUnknownTypeArgsForClasses, recursionCount + 1)) { return true; } } @@ -1024,18 +1034,19 @@ export function containsUnknown(type: Type, allowUnknownTypeArgsForClasses = fal return false; } -function _getMembersForClassRecursive(classType: ClassType, - symbolTable: SymbolTable, includeInstanceVars: boolean, - recursionCount = 0) { - +function _getMembersForClassRecursive( + classType: ClassType, + symbolTable: SymbolTable, + includeInstanceVars: boolean, + recursionCount = 0 +) { if (recursionCount > maxTypeRecursionCount) { return; } classType.details.baseClasses.forEach(baseClassType => { if (baseClassType.category === TypeCategory.Class) { - _getMembersForClassRecursive(baseClassType, - symbolTable, includeInstanceVars, recursionCount + 1); + _getMembersForClassRecursive(baseClassType, symbolTable, includeInstanceVars, recursionCount + 1); } }); @@ -1052,9 +1063,12 @@ function _getMembersForClassRecursive(classType: ClassType, }); } -function _specializeClassType(classType: ClassType, typeVarMap: TypeVarMap | undefined, - makeConcrete: boolean, recursionLevel: number): ClassType { - +function _specializeClassType( + classType: ClassType, + typeVarMap: TypeVarMap | undefined, + makeConcrete: boolean, + recursionLevel: number +): ClassType { // Handle the common case where the class has no type parameters. if (ClassType.getTypeParameters(classType).length === 0) { return classType; @@ -1066,8 +1080,7 @@ function _specializeClassType(classType: ClassType, typeVarMap: TypeVarMap | und // If type args were previously provided, specialize them. if (classType.typeArguments) { newTypeArgs = classType.typeArguments.map(oldTypeArgType => { - const newTypeArgType = specializeType(oldTypeArgType, - typeVarMap, makeConcrete, recursionLevel + 1); + const newTypeArgType = specializeType(oldTypeArgType, typeVarMap, makeConcrete, recursionLevel + 1); if (newTypeArgType !== oldTypeArgType) { specializationNeeded = true; } @@ -1118,13 +1131,16 @@ export function getConcreteTypeFromTypeVar(type: TypeVarType, recursionLevel = 0 return UnknownType.create(); } -function _specializeOverloadedFunctionType(type: OverloadedFunctionType, - typeVarMap: TypeVarMap | undefined, makeConcrete: boolean, - recursionLevel: number): OverloadedFunctionType { - +function _specializeOverloadedFunctionType( + type: OverloadedFunctionType, + typeVarMap: TypeVarMap | undefined, + makeConcrete: boolean, + recursionLevel: number +): OverloadedFunctionType { // Specialize each of the functions in the overload. - const overloads = type.overloads.map( - entry => _specializeFunctionType(entry, typeVarMap, makeConcrete, recursionLevel)); + const overloads = type.overloads.map(entry => + _specializeFunctionType(entry, typeVarMap, makeConcrete, recursionLevel) + ); // Construct a new overload with the specialized function types. const newOverloadType = OverloadedFunctionType.create(); @@ -1135,15 +1151,19 @@ function _specializeOverloadedFunctionType(type: OverloadedFunctionType, return newOverloadType; } -function _specializeFunctionType(functionType: FunctionType, - typeVarMap: TypeVarMap | undefined, makeConcrete: boolean, - recursionLevel: number): FunctionType { - - const declaredReturnType = functionType.specializedTypes && functionType.specializedTypes.returnType ? - functionType.specializedTypes.returnType : functionType.details.declaredReturnType; - const specializedReturnType = declaredReturnType ? - specializeType(declaredReturnType, typeVarMap, makeConcrete, recursionLevel + 1) : - undefined; +function _specializeFunctionType( + functionType: FunctionType, + typeVarMap: TypeVarMap | undefined, + makeConcrete: boolean, + recursionLevel: number +): FunctionType { + const declaredReturnType = + functionType.specializedTypes && functionType.specializedTypes.returnType + ? functionType.specializedTypes.returnType + : functionType.details.declaredReturnType; + const specializedReturnType = declaredReturnType + ? specializeType(declaredReturnType, typeVarMap, makeConcrete, recursionLevel + 1) + : undefined; let typesRequiredSpecialization = declaredReturnType !== specializedReturnType; const specializedParameters: SpecializedFunctionTypes = { @@ -1153,8 +1173,7 @@ function _specializeFunctionType(functionType: FunctionType, for (let i = 0; i < functionType.details.parameters.length; i++) { const paramType = FunctionType.getEffectiveParameterType(functionType, i); - const specializedType = specializeType(paramType, - typeVarMap, makeConcrete, recursionLevel + 1); + const specializedType = specializeType(paramType, typeVarMap, makeConcrete, recursionLevel + 1); specializedParameters.parameterTypes.push(specializedType); if (paramType !== specializedType) { @@ -1197,9 +1216,10 @@ export function requiresSpecialization(type: Type, recursionCount = 0): boolean return false; } - return type.typeArguments.find( - typeArg => requiresSpecialization(typeArg, recursionCount + 1) - ) !== undefined; + return ( + type.typeArguments.find(typeArg => requiresSpecialization(typeArg, recursionCount + 1)) !== + undefined + ); } // If there are any type parameters, we need to specialize @@ -1220,14 +1240,16 @@ export function requiresSpecialization(type: Type, recursionCount = 0): boolean return false; } - for (let i = 0; i < type.details.parameters.length; i ++) { + for (let i = 0; i < type.details.parameters.length; i++) { if (requiresSpecialization(FunctionType.getEffectiveParameterType(type, i), recursionCount + 1)) { return true; } } - const declaredReturnType = type.specializedTypes && type.specializedTypes.returnType ? - type.specializedTypes.returnType : type.details.declaredReturnType; + const declaredReturnType = + type.specializedTypes && type.specializedTypes.returnType + ? type.specializedTypes.returnType + : type.details.declaredReturnType; if (declaredReturnType) { if (requiresSpecialization(declaredReturnType, recursionCount + 1)) { return true; @@ -1238,13 +1260,11 @@ export function requiresSpecialization(type: Type, recursionCount = 0): boolean } case TypeCategory.OverloadedFunction: { - return type.overloads.find( - overload => requiresSpecialization(overload, recursionCount + 1)) !== undefined; + return type.overloads.find(overload => requiresSpecialization(overload, recursionCount + 1)) !== undefined; } case TypeCategory.Union: { - return type.subtypes.find( - type => requiresSpecialization(type, recursionCount + 1)) !== undefined; + return type.subtypes.find(type => requiresSpecialization(type, recursionCount + 1)) !== undefined; } case TypeCategory.TypeVar: { @@ -1262,10 +1282,10 @@ export function printLiteralValue(type: ObjectType): string { } let literalStr: string; - if (typeof(literalValue) === 'string') { - const prefix = (type.classType.details.name === 'bytes') ? 'b' : ''; - literalStr = `${ prefix }'${ literalValue.toString() }'`; - } else if (typeof(literalValue) === 'boolean') { + if (typeof literalValue === 'string') { + const prefix = type.classType.details.name === 'bytes' ? 'b' : ''; + literalStr = `${prefix}'${literalValue.toString()}'`; + } else if (typeof literalValue === 'boolean') { literalStr = literalValue ? 'True' : 'False'; } else { literalStr = literalValue.toString(); @@ -1280,5 +1300,5 @@ export function printLiteralType(type: ObjectType): string { return ''; } - return `Literal[${ literalStr }]`; + return `Literal[${literalStr}]`; } diff --git a/server/src/analyzer/typeVarMap.ts b/server/src/analyzer/typeVarMap.ts index cdf194ec9..bc0a478d6 100644 --- a/server/src/analyzer/typeVarMap.ts +++ b/server/src/analyzer/typeVarMap.ts @@ -1,16 +1,16 @@ /* -* typeVarMap.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Module that records the relationship between named TypeVars -* (type variables) and their types. It is used by the type -* evaluator to "solve" for the type of each type variable. -*/ + * typeVarMap.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Module that records the relationship between named TypeVars + * (type variables) and their types. It is used by the type + * evaluator to "solve" for the type of each type variable. + */ -import { assert } from "../common/debug"; -import { Type } from "./types"; +import { assert } from '../common/debug'; +import { Type } from './types'; export class TypeVarMap { private _typeMap: Map; diff --git a/server/src/analyzer/types.ts b/server/src/analyzer/types.ts index 04e27606a..938fadb7f 100644 --- a/server/src/analyzer/types.ts +++ b/server/src/analyzer/types.ts @@ -1,11 +1,11 @@ /* -* types.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Representation of types used during type analysis within Python. -*/ + * types.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Representation of types used during type analysis within Python. + */ import { assert } from '../common/debug'; import { ParameterCategory } from '../parser/parseNodes'; @@ -56,9 +56,19 @@ export const enum TypeCategory { TypeVar } -export type Type = UnboundType | UnknownType | AnyType | NoneType | NeverType | - FunctionType | OverloadedFunctionType | ClassType | - ObjectType | ModuleType | UnionType | TypeVarType; +export type Type = + | UnboundType + | UnknownType + | AnyType + | NoneType + | NeverType + | FunctionType + | OverloadedFunctionType + | ClassType + | ObjectType + | ModuleType + | UnionType + | TypeVarType; export type LiteralValue = number | boolean | string; @@ -145,34 +155,34 @@ export const enum ClassTypeFlags { None = 0, // Class is defined in the "builtins" or "typings" file. - BuiltInClass = 1 << 0, + BuiltInClass = 1 << 0, // Class requires special-case handling because it // exhibits non-standard behavior or is not defined // formally as a class. Examples include 'Optional' // and 'Union'. - SpecialBuiltIn = 1 << 1, + SpecialBuiltIn = 1 << 1, // Introduced in Python 3.7 - class either derives directly // from NamedTuple or has a @dataclass class decorator. - DataClass = 1 << 2, + DataClass = 1 << 2, // Flags that control whether methods should be // synthesized for a dataclass class. - SkipSynthesizedInit = 1 << 3, + SkipSynthesizedInit = 1 << 3, // Introduced in PEP 589, TypedDict classes provide a way // to specify type hints for dictionaries with different // value types and a limited set of static keys. - TypedDictClass = 1 << 4, + TypedDictClass = 1 << 4, // Used in conjunction with TypedDictClass, indicates that // the dictionary values can be omitted. - CanOmitDictValues = 1 << 5, + CanOmitDictValues = 1 << 5, // The class has a metaclass of EnumMet or derives from // a class that has this metaclass. - EnumClass = 1 << 6, + EnumClass = 1 << 6, // The class derives from a class that has the ABCMeta // metaclass. Such classes are allowed to contain @@ -182,18 +192,18 @@ export const enum ClassTypeFlags { // The class has at least one abstract method or derives // from a base class that is abstract without providing // non-abstract overrides for all abstract methods. - HasAbstractMethods = 1 << 8, + HasAbstractMethods = 1 << 8, // Derives from property class and has the semantics of // a property (with optional setter, deleter). - PropertyClass = 1 << 9, + PropertyClass = 1 << 9, // The class is decorated with a "@final" decorator // indicating that it cannot be subclassed. - Final = 1 << 10, + Final = 1 << 10, // The class derives directly from "Protocol". - ProtocolClass = 1 << 11 + ProtocolClass = 1 << 11 } interface ClassDetails { @@ -224,9 +234,7 @@ export interface ClassType extends TypeBase { } export namespace ClassType { - export function create(name: string, flags: ClassTypeFlags, - typeSourceId: TypeSourceId, docString?: string) { - + export function create(name: string, flags: ClassTypeFlags, typeSourceId: TypeSourceId, docString?: string) { const newClass: ClassType = { category: TypeCategory.Class, details: { @@ -244,11 +252,12 @@ export namespace ClassType { return newClass; } - export function cloneForSpecialization(classType: ClassType, - typeArguments: Type[] | undefined, skipAbstractClassTest = false): ClassType { - - const newClassType = create(classType.details.name, - classType.details.flags, classType.details.typeSourceId); + export function cloneForSpecialization( + classType: ClassType, + typeArguments: Type[] | undefined, + skipAbstractClassTest = false + ): ClassType { + const newClassType = create(classType.details.name, classType.details.flags, classType.details.typeSourceId); newClassType.details = classType.details; newClassType.typeArguments = typeArguments; if (skipAbstractClassTest) { @@ -260,8 +269,7 @@ export namespace ClassType { // Specifies whether the class type is generic (unspecialized) // or specialized. export function isGeneric(classType: ClassType) { - return classType.details.typeParameters.length > 0 && - classType.typeArguments === undefined; + return classType.details.typeParameters.length > 0 && classType.typeArguments === undefined; } export function isSpecialBuiltIn(classType: ClassType, className?: string) { @@ -290,19 +298,20 @@ export namespace ClassType { export function isProtocol(classType: ClassType) { // Does the class directly 'derive' from "Protocol"? - return classType.details.baseClasses.find(baseClass => { - if (baseClass.category === TypeCategory.Class) { - if (isBuiltIn(baseClass, 'Protocol')) { - return true; + return ( + classType.details.baseClasses.find(baseClass => { + if (baseClass.category === TypeCategory.Class) { + if (isBuiltIn(baseClass, 'Protocol')) { + return true; + } } - } - return false; - }) !== undefined; + return false; + }) !== undefined + ); } export function hasAbstractMethods(classType: ClassType) { - return !!(classType.details.flags & ClassTypeFlags.HasAbstractMethods) && - !classType.skipAbstractClassTest; + return !!(classType.details.flags & ClassTypeFlags.HasAbstractMethods) && !classType.skipAbstractClassTest; } export function supportsAbstractMethods(classType: ClassType) { @@ -367,10 +376,8 @@ export namespace ClassType { // If either or both have aliases (e.g. List -> list), use the // aliases for comparison purposes. - const class1Details = classType.details.aliasClass ? - classType.details.aliasClass.details : classType.details; - const class2Details = type2.details.aliasClass ? - type2.details.aliasClass.details : type2.details; + const class1Details = classType.details.aliasClass ? classType.details.aliasClass.details : classType.details; + const class2Details = type2.details.aliasClass ? type2.details.aliasClass.details : type2.details; if (class1Details === class2Details) { return true; @@ -378,34 +385,34 @@ export namespace ClassType { // Compare most of the details fields. We intentionally skip the isAbstractClass // flag because it gets set dynamically. - if (class1Details.name !== class2Details.name || - class1Details.flags !== class2Details.flags || - class1Details.typeSourceId !== class2Details.typeSourceId || - class1Details.baseClasses.length !== class2Details.baseClasses.length || - class1Details.typeParameters.length !== class2Details.typeParameters.length) { + if ( + class1Details.name !== class2Details.name || + class1Details.flags !== class2Details.flags || + class1Details.typeSourceId !== class2Details.typeSourceId || + class1Details.baseClasses.length !== class2Details.baseClasses.length || + class1Details.typeParameters.length !== class2Details.typeParameters.length + ) { return false; } for (let i = 0; i < class1Details.baseClasses.length; i++) { - if (!isTypeSame(class1Details.baseClasses[i], class2Details.baseClasses[i], - recursionCount + 1)) { - + if (!isTypeSame(class1Details.baseClasses[i], class2Details.baseClasses[i], recursionCount + 1)) { return false; } } if (class1Details.metaClass || class2Details.metaClass) { - if (!class1Details.metaClass || !class2Details.metaClass || - !isTypeSame(class1Details.metaClass, class2Details.metaClass)) { - + if ( + !class1Details.metaClass || + !class2Details.metaClass || + !isTypeSame(class1Details.metaClass, class2Details.metaClass) + ) { return false; } } for (let i = 0; i < class1Details.typeParameters.length; i++) { - if (!isTypeSame(class1Details.typeParameters[i], class2Details.typeParameters[i], - recursionCount + 1)) { - + if (!isTypeSame(class1Details.typeParameters[i], class2Details.typeParameters[i], recursionCount + 1)) { return false; } } @@ -417,10 +424,11 @@ export namespace ClassType { } for (let i = 0; i < dataClassEntries1.length; i++) { - if (dataClassEntries1[i].name !== dataClassEntries2[i].name || - dataClassEntries1[i].hasDefault !== dataClassEntries2[i].hasDefault || - !isTypeSame(dataClassEntries1[i].type, dataClassEntries2[i].type, recursionCount + 1)) { - + if ( + dataClassEntries1[i].name !== dataClassEntries2[i].name || + dataClassEntries1[i].hasDefault !== dataClassEntries2[i].hasDefault || + !isTypeSame(dataClassEntries1[i].type, dataClassEntries2[i].type, recursionCount + 1) + ) { return false; } } @@ -460,9 +468,11 @@ export namespace ClassType { // array to inheritanceChain, it will be filled in by // the call to include the chain of inherited classes starting // with type2 and ending with this type. - export function isDerivedFrom(subclassType: ClassType, - parentClassType: ClassType, inheritanceChain?: InheritanceChain): boolean { - + export function isDerivedFrom( + subclassType: ClassType, + parentClassType: ClassType, + inheritanceChain?: InheritanceChain + ): boolean { // Is it the exact same class? if (isSameGenericClass(subclassType, parentClassType)) { if (inheritanceChain) { @@ -537,55 +547,55 @@ export interface FunctionParameter { } export const enum FunctionTypeFlags { - None = 0, + None = 0, // Function is a __new__ method; first parameter is "cls" - ConstructorMethod = 1 << 0, + ConstructorMethod = 1 << 0, // Function is decorated with @classmethod; first parameter is "cls"; // can be bound to associated class - ClassMethod = 1 << 1, + ClassMethod = 1 << 1, // Function is decorated with @staticmethod; cannot be bound to class - StaticMethod = 1 << 2, + StaticMethod = 1 << 2, // Function is decorated with @abstractmethod - AbstractMethod = 1 << 3, + AbstractMethod = 1 << 3, // Function contains "yield" or "yield from" statements - Generator = 1 << 4, + Generator = 1 << 4, // Skip check that validates that all parameters without default // value expressions have corresponding arguments; used for // named tuples in some cases - DisableDefaultChecks = 1 << 5, + DisableDefaultChecks = 1 << 5, // Method has no declaration in user code, it's synthesized; used // for implied methods such as those used in namedtuple, dataclass, etc. - SynthesizedMethod = 1 << 6, + SynthesizedMethod = 1 << 6, // For some synthesized classes (in particular, NamedTuple), the // __init__ method is created with default parameters, so we will // skip the constructor check for these methods. - SkipConstructorCheck = 1 << 7, + SkipConstructorCheck = 1 << 7, // Function is decorated with @overload - Overloaded = 1 << 8, + Overloaded = 1 << 8, // Function is declared with async keyword - Async = 1 << 9, + Async = 1 << 9, // Indicates that return type should be wrapped in an awaitable type - WrapReturnTypeInAwait = 1 << 10, + WrapReturnTypeInAwait = 1 << 10, // Function is declared within a type stub fille - StubDefinition = 1 << 11, + StubDefinition = 1 << 11, // Function is decorated with @final - Final = 1 << 12, + Final = 1 << 12, // Function has one or more parameters that are missing type annotations - UnannotatedParams = 1 << 13 + UnannotatedParams = 1 << 13 } interface FunctionDetails { @@ -650,8 +660,7 @@ export namespace FunctionType { // If we strip off the first parameter, this is no longer an // instance method or class method. if (deleteFirstParam) { - newFunction.details.flags &= ~(FunctionTypeFlags.ConstructorMethod | - FunctionTypeFlags.ClassMethod); + newFunction.details.flags &= ~(FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.ClassMethod); newFunction.details.flags |= FunctionTypeFlags.StaticMethod; newFunction.ignoreFirstParamOfDeclaration = true; } @@ -671,9 +680,10 @@ export namespace FunctionType { // Creates a shallow copy of the function type with new // specialized types. The clone shares the _functionDetails // with the object being cloned. - export function cloneForSpecialization(type: FunctionType, - specializedTypes: SpecializedFunctionTypes): FunctionType { - + export function cloneForSpecialization( + type: FunctionType, + specializedTypes: SpecializedFunctionTypes + ): FunctionType { const newFunction = create(type.details.flags, type.details.docString); newFunction.details = type.details; @@ -684,8 +694,13 @@ export namespace FunctionType { } export function isInstanceMethod(type: FunctionType): boolean { - return (type.details.flags & (FunctionTypeFlags.ConstructorMethod | - FunctionTypeFlags.StaticMethod | FunctionTypeFlags.ClassMethod)) === 0; + return ( + (type.details.flags & + (FunctionTypeFlags.ConstructorMethod | + FunctionTypeFlags.StaticMethod | + FunctionTypeFlags.ClassMethod)) === + 0 + ); } export function isConstructorMethod(type: FunctionType): boolean { @@ -758,8 +773,9 @@ export namespace FunctionType { } export function getSpecializedReturnType(type: FunctionType) { - return type.specializedTypes && type.specializedTypes.returnType ? - type.specializedTypes.returnType : type.details.declaredReturnType; + return type.specializedTypes && type.specializedTypes.returnType + ? type.specializedTypes.returnType + : type.details.declaredReturnType; } } @@ -886,13 +902,11 @@ export namespace TypeVarType { } export function isNoneOrNever(type: Type): boolean { - return type.category === TypeCategory.None || - type.category === TypeCategory.Never; + return type.category === TypeCategory.None || type.category === TypeCategory.Never; } export function isAnyOrUnknown(type: Type): boolean { - if (type.category === TypeCategory.Any || - type.category === TypeCategory.Unknown) { + if (type.category === TypeCategory.Any || type.category === TypeCategory.Unknown) { return true; } @@ -1005,9 +1019,7 @@ export function isTypeSame(type1: Type, type2: Type, recursionCount = 0): boolea return2Type = functionType2.specializedTypes.returnType; } if (return1Type || return2Type) { - if (!return1Type || !return2Type || - !isTypeSame(return1Type, return2Type, recursionCount + 1)) { - + if (!return1Type || !return2Type || !isTypeSame(return1Type, return2Type, recursionCount + 1)) { return false; } } diff --git a/server/src/commands/commands.ts b/server/src/commands/commands.ts index 986237902..7553c7039 100644 --- a/server/src/commands/commands.ts +++ b/server/src/commands/commands.ts @@ -1,11 +1,11 @@ /* -* commands.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Command identifier strings. -*/ + * commands.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Command identifier strings. + */ export const enum Commands { createTypeStub = 'pyright.createtypestub', diff --git a/server/src/commands/createTypeStub.ts b/server/src/commands/createTypeStub.ts index 4509b72b7..5ee62b875 100644 --- a/server/src/commands/createTypeStub.ts +++ b/server/src/commands/createTypeStub.ts @@ -13,7 +13,7 @@ import { AnalyzerServiceExecutor } from '../languageService/analyzerServiceExecu import { ServerCommand } from './commandController'; export class CreateTypeStubCommand implements ServerCommand { - constructor(private _ls: LanguageServerInterface) { } + constructor(private _ls: LanguageServerInterface) {} async execute(cmdParams: ExecuteCommandParams): Promise { if (cmdParams.arguments && cmdParams.arguments.length >= 2) { @@ -25,7 +25,7 @@ export class CreateTypeStubCommand implements ServerCommand { // Allocate a temporary pseudo-workspace to perform this job. const workspace: WorkspaceServiceInstance = { - workspaceName: `Create Type Stub ${ importName }`, + workspaceName: `Create Type Stub ${importName}`, rootPath: workspaceRoot, rootUri: convertPathToUri(workspaceRoot), serviceInstance: service, @@ -37,7 +37,7 @@ export class CreateTypeStubCommand implements ServerCommand { try { service.writeTypeStub(); service.dispose(); - const infoMessage = `Type stub was successfully created for '${ importName }'.`; + const infoMessage = `Type stub was successfully created for '${importName}'.`; this._ls.window.showInformationMessage(infoMessage); this._handlePostCreateTypeStub(); } catch (err) { @@ -45,7 +45,7 @@ export class CreateTypeStubCommand implements ServerCommand { if (err instanceof Error) { errMessage = ': ' + err.message; } - errMessage = `An error occurred when creating type stub for '${ importName }'` + errMessage; + errMessage = `An error occurred when creating type stub for '${importName}'` + errMessage; this._ls.console.error(errMessage); this._ls.window.showErrorMessage(errMessage); } diff --git a/server/src/commands/quickActionCommand.ts b/server/src/commands/quickActionCommand.ts index 3779f7281..2629a853a 100644 --- a/server/src/commands/quickActionCommand.ts +++ b/server/src/commands/quickActionCommand.ts @@ -11,7 +11,7 @@ import { LanguageServerInterface } from '../languageServerBase'; import { ServerCommand } from './commandController'; export class QuickActionCommand implements ServerCommand { - constructor(private _ls: LanguageServerInterface) { } + constructor(private _ls: LanguageServerInterface) {} async execute(cmdParams: ExecuteCommandParams): Promise { if (cmdParams.arguments && cmdParams.arguments.length >= 1) { diff --git a/server/src/common/collectionUtils.ts b/server/src/common/collectionUtils.ts index 2d3685751..181a1eef2 100644 --- a/server/src/common/collectionUtils.ts +++ b/server/src/common/collectionUtils.ts @@ -11,7 +11,11 @@ import { compareValues, Comparison, equateValues, isArray } from './core'; export const emptyArray: never[] = [] as never[]; export type EqualityComparer = (a: T, b: T) => boolean; -export function contains(array: readonly T[] | undefined, value: T, equalityComparer: EqualityComparer = equateValues): boolean { +export function contains( + array: readonly T[] | undefined, + value: T, + equalityComparer: EqualityComparer = equateValues +): boolean { if (array) { for (const v of array) { if (equalityComparer(v, value)) { @@ -36,20 +40,29 @@ export interface Push { * appended. */ export function append[number] | undefined>( - to: TArray, value: TValue): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable[number][]; + to: TArray, + value: TValue +): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable[number][]; export function append(to: T[], value: T | undefined): T[]; export function append(to: T[] | undefined, value: T): T[]; export function append(to: T[] | undefined, value: T | undefined): T[] | undefined; export function append(to: Push, value: T | undefined): void; export function append(to: T[], value: T | undefined): T[] | undefined { - if (value === undefined) { return to; } - if (to === undefined) { return [value]; } + if (value === undefined) { + return to; + } + if (to === undefined) { + return [value]; + } to.push(value); return to; } /** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ -export function find(array: readonly T[], predicate: (element: T, index: number) => element is U): U | undefined; +export function find( + array: readonly T[], + predicate: (element: T, index: number) => element is U +): U | undefined; export function find(array: readonly T[], predicate: (element: T, index: number) => boolean): T | undefined; export function find(array: readonly T[], predicate: (element: T, index: number) => boolean): T | undefined { for (let i = 0; i < array.length; i++) { @@ -80,10 +93,24 @@ function toOffset(array: readonly any[], offset: number) { * @param end The offset in `from` at which to stop copying values (non-inclusive). */ export function addRange(to: T[], from: readonly T[] | undefined, start?: number, end?: number): T[]; -export function addRange(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined; -export function addRange(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined { - if (from === undefined || from.length === 0) { return to; } - if (to === undefined) { return from.slice(start, end); } +export function addRange( + to: T[] | undefined, + from: readonly T[] | undefined, + start?: number, + end?: number +): T[] | undefined; +export function addRange( + to: T[] | undefined, + from: readonly T[] | undefined, + start?: number, + end?: number +): T[] | undefined { + if (from === undefined || from.length === 0) { + return to; + } + if (to === undefined) { + return from.slice(start, end); + } start = start === undefined ? 0 : toOffset(from, start); end = end === undefined ? from.length : toOffset(from, end); for (let i = start; i < end && i < from.length; i++) { @@ -139,7 +166,7 @@ function indicesOf(array: readonly unknown[]): number[] { export function stableSort(array: readonly T[], comparer: Comparer): SortedReadonlyArray { const indices = indicesOf(array); stableSortIndices(array, indices, comparer); - return indices.map(i => array[i]) as SortedArray as SortedReadonlyArray; + return (indices.map(i => array[i]) as SortedArray) as SortedReadonlyArray; } function stableSortIndices(array: readonly T[], indices: number[], comparer: Comparer) { @@ -170,10 +197,10 @@ export function some(array: readonly T[] | undefined, predicate?: (value: T) } /** -* Iterates through `array` by index and performs the callback on each element of array until the callback -* returns a falsey value, then returns false. -* If no such value is found, the callback is applied to each element of array and `true` is returned. -*/ + * Iterates through `array` by index and performs the callback on each element of array until the callback + * returns a falsey value, then returns false. + * If no such value is found, the callback is applied to each element of array and `true` is returned. + */ export function every(array: readonly T[], callback: (element: T, index: number) => boolean): boolean { if (array) { return array.every(callback); @@ -193,9 +220,13 @@ export function every(array: readonly T[], callback: (element: T, index: numb * @param keyComparer A callback used to compare two keys in a sorted array. * @param offset An offset into `array` at which to start the search. */ -export function binarySearch(array: readonly T[], value: T, - keySelector: (v: T) => U, keyComparer: Comparer, offset?: number): number { - +export function binarySearch( + array: readonly T[], + value: T, + keySelector: (v: T) => U, + keyComparer: Comparer, + offset?: number +): number { return binarySearchKey(array, keySelector(value), keySelector, keyComparer, offset); } @@ -209,9 +240,13 @@ export function binarySearch(array: readonly T[], value: T, * @param keyComparer A callback used to compare two keys in a sorted array. * @param offset An offset into `array` at which to start the search. */ -export function binarySearchKey(array: readonly T[], key: U, - keySelector: (v: T) => U, keyComparer: Comparer, offset?: number): number { - +export function binarySearchKey( + array: readonly T[], + key: U, + keySelector: (v: T) => U, + keyComparer: Comparer, + offset?: number +): number { if (!some(array)) { return -1; } diff --git a/server/src/common/commandLineOptions.ts b/server/src/common/commandLineOptions.ts index 2cd1037ad..6f7f3e659 100644 --- a/server/src/common/commandLineOptions.ts +++ b/server/src/common/commandLineOptions.ts @@ -1,13 +1,13 @@ /* -* commandLineOptions.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Class that holds the command-line options (those that can be -* passed into the main entry point of the command-line version -* of the analyzer). -*/ + * commandLineOptions.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Class that holds the command-line options (those that can be + * passed into the main entry point of the command-line version + * of the analyzer). + */ // Some options can be specified by command line. export class CommandLineOptions { diff --git a/server/src/common/configOptions.ts b/server/src/common/configOptions.ts index 887b5ac7f..17637046d 100644 --- a/server/src/common/configOptions.ts +++ b/server/src/common/configOptions.ts @@ -1,18 +1,17 @@ /* -* configOptions.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Class that holds the configuration options for the analyzer. -*/ + * configOptions.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Class that holds the configuration options for the analyzer. + */ import { isAbsolute } from 'path'; import { ConsoleInterface } from './console'; import { DiagnosticRule } from './diagnosticRules'; -import { combinePaths, ensureTrailingDirectorySeparator, FileSpec, - getFileSpec, normalizePath } from './pathUtils'; +import { combinePaths, ensureTrailingDirectorySeparator, FileSpec, getFileSpec, normalizePath } from './pathUtils'; import { latestStablePythonVersion, PythonVersion, versionFromString } from './pythonVersion'; export class ExecutionEnvironment { @@ -163,9 +162,7 @@ export interface DiagnosticSettings { reportImplicitStringConcatenation: DiagnosticLevel; } -export function cloneDiagnosticSettings( - diagSettings: DiagnosticSettings): DiagnosticSettings { - +export function cloneDiagnosticSettings(diagSettings: DiagnosticSettings): DiagnosticSettings { // Create a shallow copy of the existing object. return Object.assign({}, diagSettings); } @@ -399,14 +396,12 @@ export class ConfigOptions { // execution environment is used. findExecEnvironment(filePath: string): ExecutionEnvironment { let execEnv = this.executionEnvironments.find(env => { - const envRoot = ensureTrailingDirectorySeparator( - normalizePath(combinePaths(this.projectRoot, env.root))); + const envRoot = ensureTrailingDirectorySeparator(normalizePath(combinePaths(this.projectRoot, env.root))); return filePath.startsWith(envRoot); }); if (!execEnv) { - execEnv = new ExecutionEnvironment(this.projectRoot, - this.defaultPythonVersion, this.defaultPythonPlatform); + execEnv = new ExecutionEnvironment(this.projectRoot, this.defaultPythonVersion, this.defaultPythonPlatform); } return execEnv; @@ -423,9 +418,9 @@ export class ConfigOptions { const filesList = configObj.include as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { - console.log(`Index ${ index } of "include" array should be a string.`); + console.log(`Index ${index} of "include" array should be a string.`); } else if (isAbsolute(fileSpec)) { - console.log(`Ignoring path "${ fileSpec }" in "include" array because it is not relative.`); + console.log(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`); } else { this.include.push(getFileSpec(this.projectRoot, fileSpec)); } @@ -442,9 +437,9 @@ export class ConfigOptions { const filesList = configObj.exclude as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { - console.log(`Index ${ index } of "exclude" array should be a string.`); + console.log(`Index ${index} of "exclude" array should be a string.`); } else if (isAbsolute(fileSpec)) { - console.log(`Ignoring path "${ fileSpec }" in "exclude" array because it is not relative.`); + console.log(`Ignoring path "${fileSpec}" in "exclude" array because it is not relative.`); } else { this.exclude.push(getFileSpec(this.projectRoot, fileSpec)); } @@ -461,9 +456,9 @@ export class ConfigOptions { const filesList = configObj.ignore as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { - console.log(`Index ${ index } of "ignore" array should be a string.`); + console.log(`Index ${index} of "ignore" array should be a string.`); } else if (isAbsolute(fileSpec)) { - console.log(`Ignoring path "${ fileSpec }" in "ignore" array because it is not relative.`); + console.log(`Ignoring path "${fileSpec}" in "ignore" array because it is not relative.`); } else { this.ignore.push(getFileSpec(this.projectRoot, fileSpec)); } @@ -480,9 +475,9 @@ export class ConfigOptions { const filesList = configObj.strict as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { - console.log(`Index ${ index } of "strict" array should be a string.`); + console.log(`Index ${index} of "strict" array should be a string.`); } else if (isAbsolute(fileSpec)) { - console.log(`Ignoring path "${ fileSpec }" in "strict" array because it is not relative.`); + console.log(`Ignoring path "${fileSpec}" in "strict" array because it is not relative.`); } else { this.strict.push(getFileSpec(this.projectRoot, fileSpec)); } @@ -495,194 +490,270 @@ export class ConfigOptions { this.diagnosticSettings = { // Use strict inference rules for list expressions? strictListInference: this._convertBoolean( - configObj.strictListInference, DiagnosticRule.strictListInference, - defaultSettings.strictListInference), + configObj.strictListInference, + DiagnosticRule.strictListInference, + defaultSettings.strictListInference + ), // Use strict inference rules for dictionary expressions? strictDictionaryInference: this._convertBoolean( - configObj.strictDictionaryInference, DiagnosticRule.strictDictionaryInference, - defaultSettings.strictDictionaryInference), + configObj.strictDictionaryInference, + DiagnosticRule.strictDictionaryInference, + defaultSettings.strictDictionaryInference + ), // Should a None default value imply that the parameter type // is Optional? strictParameterNoneValue: this._convertBoolean( - configObj.strictParameterNoneValue, DiagnosticRule.strictParameterNoneValue, - defaultSettings.strictParameterNoneValue), + configObj.strictParameterNoneValue, + DiagnosticRule.strictParameterNoneValue, + defaultSettings.strictParameterNoneValue + ), // Should "# type: ignore" be honored? enableTypeIgnoreComments: this._convertBoolean( - configObj.enableTypeIgnoreComments, DiagnosticRule.enableTypeIgnoreComments, - defaultSettings.enableTypeIgnoreComments), + configObj.enableTypeIgnoreComments, + DiagnosticRule.enableTypeIgnoreComments, + defaultSettings.enableTypeIgnoreComments + ), // Read the "reportTypeshedErrors" entry. reportTypeshedErrors: this._convertDiagnosticLevel( - configObj.reportTypeshedErrors, DiagnosticRule.reportTypeshedErrors, - defaultSettings.reportTypeshedErrors), + configObj.reportTypeshedErrors, + DiagnosticRule.reportTypeshedErrors, + defaultSettings.reportTypeshedErrors + ), // Read the "reportMissingImports" entry. reportMissingImports: this._convertDiagnosticLevel( - configObj.reportMissingImports, DiagnosticRule.reportMissingImports, - defaultSettings.reportMissingImports), + configObj.reportMissingImports, + DiagnosticRule.reportMissingImports, + defaultSettings.reportMissingImports + ), // Read the "reportUnusedImport" entry. reportUnusedImport: this._convertDiagnosticLevel( - configObj.reportUnusedImport, DiagnosticRule.reportUnusedImport, - defaultSettings.reportUnusedImport), + configObj.reportUnusedImport, + DiagnosticRule.reportUnusedImport, + defaultSettings.reportUnusedImport + ), // Read the "reportUnusedClass" entry. reportUnusedClass: this._convertDiagnosticLevel( - configObj.reportUnusedClass, DiagnosticRule.reportUnusedClass, - defaultSettings.reportUnusedClass), + configObj.reportUnusedClass, + DiagnosticRule.reportUnusedClass, + defaultSettings.reportUnusedClass + ), // Read the "reportUnusedFunction" entry. reportUnusedFunction: this._convertDiagnosticLevel( - configObj.reportUnusedFunction, DiagnosticRule.reportUnusedFunction, - defaultSettings.reportUnusedFunction), + configObj.reportUnusedFunction, + DiagnosticRule.reportUnusedFunction, + defaultSettings.reportUnusedFunction + ), // Read the "reportUnusedVariable" entry. reportUnusedVariable: this._convertDiagnosticLevel( - configObj.reportUnusedVariable, DiagnosticRule.reportUnusedVariable, - defaultSettings.reportUnusedVariable), + configObj.reportUnusedVariable, + DiagnosticRule.reportUnusedVariable, + defaultSettings.reportUnusedVariable + ), // Read the "reportDuplicateImport" entry. reportDuplicateImport: this._convertDiagnosticLevel( - configObj.reportDuplicateImport, DiagnosticRule.reportDuplicateImport, - defaultSettings.reportDuplicateImport), + configObj.reportDuplicateImport, + DiagnosticRule.reportDuplicateImport, + defaultSettings.reportDuplicateImport + ), // Read the "reportMissingTypeStubs" entry. reportMissingTypeStubs: this._convertDiagnosticLevel( - configObj.reportMissingTypeStubs, DiagnosticRule.reportMissingTypeStubs, - defaultSettings.reportMissingTypeStubs), + configObj.reportMissingTypeStubs, + DiagnosticRule.reportMissingTypeStubs, + defaultSettings.reportMissingTypeStubs + ), // Read the "reportImportCycles" entry. reportImportCycles: this._convertDiagnosticLevel( - configObj.reportImportCycles, DiagnosticRule.reportImportCycles, - defaultSettings.reportImportCycles), + configObj.reportImportCycles, + DiagnosticRule.reportImportCycles, + defaultSettings.reportImportCycles + ), // Read the "reportOptionalSubscript" entry. reportOptionalSubscript: this._convertDiagnosticLevel( - configObj.reportOptionalSubscript, DiagnosticRule.reportOptionalSubscript, - defaultSettings.reportOptionalSubscript), + configObj.reportOptionalSubscript, + DiagnosticRule.reportOptionalSubscript, + defaultSettings.reportOptionalSubscript + ), // Read the "reportOptionalMemberAccess" entry. reportOptionalMemberAccess: this._convertDiagnosticLevel( - configObj.reportOptionalMemberAccess, DiagnosticRule.reportOptionalMemberAccess, - defaultSettings.reportOptionalMemberAccess), + configObj.reportOptionalMemberAccess, + DiagnosticRule.reportOptionalMemberAccess, + defaultSettings.reportOptionalMemberAccess + ), // Read the "reportOptionalCall" entry. reportOptionalCall: this._convertDiagnosticLevel( - configObj.reportOptionalCall, DiagnosticRule.reportOptionalCall, - defaultSettings.reportOptionalCall), + configObj.reportOptionalCall, + DiagnosticRule.reportOptionalCall, + defaultSettings.reportOptionalCall + ), // Read the "reportOptionalIterable" entry. reportOptionalIterable: this._convertDiagnosticLevel( - configObj.reportOptionalIterable, DiagnosticRule.reportOptionalIterable, - defaultSettings.reportOptionalIterable), + configObj.reportOptionalIterable, + DiagnosticRule.reportOptionalIterable, + defaultSettings.reportOptionalIterable + ), // Read the "reportOptionalContextManager" entry. reportOptionalContextManager: this._convertDiagnosticLevel( - configObj.reportOptionalContextManager, DiagnosticRule.reportOptionalContextManager, - defaultSettings.reportOptionalContextManager), + configObj.reportOptionalContextManager, + DiagnosticRule.reportOptionalContextManager, + defaultSettings.reportOptionalContextManager + ), // Read the "reportOptionalOperand" entry. reportOptionalOperand: this._convertDiagnosticLevel( - configObj.reportOptionalOperand, DiagnosticRule.reportOptionalOperand, - defaultSettings.reportOptionalOperand), + configObj.reportOptionalOperand, + DiagnosticRule.reportOptionalOperand, + defaultSettings.reportOptionalOperand + ), // Read the "reportUntypedFunctionDecorator" entry. reportUntypedFunctionDecorator: this._convertDiagnosticLevel( - configObj.reportUntypedFunctionDecorator, DiagnosticRule.reportUntypedFunctionDecorator, - defaultSettings.reportUntypedFunctionDecorator), + configObj.reportUntypedFunctionDecorator, + DiagnosticRule.reportUntypedFunctionDecorator, + defaultSettings.reportUntypedFunctionDecorator + ), // Read the "reportUntypedClassDecorator" entry. reportUntypedClassDecorator: this._convertDiagnosticLevel( - configObj.reportUntypedClassDecorator, DiagnosticRule.reportUntypedClassDecorator, - defaultSettings.reportUntypedClassDecorator), + configObj.reportUntypedClassDecorator, + DiagnosticRule.reportUntypedClassDecorator, + defaultSettings.reportUntypedClassDecorator + ), // Read the "reportUntypedBaseClass" entry. reportUntypedBaseClass: this._convertDiagnosticLevel( - configObj.reportUntypedBaseClass, DiagnosticRule.reportUntypedBaseClass, - defaultSettings.reportUntypedBaseClass), + configObj.reportUntypedBaseClass, + DiagnosticRule.reportUntypedBaseClass, + defaultSettings.reportUntypedBaseClass + ), // Read the "reportUntypedNamedTuple" entry. reportUntypedNamedTuple: this._convertDiagnosticLevel( - configObj.reportUntypedNamedTuple, DiagnosticRule.reportUntypedNamedTuple, - defaultSettings.reportUntypedNamedTuple), + configObj.reportUntypedNamedTuple, + DiagnosticRule.reportUntypedNamedTuple, + defaultSettings.reportUntypedNamedTuple + ), // Read the "reportPrivateUsage" entry. reportPrivateUsage: this._convertDiagnosticLevel( - configObj.reportPrivateUsage, DiagnosticRule.reportPrivateUsage, - defaultSettings.reportPrivateUsage), + configObj.reportPrivateUsage, + DiagnosticRule.reportPrivateUsage, + defaultSettings.reportPrivateUsage + ), // Read the "reportConstantRedefinition" entry. reportConstantRedefinition: this._convertDiagnosticLevel( - configObj.reportConstantRedefinition, DiagnosticRule.reportConstantRedefinition, - defaultSettings.reportConstantRedefinition), + configObj.reportConstantRedefinition, + DiagnosticRule.reportConstantRedefinition, + defaultSettings.reportConstantRedefinition + ), // Read the "reportIncompatibleMethodOverride" entry. reportIncompatibleMethodOverride: this._convertDiagnosticLevel( - configObj.reportIncompatibleMethodOverride, DiagnosticRule.reportIncompatibleMethodOverride, - defaultSettings.reportIncompatibleMethodOverride), + configObj.reportIncompatibleMethodOverride, + DiagnosticRule.reportIncompatibleMethodOverride, + defaultSettings.reportIncompatibleMethodOverride + ), // Read the "reportInvalidStringEscapeSequence" entry. reportInvalidStringEscapeSequence: this._convertDiagnosticLevel( - configObj.reportInvalidStringEscapeSequence, DiagnosticRule.reportInvalidStringEscapeSequence, - defaultSettings.reportInvalidStringEscapeSequence), + configObj.reportInvalidStringEscapeSequence, + DiagnosticRule.reportInvalidStringEscapeSequence, + defaultSettings.reportInvalidStringEscapeSequence + ), // Read the "reportUnknownParameterType" entry. reportUnknownParameterType: this._convertDiagnosticLevel( - configObj.reportUnknownParameterType, DiagnosticRule.reportUnknownParameterType, - defaultSettings.reportUnknownParameterType), + configObj.reportUnknownParameterType, + DiagnosticRule.reportUnknownParameterType, + defaultSettings.reportUnknownParameterType + ), // Read the "reportUnknownArgumentType" entry. reportUnknownArgumentType: this._convertDiagnosticLevel( - configObj.reportUnknownArgumentType, DiagnosticRule.reportUnknownArgumentType, - defaultSettings.reportUnknownArgumentType), + configObj.reportUnknownArgumentType, + DiagnosticRule.reportUnknownArgumentType, + defaultSettings.reportUnknownArgumentType + ), // Read the "reportUnknownLambdaType" entry. reportUnknownLambdaType: this._convertDiagnosticLevel( - configObj.reportUnknownLambdaType, DiagnosticRule.reportUnknownLambdaType, - defaultSettings.reportUnknownLambdaType), + configObj.reportUnknownLambdaType, + DiagnosticRule.reportUnknownLambdaType, + defaultSettings.reportUnknownLambdaType + ), // Read the "reportUnknownVariableType" entry. reportUnknownVariableType: this._convertDiagnosticLevel( - configObj.reportUnknownVariableType, DiagnosticRule.reportUnknownVariableType, - defaultSettings.reportUnknownVariableType), + configObj.reportUnknownVariableType, + DiagnosticRule.reportUnknownVariableType, + defaultSettings.reportUnknownVariableType + ), // Read the "reportUnknownMemberType" entry. reportUnknownMemberType: this._convertDiagnosticLevel( - configObj.reportUnknownMemberType, DiagnosticRule.reportUnknownMemberType, - defaultSettings.reportUnknownMemberType), + configObj.reportUnknownMemberType, + DiagnosticRule.reportUnknownMemberType, + defaultSettings.reportUnknownMemberType + ), // Read the "reportCallInDefaultInitializer" entry. reportCallInDefaultInitializer: this._convertDiagnosticLevel( - configObj.reportCallInDefaultInitializer, DiagnosticRule.reportCallInDefaultInitializer, - defaultSettings.reportCallInDefaultInitializer), + configObj.reportCallInDefaultInitializer, + DiagnosticRule.reportCallInDefaultInitializer, + defaultSettings.reportCallInDefaultInitializer + ), // Read the "reportUnnecessaryIsInstance" entry. reportUnnecessaryIsInstance: this._convertDiagnosticLevel( - configObj.reportUnnecessaryIsInstance, DiagnosticRule.reportUnnecessaryIsInstance, - defaultSettings.reportUnnecessaryIsInstance), + configObj.reportUnnecessaryIsInstance, + DiagnosticRule.reportUnnecessaryIsInstance, + defaultSettings.reportUnnecessaryIsInstance + ), // Read the "reportUnnecessaryCast" entry. reportUnnecessaryCast: this._convertDiagnosticLevel( - configObj.reportUnnecessaryCast, DiagnosticRule.reportUnnecessaryCast, - defaultSettings.reportUnnecessaryCast), + configObj.reportUnnecessaryCast, + DiagnosticRule.reportUnnecessaryCast, + defaultSettings.reportUnnecessaryCast + ), // Read the "reportAssertAlwaysTrue" entry. reportAssertAlwaysTrue: this._convertDiagnosticLevel( - configObj.reportAssertAlwaysTrue, DiagnosticRule.reportAssertAlwaysTrue, - defaultSettings.reportAssertAlwaysTrue), + configObj.reportAssertAlwaysTrue, + DiagnosticRule.reportAssertAlwaysTrue, + defaultSettings.reportAssertAlwaysTrue + ), // Read the "reportSelfClsParameterName" entry. reportSelfClsParameterName: this._convertDiagnosticLevel( - configObj.reportSelfClsParameterName, DiagnosticRule.reportSelfClsParameterName, - defaultSettings.reportSelfClsParameterName), + configObj.reportSelfClsParameterName, + DiagnosticRule.reportSelfClsParameterName, + defaultSettings.reportSelfClsParameterName + ), // Read the "reportImplicitStringConcatenation" entry. reportImplicitStringConcatenation: this._convertDiagnosticLevel( - configObj.reportImplicitStringConcatenation, DiagnosticRule.reportImplicitStringConcatenation, - defaultSettings.reportImplicitStringConcatenation) + configObj.reportImplicitStringConcatenation, + DiagnosticRule.reportImplicitStringConcatenation, + defaultSettings.reportImplicitStringConcatenation + ) }; // Read the "venvPath". @@ -736,9 +807,9 @@ export class ConfigOptions { if (typeof configObj.typeshedPath !== 'string') { console.log(`Config "typeshedPath" field must contain a string.`); } else { - this.typeshedPath = configObj.typeshedPath ? - normalizePath(combinePaths(this.projectRoot, configObj.typeshedPath)) : - ''; + this.typeshedPath = configObj.typeshedPath + ? normalizePath(combinePaths(this.projectRoot, configObj.typeshedPath)) + : ''; } } @@ -777,13 +848,11 @@ export class ConfigOptions { return value ? true : false; } - console.log(`Config "${ fieldName }" entry must be true or false.`); + console.log(`Config "${fieldName}" entry must be true or false.`); return defaultValue; } - private _convertDiagnosticLevel(value: any, fieldName: string, - defaultValue: DiagnosticLevel): DiagnosticLevel { - + private _convertDiagnosticLevel(value: any, fieldName: string, defaultValue: DiagnosticLevel): DiagnosticLevel { if (value === undefined) { return defaultValue; } else if (typeof value === 'boolean') { @@ -794,33 +863,41 @@ export class ConfigOptions { } } - console.log(`Config "${ fieldName }" entry must be true, false, "error", "warning" or "none".`); + console.log(`Config "${fieldName}" entry must be true, false, "error", "warning" or "none".`); return defaultValue; } - private _initExecutionEnvironmentFromJson(envObj: any, index: number, - console: ConsoleInterface): ExecutionEnvironment | undefined { + private _initExecutionEnvironmentFromJson( + envObj: any, + index: number, + console: ConsoleInterface + ): ExecutionEnvironment | undefined { try { - const newExecEnv = new ExecutionEnvironment(this.projectRoot, - this.defaultPythonVersion, this.defaultPythonPlatform); + const newExecEnv = new ExecutionEnvironment( + this.projectRoot, + this.defaultPythonVersion, + this.defaultPythonPlatform + ); // Validate the root. if (envObj.root && typeof envObj.root === 'string') { newExecEnv.root = normalizePath(combinePaths(this.projectRoot, envObj.root)); } else { - console.log(`Config executionEnvironments index ${ index }: missing root value.`); + console.log(`Config executionEnvironments index ${index}: missing root value.`); } // Validate the extraPaths. if (envObj.extraPaths) { if (!Array.isArray(envObj.extraPaths)) { - console.log(`Config executionEnvironments index ${ index }: extraPaths field must contain an array.`); + console.log(`Config executionEnvironments index ${index}: extraPaths field must contain an array.`); } else { const pathList = envObj.extraPaths as string[]; pathList.forEach((path, pathIndex) => { if (typeof path !== 'string') { - console.log(`Config executionEnvironments index ${ index }:` + - ` extraPaths field ${ pathIndex } must be a string.`); + console.log( + `Config executionEnvironments index ${index}:` + + ` extraPaths field ${pathIndex} must be a string.` + ); } else { newExecEnv.extraPaths.push(normalizePath(combinePaths(this.projectRoot, path))); } @@ -835,10 +912,10 @@ export class ConfigOptions { if (version) { newExecEnv.pythonVersion = version; } else { - console.log(`Config executionEnvironments index ${ index } contains unsupported pythonVersion.`); + console.log(`Config executionEnvironments index ${index} contains unsupported pythonVersion.`); } } else { - console.log(`Config executionEnvironments index ${ index } pythonVersion must be a string.`); + console.log(`Config executionEnvironments index ${index} pythonVersion must be a string.`); } } @@ -847,7 +924,7 @@ export class ConfigOptions { if (typeof envObj.pythonPlatform === 'string') { newExecEnv.pythonPlatform = envObj.pythonPlatform; } else { - console.log(`Config executionEnvironments index ${ index } pythonPlatform must be a string.`); + console.log(`Config executionEnvironments index ${index} pythonPlatform must be a string.`); } } @@ -856,13 +933,13 @@ export class ConfigOptions { if (typeof envObj.venv === 'string') { newExecEnv.venv = envObj.venv; } else { - console.log(`Config executionEnvironments index ${ index } venv must be a string.`); + console.log(`Config executionEnvironments index ${index} venv must be a string.`); } } return newExecEnv; } catch { - console.log(`Config executionEnvironments index ${ index } is not accessible.`); + console.log(`Config executionEnvironments index ${index} is not accessible.`); } return undefined; diff --git a/server/src/common/console.ts b/server/src/common/console.ts index d660df081..38c49075e 100644 --- a/server/src/common/console.ts +++ b/server/src/common/console.ts @@ -1,12 +1,12 @@ /* -* console.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Provides an abstraction for console logging and error-reporting -* methods. -*/ + * console.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Provides an abstraction for console logging and error-reporting + * methods. + */ export interface ConsoleInterface { log: (message: string) => void; diff --git a/server/src/common/core.ts b/server/src/common/core.ts index ec8573c99..de1bd9091 100644 --- a/server/src/common/core.ts +++ b/server/src/common/core.ts @@ -21,32 +21,48 @@ export const enum Comparison { export type AnyFunction = (...args: never[]) => void; /** Do nothing and return false */ -export function returnFalse(): false { return false; } +export function returnFalse(): false { + return false; +} /** Do nothing and return true */ -export function returnTrue(): true { return true; } +export function returnTrue(): true { + return true; +} /** Do nothing and return undefined */ -export function returnUndefined(): undefined { return undefined; } +export function returnUndefined(): undefined { + return undefined; +} /** Returns its argument. */ -export function identity(x: T) { return x; } +export function identity(x: T) { + return x; +} /** Returns lower case string */ -export function toLowerCase(x: string) { return x.toLowerCase(); } +export function toLowerCase(x: string) { + return x.toLowerCase(); +} -export function equateValues(a: T, b: T) { return a === b; } +export function equateValues(a: T, b: T) { + return a === b; +} export type GetCanonicalFileName = (fileName: string) => string; export function compareComparableValues(a: string | undefined, b: string | undefined): Comparison; export function compareComparableValues(a: number | undefined, b: number | undefined): Comparison; export function compareComparableValues(a: string | number | undefined, b: string | number | undefined) { - return a === b ? Comparison.EqualTo : - a === undefined ? Comparison.LessThan : - b === undefined ? Comparison.GreaterThan : - a < b ? Comparison.LessThan : - Comparison.GreaterThan; + return a === b + ? Comparison.EqualTo + : a === undefined + ? Comparison.LessThan + : b === undefined + ? Comparison.GreaterThan + : a < b + ? Comparison.LessThan + : Comparison.GreaterThan; } /** diff --git a/server/src/common/debug.ts b/server/src/common/debug.ts index 145981b08..b52bf117b 100644 --- a/server/src/common/debug.ts +++ b/server/src/common/debug.ts @@ -4,17 +4,22 @@ * Licensed under the MIT license. * * Various debug helper methods to show user friendly debugging info -*/ + */ import { stableSort } from './collectionUtils'; import { AnyFunction, compareValues, hasProperty } from './core'; -export function assert(expression: boolean, message?: string, - verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void { - +export function assert( + expression: boolean, + message?: string, + verboseDebugInfo?: string | (() => string), + stackCrawlMark?: AnyFunction +): void { if (!expression) { if (verboseDebugInfo) { - message += '\r\nVerbose Debug Information: ' + (typeof verboseDebugInfo === 'string' ? verboseDebugInfo : verboseDebugInfo()); + message += + '\r\nVerbose Debug Information: ' + + (typeof verboseDebugInfo === 'string' ? verboseDebugInfo : verboseDebugInfo()); } fail(message ? 'False expression: ' + message : 'False expression.', stackCrawlMark || assert); } @@ -22,7 +27,7 @@ export function assert(expression: boolean, message?: string, export function fail(message?: string, stackCrawlMark?: AnyFunction): never { // debugger; - const e = new Error(message ? `Debug Failure. ${ message }` : 'Debug Failure.'); + const e = new Error(message ? `Debug Failure. ${message}` : 'Debug Failure.'); if ((Error as any).captureStackTrace) { (Error as any).captureStackTrace(e, stackCrawlMark || fail); } @@ -30,7 +35,9 @@ export function fail(message?: string, stackCrawlMark?: AnyFunction): never { } export function assertDefined(value: T | null | undefined, message?: string): T { - if (value === undefined || value === null) { return fail(message); } + if (value === undefined || value === null) { + return fail(message); + } return value; } @@ -43,7 +50,7 @@ export function assertEachDefined(value: A, message?: export function assertNever(member: never, message = 'Illegal value:', stackCrawlMark?: AnyFunction): never { const detail = JSON.stringify(member); - return fail(`${ message } ${ detail }`, stackCrawlMark || assertNever); + return fail(`${message} ${detail}`, stackCrawlMark || assertNever); } export function getFunctionName(func: AnyFunction) { @@ -74,7 +81,7 @@ export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) { break; } if (enumValue !== 0 && enumValue & value) { - result = `${ result }${ result ? '|' : '' }${ enumName }`; + result = `${result}${result ? '|' : ''}${enumName}`; remainingFlags &= ~enumValue; } } diff --git a/server/src/common/diagnostic.ts b/server/src/common/diagnostic.ts index bebf17c3d..0615ba33d 100644 --- a/server/src/common/diagnostic.ts +++ b/server/src/common/diagnostic.ts @@ -1,11 +1,11 @@ /* -* diagnostics.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Class that represents errors and warnings. -*/ + * diagnostics.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Class that represents errors and warnings. + */ import { Commands } from '../commands/commands'; import { Range } from './textRange'; @@ -42,9 +42,7 @@ export class Diagnostic { private _rule: string | undefined; private _relatedInfo: DiagnosticRelatedInfo[] = []; - constructor(readonly category: DiagnosticCategory, readonly message: string, - readonly range: Range) { - } + constructor(readonly category: DiagnosticCategory, readonly message: string, readonly range: Range) {} addAction(action: DiagnosticAction) { if (this._actions === undefined) { diff --git a/server/src/common/diagnosticRules.ts b/server/src/common/diagnosticRules.ts index eefb7c827..c290544d3 100644 --- a/server/src/common/diagnosticRules.ts +++ b/server/src/common/diagnosticRules.ts @@ -1,12 +1,12 @@ /* -* diagnosticRules.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Strings that represent each of the diagnostic rules -* that can be enabled or disabled in the configuration. -*/ + * diagnosticRules.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Strings that represent each of the diagnostic rules + * that can be enabled or disabled in the configuration. + */ export const enum DiagnosticRule { strictListInference = 'strictListInference', diff --git a/server/src/common/diagnosticSink.ts b/server/src/common/diagnosticSink.ts index ba38fcb72..a1eaf9af6 100644 --- a/server/src/common/diagnosticSink.ts +++ b/server/src/common/diagnosticSink.ts @@ -1,15 +1,15 @@ /* -* diagnostics.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Class that represents errors and warnings. -*/ + * diagnostics.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Class that represents errors and warnings. + */ import { Diagnostic, DiagnosticCategory } from './diagnostic'; import { convertOffsetsToRange } from './positionUtils'; -import { Range,TextRange } from './textRange'; +import { Range, TextRange } from './textRange'; import { TextRangeCollection } from './textRangeCollection'; // Represents a collection of diagnostics within a file. diff --git a/server/src/common/editAction.ts b/server/src/common/editAction.ts index b1df8041e..e323f411a 100644 --- a/server/src/common/editAction.ts +++ b/server/src/common/editAction.ts @@ -1,13 +1,13 @@ /* -* editAction.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Represents a single edit within a file. -*/ + * editAction.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Represents a single edit within a file. + */ -import { Range } from "./textRange"; +import { Range } from './textRange'; export interface TextEditAction { range: Range; diff --git a/server/src/common/extensions.ts b/server/src/common/extensions.ts index 514ff27b6..8812e71cf 100644 --- a/server/src/common/extensions.ts +++ b/server/src/common/extensions.ts @@ -1,15 +1,15 @@ /* -* extensions.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Extension methods to various types. -*/ + * extensions.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Extension methods to various types. + */ /* eslint-disable @typescript-eslint/no-empty-function */ // Explicitly tells that promise should be run asynchronously. -Promise.prototype.ignoreErrors = function (this: Promise) { - this.catch(() => { }); +Promise.prototype.ignoreErrors = function(this: Promise) { + this.catch(() => {}); }; diff --git a/server/src/common/pathUtils.ts b/server/src/common/pathUtils.ts index a1cce7490..d842883e7 100644 --- a/server/src/common/pathUtils.ts +++ b/server/src/common/pathUtils.ts @@ -1,11 +1,11 @@ /* -* pathUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Pathname utility functions. -*/ + * pathUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Pathname utility functions. + */ import * as path from 'path'; import Char from 'typescript-char'; @@ -15,8 +15,11 @@ import { some } from './collectionUtils'; import { compareValues, Comparison, GetCanonicalFileName, identity } from './core'; import * as debug from './debug'; import { - compareStringsCaseInsensitive, compareStringsCaseSensitive, equateStringsCaseInsensitive, - equateStringsCaseSensitive, getStringComparer + compareStringsCaseInsensitive, + compareStringsCaseSensitive, + equateStringsCaseInsensitive, + equateStringsCaseSensitive, + getStringComparer } from './stringUtils'; import { VirtualFileSystem } from './vfs'; @@ -36,7 +39,10 @@ export interface FileSystemEntries { directories: string[]; } -export function forEachAncestorDirectory(directory: string, callback: (directory: string) => string | undefined): string | undefined { +export function forEachAncestorDirectory( + directory: string, + callback: (directory: string) => string | undefined +): string | undefined { while (true) { const result = callback(directory); if (result !== undefined) { @@ -58,15 +64,23 @@ export function getDirectoryPath(pathString: string): string { export function getRootLength(pathString: string): number { if (pathString.charAt(0) === path.sep) { - if (pathString.charAt(1) !== path.sep) { return 1; } + if (pathString.charAt(1) !== path.sep) { + return 1; + } const p1 = pathString.indexOf(path.sep, 2); - if (p1 < 0) { return 2; } + if (p1 < 0) { + return 2; + } const p2 = pathString.indexOf(path.sep, p1 + 1); - if (p2 < 0) { return p1 + 1; } + if (p2 < 0) { + return p1 + 1; + } return p2 + 1; } if (pathString.charAt(1) === ':') { - if (pathString.charAt(2) === path.sep) { return 3; } + if (pathString.charAt(2) === path.sep) { + return 3; + } } return 0; } @@ -84,7 +98,9 @@ export function getPathComponents(pathString: string) { } export function reducePathComponents(components: readonly string[]) { - if (!some(components)) { return []; } + if (!some(components)) { + return []; + } // Reduce the path components by eliminating // any '.' or '..'. @@ -112,7 +128,9 @@ export function reducePathComponents(components: readonly string[]) { } export function combinePathComponents(components: string[]): string { - if (components.length === 0) { return ''; } + if (components.length === 0) { + return ''; + } const root = components[0] && ensureTrailingDirectorySeparator(components[0]); return normalizeSlashes(root + components.slice(1).join(path.sep)); @@ -158,7 +176,9 @@ export function getFileSize(fs: VirtualFileSystem, path: string) { if (stat.isFile()) { return stat.size; } - } catch { /*ignore*/ } + } catch { + /*ignore*/ + } return 0; } @@ -242,8 +262,12 @@ export function containsPath(parent: string, child: string, currentDirectory?: s ignoreCase = currentDirectory; } - if (parent === undefined || child === undefined) { return false; } - if (parent === child) { return true; } + if (parent === undefined || child === undefined) { + return false; + } + if (parent === child) { + return true; + } const parentComponents = getPathComponents(parent); const childComponents = getPathComponents(child); @@ -281,10 +305,22 @@ export function changeAnyExtension(path: string, ext: string): string; * changeAnyExtension("/path/to/file.ext", ".js", [".ext", ".ts"]) === "/path/to/file.js" * ``` */ -export function changeAnyExtension(path: string, ext: string, extensions: string | readonly string[], ignoreCase: boolean): string; -export function changeAnyExtension(path: string, ext: string, extensions?: string | readonly string[], ignoreCase?: boolean): string { - const pathext = extensions !== undefined && ignoreCase !== undefined ? - getAnyExtensionFromPath(path, extensions, ignoreCase) : getAnyExtensionFromPath(path); +export function changeAnyExtension( + path: string, + ext: string, + extensions: string | readonly string[], + ignoreCase: boolean +): string; +export function changeAnyExtension( + path: string, + ext: string, + extensions?: string | readonly string[], + ignoreCase?: boolean +): string { + const pathext = + extensions !== undefined && ignoreCase !== undefined + ? getAnyExtensionFromPath(path, extensions, ignoreCase) + : getAnyExtensionFromPath(path); return pathext ? path.slice(0, path.length - pathext.length) + (ext.startsWith('.') ? ext : '.' + ext) : path; } @@ -309,13 +345,24 @@ export function getAnyExtensionFromPath(path: string): string; * getAnyExtensionFromPath("/path/to/file.js", [".ext", ".js"], true) === ".js" * getAnyExtensionFromPath("/path/to/file.ext", ".EXT", false) === "" */ -export function getAnyExtensionFromPath(path: string, extensions: string | readonly string[], ignoreCase: boolean): string; -export function getAnyExtensionFromPath(path: string, extensions?: string | readonly string[], ignoreCase?: boolean): string { +export function getAnyExtensionFromPath( + path: string, + extensions: string | readonly string[], + ignoreCase: boolean +): string; +export function getAnyExtensionFromPath( + path: string, + extensions?: string | readonly string[], + ignoreCase?: boolean +): string { // Retrieves any string from the final "." onwards from a base file name. // Unlike extensionFromPath, which throws an exception on unrecognized extensions. if (extensions) { - return getAnyExtensionFromPathWorker(stripTrailingDirectorySeparator(path), extensions, - ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive); + return getAnyExtensionFromPathWorker( + stripTrailingDirectorySeparator(path), + extensions, + ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive + ); } const baseFileName = getBaseFileName(path); const extensionIndex = baseFileName.lastIndexOf('.'); @@ -354,20 +401,28 @@ export function getBaseFileName(pathString: string): string; * getBaseFileName("/path/to/file.ext", ".EXT", false) === "file.ext" * ``` */ -export function getBaseFileName(pathString: string, extensions: string | readonly string[], ignoreCase: boolean): string; +export function getBaseFileName( + pathString: string, + extensions: string | readonly string[], + ignoreCase: boolean +): string; export function getBaseFileName(pathString: string, extensions?: string | readonly string[], ignoreCase?: boolean) { pathString = normalizeSlashes(pathString); // if the path provided is itself the root, then it has not file name. const rootLength = getRootLength(pathString); - if (rootLength === pathString.length) { return ''; } + if (rootLength === pathString.length) { + return ''; + } // return the trailing portion of the path starting after the last (non-terminal) directory // separator but not including any trailing directory separator. pathString = stripTrailingDirectorySeparator(pathString); const name = pathString.slice(Math.max(getRootLength(pathString), pathString.lastIndexOf(path.sep) + 1)); - const extension = extensions !== undefined && ignoreCase !== undefined ? - getAnyExtensionFromPath(name, extensions, ignoreCase) : undefined; + const extension = + extensions !== undefined && ignoreCase !== undefined + ? getAnyExtensionFromPath(name, extensions, ignoreCase) + : undefined; return extension ? name.slice(0, name.length - extension.length) : name; } @@ -379,15 +434,29 @@ export function getRelativePathFromDirectory(from: string, to: string, ignoreCas /** * Gets a relative path that can be used to traverse between `from` and `to`. */ -export function getRelativePathFromDirectory(fromDirectory: string, to: string, getCanonicalFileName: GetCanonicalFileName): string; -export function getRelativePathFromDirectory(fromDirectory: string, to: string, - getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean) { - - debug.assert((getRootLength(fromDirectory) > 0) === (getRootLength(to) > 0), 'Paths must either both be absolute or both be relative'); - const getCanonicalFileName = typeof getCanonicalFileNameOrIgnoreCase === 'function' ? getCanonicalFileNameOrIgnoreCase : identity; +export function getRelativePathFromDirectory( + fromDirectory: string, + to: string, + getCanonicalFileName: GetCanonicalFileName +): string; +export function getRelativePathFromDirectory( + fromDirectory: string, + to: string, + getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean +) { + debug.assert( + getRootLength(fromDirectory) > 0 === getRootLength(to) > 0, + 'Paths must either both be absolute or both be relative' + ); + const getCanonicalFileName = + typeof getCanonicalFileNameOrIgnoreCase === 'function' ? getCanonicalFileNameOrIgnoreCase : identity; const ignoreCase = typeof getCanonicalFileNameOrIgnoreCase === 'boolean' ? getCanonicalFileNameOrIgnoreCase : false; - const pathComponents = getPathComponentsRelativeTo(fromDirectory, to, ignoreCase ? - equateStringsCaseInsensitive : equateStringsCaseSensitive, getCanonicalFileName); + const pathComponents = getPathComponentsRelativeTo( + fromDirectory, + to, + ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive, + getCanonicalFileName + ); return combinePathComponents(pathComponents); } @@ -513,8 +582,8 @@ export function getWildcardRegexPattern(rootPath: string, fileSpec: string): str const pathComponents = getPathComponents(absolutePath); const escapedSeparator = getRegexEscapedSeparator(); - const doubleAsteriskRegexFragment = `(${ escapedSeparator }[^${ escapedSeparator }.][^${ escapedSeparator }]*)*?`; - const reservedCharacterPattern = new RegExp(`[^\\w\\s${ escapedSeparator }]`, 'g'); + const doubleAsteriskRegexFragment = `(${escapedSeparator}[^${escapedSeparator}.][^${escapedSeparator}]*)*?`; + const reservedCharacterPattern = new RegExp(`[^\\w\\s${escapedSeparator}]`, 'g'); // Strip the directory separator from the root component. if (pathComponents.length > 0) { @@ -532,17 +601,16 @@ export function getWildcardRegexPattern(rootPath: string, fileSpec: string): str component = escapedSeparator + component; } - regExPattern += component.replace( - reservedCharacterPattern, match => { - if (match === '*') { - return `[^${ escapedSeparator }]*`; - } else if (match === '?') { - return `[^${ escapedSeparator }]`; - } else { - // escaping anything that is not reserved characters - word/space/separator - return '\\' + match; - } - }); + regExPattern += component.replace(reservedCharacterPattern, match => { + if (match === '*') { + return `[^${escapedSeparator}]*`; + } else if (match === '?') { + return `[^${escapedSeparator}]`; + } else { + // escaping anything that is not reserved characters - word/space/separator + return '\\' + match; + } + }); firstComponent = false; } @@ -591,7 +659,7 @@ export function getWildcardRoot(rootPath: string, fileSpec: string): string { export function getFileSpec(rootPath: string, fileSpec: string): FileSpec { let regExPattern = getWildcardRegexPattern(rootPath, fileSpec); const escapedSeparator = getRegexEscapedSeparator(); - regExPattern = `^(${ regExPattern })($|${ escapedSeparator })`; + regExPattern = `^(${regExPattern})($|${escapedSeparator})`; const regExp = new RegExp(regExPattern); const wildcardRoot = getWildcardRoot(rootPath, fileSpec); @@ -625,9 +693,15 @@ export function isDiskPathRoot(path: string) { //// Path Comparisons function comparePathsWorker(a: string, b: string, componentComparer: (a: string, b: string) => Comparison) { - if (a === b) { return Comparison.EqualTo; } - if (a === undefined) { return Comparison.LessThan; } - if (b === undefined) { return Comparison.GreaterThan; } + if (a === b) { + return Comparison.EqualTo; + } + if (a === undefined) { + return Comparison.LessThan; + } + if (b === undefined) { + return Comparison.GreaterThan; + } // NOTE: Performance optimization - shortcut if the root segments differ as there would be no // need to perform path reduction. @@ -640,7 +714,7 @@ function comparePathsWorker(a: string, b: string, componentComparer: (a: string, // check path for these segments: '', '.'. '..' const escapedSeparator = getRegexEscapedSeparator(); - const relativePathSegmentRegExp = new RegExp(`(^|${ escapedSeparator }).{0,2}($|${ escapedSeparator })`); + const relativePathSegmentRegExp = new RegExp(`(^|${escapedSeparator}).{0,2}($|${escapedSeparator})`); // NOTE: Performance optimization - shortcut if there are no relative path segments in // the non-root portion of the path @@ -665,21 +739,31 @@ function comparePathsWorker(a: string, b: string, componentComparer: (a: string, return compareValues(aComponents.length, bComponents.length); } -function getAnyExtensionFromPathWorker(path: string, extensions: string | readonly string[], - stringEqualityComparer: (a: string, b: string) => boolean) { - +function getAnyExtensionFromPathWorker( + path: string, + extensions: string | readonly string[], + stringEqualityComparer: (a: string, b: string) => boolean +) { if (typeof extensions === 'string') { return tryGetExtensionFromPath(path, extensions, stringEqualityComparer) || ''; } for (const extension of extensions) { const result = tryGetExtensionFromPath(path, extension, stringEqualityComparer); - if (result) { return result; } + if (result) { + return result; + } } return ''; } -function tryGetExtensionFromPath(path: string, extension: string, stringEqualityComparer: (a: string, b: string) => boolean) { - if (!extension.startsWith('.')) { extension = '.' + extension; } +function tryGetExtensionFromPath( + path: string, + extension: string, + stringEqualityComparer: (a: string, b: string) => boolean +) { + if (!extension.startsWith('.')) { + extension = '.' + extension; + } if (path.length >= extension.length && path.charCodeAt(path.length - extension.length) === Char.Period) { const pathExtension = path.slice(path.length - extension.length); if (stringEqualityComparer(pathExtension, extension)) { @@ -690,9 +774,12 @@ function tryGetExtensionFromPath(path: string, extension: string, stringEquality return undefined; } -function getPathComponentsRelativeTo(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean, - getCanonicalFileName: GetCanonicalFileName) { - +function getPathComponentsRelativeTo( + from: string, + to: string, + stringEqualityComparer: (a: string, b: string) => boolean, + getCanonicalFileName: GetCanonicalFileName +) { const fromComponents = getPathComponents(from); const toComponents = getPathComponents(to); @@ -701,7 +788,9 @@ function getPathComponentsRelativeTo(from: string, to: string, stringEqualityCom const fromComponent = getCanonicalFileName(fromComponents[start]); const toComponent = getCanonicalFileName(toComponents[start]); const comparer = start === 0 ? equateStringsCaseInsensitive : stringEqualityComparer; - if (!comparer(fromComponent, toComponent)) { break; } + if (!comparer(fromComponent, toComponent)) { + break; + } } if (start === 0) { @@ -725,9 +814,12 @@ function fileSystemEntryExists(fs: VirtualFileSystem, path: string, entryKind: F try { const stat = fs.statSync(path); switch (entryKind) { - case FileSystemEntryKind.File: return stat.isFile(); - case FileSystemEntryKind.Directory: return stat.isDirectory(); - default: return false; + case FileSystemEntryKind.File: + return stat.isFile(); + case FileSystemEntryKind.Directory: + return stat.isDirectory(); + default: + return false; } } catch (e) { return false; diff --git a/server/src/common/positionUtils.ts b/server/src/common/positionUtils.ts index 356dd01bd..44e94f40f 100644 --- a/server/src/common/positionUtils.ts +++ b/server/src/common/positionUtils.ts @@ -1,12 +1,12 @@ /* -* positionUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Utility routines for converting between file offsets and -* line/column positions. -*/ + * positionUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Utility routines for converting between file offsets and + * line/column positions. + */ import { assert } from './debug'; import { Position, Range, TextRange } from './textRange'; @@ -38,16 +38,18 @@ export function convertOffsetToPosition(offset: number, lines: TextRangeCollecti } // Translates a start/end file offset into a pair of line/column positions. -export function convertOffsetsToRange(startOffset: number, endOffset: number, - lines: TextRangeCollection): Range { +export function convertOffsetsToRange( + startOffset: number, + endOffset: number, + lines: TextRangeCollection +): Range { const start = convertOffsetToPosition(startOffset, lines); const end = convertOffsetToPosition(endOffset, lines); return { start, end }; } // Translates a position (line and col) into a file offset. -export function convertPositionToOffset(position: Position, - lines: TextRangeCollection): number | undefined { +export function convertPositionToOffset(position: Position, lines: TextRangeCollection): number | undefined { if (position.line >= lines.count) { return undefined; } diff --git a/server/src/common/pythonVersion.ts b/server/src/common/pythonVersion.ts index e27e6e19e..d8d5342c0 100644 --- a/server/src/common/pythonVersion.ts +++ b/server/src/common/pythonVersion.ts @@ -1,12 +1,12 @@ /* -* pythonLanguageVersion.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Types and functions that relate to the Python language version -* and features within them. -*/ + * pythonLanguageVersion.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Types and functions that relate to the Python language version + * and features within them. + */ export enum PythonVersion { // The order of this enumeration is significant. We assume @@ -31,9 +31,9 @@ export const latestStablePythonVersion = PythonVersion.V38; export const latestPythonVersion = PythonVersion.V38; export function versionToString(version: PythonVersion): string { - const majorVersion = (version >> 8) & 0xFF; - const minorVersion = version & 0xFF; - return `${ majorVersion }.${ minorVersion }`; + const majorVersion = (version >> 8) & 0xff; + const minorVersion = version & 0xff; + return `${majorVersion}.${minorVersion}`; } export function versionFromString(verString: string): PythonVersion | undefined { @@ -67,5 +67,5 @@ export function versionFromString(verString: string): PythonVersion | undefined } export function is3x(version: PythonVersion): boolean { - return (version >> 8) === 3; + return version >> 8 === 3; } diff --git a/server/src/common/textRange.ts b/server/src/common/textRange.ts index b5efba544..3471e35a9 100644 --- a/server/src/common/textRange.ts +++ b/server/src/common/textRange.ts @@ -1,11 +1,11 @@ /* -* textRange.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Specifies the range of text within a larger string. -*/ + * textRange.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Specifies the range of text within a larger string. + */ export interface TextRange { start: number; @@ -108,8 +108,7 @@ export function doRangesOverlap(a: Range, b: Range) { } export function doesRangeContain(range: Range, position: Position) { - return comparePositions(range.start, position) >= 0 && - comparePositions(range.end, position) <= 0; + return comparePositions(range.start, position) >= 0 && comparePositions(range.end, position) <= 0; } export function rangesAreEqual(a: Range, b: Range) { diff --git a/server/src/common/textRangeCollection.ts b/server/src/common/textRangeCollection.ts index cbcf65037..a493cdec7 100644 --- a/server/src/common/textRangeCollection.ts +++ b/server/src/common/textRangeCollection.ts @@ -1,15 +1,15 @@ /* -* textRangeCollection.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Based on code from vscode-python repository: -* https://github.com/Microsoft/vscode-python -* -* Class that maintains an ordered list of text ranges and allows -* for indexing and fast lookups within this list. -*/ + * textRangeCollection.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Based on code from vscode-python repository: + * https://github.com/Microsoft/vscode-python + * + * Class that maintains an ordered list of text ranges and allows + * for indexing and fast lookups within this list. + */ import { TextRange } from './textRange'; @@ -107,9 +107,7 @@ export class TextRangeCollection { return mid; } - if (mid < this.count - 1 && TextRange.getEnd(item) <= position && - position < this._items[mid + 1].start) { - + if (mid < this.count - 1 && TextRange.getEnd(item) <= position && position < this._items[mid + 1].start) { return -1; } diff --git a/server/src/common/timing.ts b/server/src/common/timing.ts index e2ee9bd9e..a1955163c 100644 --- a/server/src/common/timing.ts +++ b/server/src/common/timing.ts @@ -1,12 +1,12 @@ /* -* timing.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* A simple duration class that can be used to record and report -* durations at the millisecond level of resolution. -*/ + * timing.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * A simple duration class that can be used to record and report + * durations at the millisecond level of resolution. + */ import { ConsoleInterface } from './console'; @@ -75,7 +75,7 @@ export class TimingStats { typeCheckerTime = new TimingStat(); printSummary(console: ConsoleInterface) { - console.log(`Completed in ${ this.totalDuration.getDurationInSeconds() }sec`); + console.log(`Completed in ${this.totalDuration.getDurationInSeconds()}sec`); } printDetails(console: ConsoleInterface) { diff --git a/server/src/common/vfs.ts b/server/src/common/vfs.ts index 68f38ad30..fcca2f66d 100644 --- a/server/src/common/vfs.ts +++ b/server/src/common/vfs.ts @@ -15,7 +15,11 @@ import * as fs from 'fs'; import { ConsoleInterface, NullConsole } from './console'; -export type Listener = (eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', path: string, stats?: Stats) => void; +export type Listener = ( + eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', + path: string, + stats?: Stats +) => void; export interface FileWatcher { close(): void; @@ -61,21 +65,38 @@ const _isMacintosh = process.platform === 'darwin'; const _isLinux = process.platform === 'linux'; class FileSystem implements VirtualFileSystem { - constructor(private _console: ConsoleInterface) { - } + constructor(private _console: ConsoleInterface) {} - existsSync(path: string) { return fs.existsSync(path); } - mkdirSync(path: string) { fs.mkdirSync(path); } - chdir(path: string) { process.chdir(path); } - readdirSync(path: string) { return fs.readdirSync(path); } + existsSync(path: string) { + return fs.existsSync(path); + } + mkdirSync(path: string) { + fs.mkdirSync(path); + } + chdir(path: string) { + process.chdir(path); + } + readdirSync(path: string) { + return fs.readdirSync(path); + } readFileSync(path: string, encoding?: null): Buffer; readFileSync(path: string, encoding: string): string; readFileSync(path: string, encoding?: string | null): Buffer | string; - readFileSync(path: string, encoding: string | null = null) { return fs.readFileSync(path, { encoding }); } - writeFileSync(path: string, data: string | Buffer, encoding: string | null) { fs.writeFileSync(path, data, { encoding }); } - statSync(path: string) { return fs.statSync(path); } - unlinkSync(path: string) { fs.unlinkSync(path); } - realpathSync(path: string) { return fs.realpathSync(path); } + readFileSync(path: string, encoding: string | null = null) { + return fs.readFileSync(path, { encoding }); + } + writeFileSync(path: string, data: string | Buffer, encoding: string | null) { + fs.writeFileSync(path, data, { encoding }); + } + statSync(path: string) { + return fs.statSync(path); + } + unlinkSync(path: string) { + fs.unlinkSync(path); + } + realpathSync(path: string) { + return fs.realpathSync(path); + } getModulePath(): string { // The entry point to the tool should have set the __rootDirectory diff --git a/server/src/index.d.ts b/server/src/index.d.ts index a778f3877..f58b1e51d 100644 --- a/server/src/index.d.ts +++ b/server/src/index.d.ts @@ -1,13 +1,13 @@ /* -* index.d.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Global definitions of extension interfaces. -*/ + * index.d.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Global definitions of extension interfaces. + */ declare interface Promise { - // Catches task error and ignores them. - ignoreErrors(): void; + // Catches task error and ignores them. + ignoreErrors(): void; } diff --git a/server/src/languageServerBase.ts b/server/src/languageServerBase.ts index f3371ec6c..2190d0690 100644 --- a/server/src/languageServerBase.ts +++ b/server/src/languageServerBase.ts @@ -1,17 +1,36 @@ /* -* languageServerBase.ts -* -* Implements common language server functionality. -*/ + * languageServerBase.ts + * + * Implements common language server functionality. + */ import './common/extensions'; import { - CodeAction, CodeActionKind, CodeActionParams, Command, - ConfigurationItem, createConnection, Diagnostic, DiagnosticRelatedInformation, - DiagnosticSeverity, DiagnosticTag, DocumentSymbol, ExecuteCommandParams, IConnection, - InitializeResult, Location, MarkupKind, ParameterInformation, RemoteConsole, RemoteWindow, SignatureInformation, - SymbolInformation, TextDocuments, TextEdit, WorkspaceEdit + CodeAction, + CodeActionKind, + CodeActionParams, + Command, + ConfigurationItem, + createConnection, + Diagnostic, + DiagnosticRelatedInformation, + DiagnosticSeverity, + DiagnosticTag, + DocumentSymbol, + ExecuteCommandParams, + IConnection, + InitializeResult, + Location, + MarkupKind, + ParameterInformation, + RemoteConsole, + RemoteWindow, + SignatureInformation, + SymbolInformation, + TextDocuments, + TextEdit, + WorkspaceEdit } from 'vscode-languageserver'; import { ImportResolver } from './analyzer/importResolver'; @@ -79,12 +98,12 @@ export abstract class LanguageServerBase implements LanguageServerInterface { fs: VirtualFileSystem; constructor(private _productName: string, rootDirectory: string) { - this._connection.console.log(`${ _productName } language server starting`); + this._connection.console.log(`${_productName} language server starting`); // virtual file system to be used. initialized to real file system by default. but can't be overritten this.fs = createFromRealFileSystem(this._connection.console); // Stash the base directory into a global variable. (global as any).__rootDirectory = rootDirectory; - this._connection.console.log(`Server root directory: ${ rootDirectory }`); + this._connection.console.log(`Server root directory: ${rootDirectory}`); // Create workspace map. this._workspaceMap = new WorkspaceMap(this); @@ -98,7 +117,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface { } protected abstract async executeCommand(cmdParams: ExecuteCommandParams): Promise; - protected abstract async executeCodeAction(cmdParams: CodeActionParams): Promise<(Command | CodeAction)[] | undefined | null>; + protected abstract async executeCodeAction( + cmdParams: CodeActionParams + ): Promise<(Command | CodeAction)[] | undefined | null>; abstract async getSettings(workspace: WorkspaceServiceInstance): Promise; protected getConfiguration(workspace: WorkspaceServiceInstance, section: string) { @@ -127,7 +148,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { // Creates a service instance that's used for analyzing a // program within a workspace. createAnalyzerService(name: string): AnalyzerService { - this._connection.console.log(`Starting service instance "${ name }"`); + this._connection.console.log(`Starting service instance "${name}"`); const service = new AnalyzerService(name, this.fs, this._connection.console, this.createImportResolver); // Don't allow the analysis engine to go too long without @@ -156,8 +177,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface { } const fileOrFiles = results.filesRequiringAnalysis !== 1 ? 'files' : 'file'; - this._connection.sendNotification('pyright/reportProgress', - `${ results.filesRequiringAnalysis } ${ fileOrFiles } to analyze`); + this._connection.sendNotification( + 'pyright/reportProgress', + `${results.filesRequiringAnalysis} ${fileOrFiles} to analyze` + ); } } else { if (this._isDisplayingProgress) { @@ -184,58 +207,57 @@ export abstract class LanguageServerBase implements LanguageServerInterface { private _setupConnection(): void { // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. - this._connection.onInitialize((params): InitializeResult => { - this.rootPath = params.rootPath || ''; + this._connection.onInitialize( + (params): InitializeResult => { + this.rootPath = params.rootPath || ''; - // Create a service instance for each of the workspace folders. - if (params.workspaceFolders) { - params.workspaceFolders.forEach(folder => { - const path = convertUriToPath(folder.uri); - this._workspaceMap.set(path, { - workspaceName: folder.name, - rootPath: path, - rootUri: folder.uri, - serviceInstance: this.createAnalyzerService(folder.name), + // Create a service instance for each of the workspace folders. + if (params.workspaceFolders) { + params.workspaceFolders.forEach(folder => { + const path = convertUriToPath(folder.uri); + this._workspaceMap.set(path, { + workspaceName: folder.name, + rootPath: path, + rootUri: folder.uri, + serviceInstance: this.createAnalyzerService(folder.name), + disableLanguageServices: false + }); + }); + } else if (params.rootPath) { + this._workspaceMap.set(params.rootPath, { + workspaceName: '', + rootPath: params.rootPath, + rootUri: '', + serviceInstance: this.createAnalyzerService(params.rootPath), disableLanguageServices: false }); - }); - } else if (params.rootPath) { - this._workspaceMap.set(params.rootPath, { - workspaceName: '', - rootPath: params.rootPath, - rootUri: '', - serviceInstance: this.createAnalyzerService(params.rootPath), - disableLanguageServices: false - }); - } - - return { - capabilities: { - // Tell the client that the server works in FULL text document - // sync mode (as opposed to incremental). - textDocumentSync: this._documents.syncKind, - definitionProvider: true, - referencesProvider: true, - documentSymbolProvider: true, - workspaceSymbolProvider: true, - hoverProvider: true, - renameProvider: true, - completionProvider: { - triggerCharacters: ['.', '['], - resolveProvider: true - }, - signatureHelpProvider: { - triggerCharacters: ['(', ',', ')'] - }, - codeActionProvider: { - codeActionKinds: [ - CodeActionKind.QuickFix, - CodeActionKind.SourceOrganizeImports - ] - } } - }; - }); + + return { + capabilities: { + // Tell the client that the server works in FULL text document + // sync mode (as opposed to incremental). + textDocumentSync: this._documents.syncKind, + definitionProvider: true, + referencesProvider: true, + documentSymbolProvider: true, + workspaceSymbolProvider: true, + hoverProvider: true, + renameProvider: true, + completionProvider: { + triggerCharacters: ['.', '['], + resolveProvider: true + }, + signatureHelpProvider: { + triggerCharacters: ['(', ',', ')'] + }, + codeActionProvider: { + codeActionKinds: [CodeActionKind.QuickFix, CodeActionKind.SourceOrganizeImports] + } + } + }; + } + ); this._connection.onDidChangeConfiguration(_ => { this._connection.console.log(`Received updated settings`); @@ -262,8 +284,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { if (!locations) { return undefined; } - return locations.map(loc => - Location.create(convertPathToUri(loc.path), loc.range)); + return locations.map(loc => Location.create(convertPathToUri(loc.path), loc.range)); }); this._connection.onReferences(params => { @@ -278,13 +299,15 @@ export abstract class LanguageServerBase implements LanguageServerInterface { if (workspace.disableLanguageServices) { return; } - const locations = workspace.serviceInstance.getReferencesForPosition(filePath, position, - params.context.includeDeclaration); + const locations = workspace.serviceInstance.getReferencesForPosition( + filePath, + position, + params.context.includeDeclaration + ); if (!locations) { return undefined; } - return locations.map(loc => - Location.create(convertPathToUri(loc.path), loc.range)); + return locations.map(loc => Location.create(convertPathToUri(loc.path), loc.range)); }); this._connection.onDocumentSymbol(params => { @@ -307,8 +330,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { this._workspaceMap.forEach(workspace => { if (!workspace.disableLanguageServices) { - workspace.serviceInstance.addSymbolsForWorkspace( - symbolList, params.query); + workspace.serviceInstance.addSymbolsForWorkspace(symbolList, params.query); } }); @@ -329,12 +351,14 @@ export abstract class LanguageServerBase implements LanguageServerInterface { return undefined; } - const markupString = hoverResults.parts.map(part => { - if (part.python) { - return '```python\n' + part.text + '\n```\n'; - } - return part.text; - }).join(''); + const markupString = hoverResults.parts + .map(part => { + if (part.python) { + return '```python\n' + part.text + '\n```\n'; + } + return part.text; + }) + .join(''); return { contents: { @@ -357,8 +381,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { if (workspace.disableLanguageServices) { return; } - const signatureHelpResults = workspace.serviceInstance.getSignatureHelpForPosition( - filePath, position); + const signatureHelpResults = workspace.serviceInstance.getSignatureHelpForPosition(filePath, position); if (!signatureHelpResults) { return undefined; } @@ -369,16 +392,17 @@ export abstract class LanguageServerBase implements LanguageServerInterface { if (sig.parameters) { paramInfo = sig.parameters.map(param => { return ParameterInformation.create( - [param.startOffset, param.endOffset], param.documentation); + [param.startOffset, param.endOffset], + param.documentation + ); }); } - return SignatureInformation.create(sig.label, sig.documentation, - ...paramInfo); + return SignatureInformation.create(sig.label, sig.documentation, ...paramInfo); }), - activeSignature: signatureHelpResults.activeSignature !== undefined ? - signatureHelpResults.activeSignature : null, - activeParameter: signatureHelpResults.activeParameter !== undefined ? - signatureHelpResults.activeParameter : null + activeSignature: + signatureHelpResults.activeSignature !== undefined ? signatureHelpResults.activeSignature : null, + activeParameter: + signatureHelpResults.activeParameter !== undefined ? signatureHelpResults.activeParameter : null }; }); @@ -396,7 +420,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface { } const completions = workspace.serviceInstance.getCompletionsForPosition( - filePath, position, workspace.rootPath); + filePath, + position, + workspace.rootPath + ); // Always mark as incomplete so we get called back when the // user continues typing. Without this, the editor will assume @@ -414,8 +441,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { if (completionItemData) { const workspace = this._workspaceMap.get(completionItemData.workspacePath); if (workspace && completionItemData.filePath) { - workspace.serviceInstance.resolveCompletionItem( - completionItemData.filePath, params); + workspace.serviceInstance.resolveCompletionItem(completionItemData.filePath, params); } } return params; @@ -433,8 +459,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { if (workspace.disableLanguageServices) { return; } - const editActions = workspace.serviceInstance.renameSymbolAtPosition( - filePath, position, params.newName); + const editActions = workspace.serviceInstance.renameSymbolAtPosition(filePath, position, params.newName); if (!editActions) { return undefined; @@ -462,10 +487,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { this._connection.onDidOpenTextDocument(params => { const filePath = convertUriToPath(params.textDocument.uri); const service = this._workspaceMap.getWorkspaceForFile(filePath).serviceInstance; - service.setFileOpened( - filePath, - params.textDocument.version, - params.textDocument.text); + service.setFileOpened(filePath, params.textDocument.version, params.textDocument.text); }); this._connection.onDidChangeTextDocument(params => { @@ -473,10 +495,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface { const filePath = convertUriToPath(params.textDocument.uri); const service = this._workspaceMap.getWorkspaceForFile(filePath).serviceInstance; - service.updateOpenFileContents( - filePath, - params.textDocument.version, - params.contentChanges[0].text); + service.updateOpenFileContents(filePath, params.textDocument.version, params.contentChanges[0].text); }); this._connection.onDidCloseTextDocument(params => { @@ -522,25 +541,26 @@ export abstract class LanguageServerBase implements LanguageServerInterface { workspace.disableLanguageServices = !!serverSettings.disableLanguageServices; } - updateOptionsAndRestartService(workspace: WorkspaceServiceInstance, - serverSettings: ServerSettings, typeStubTargetImportName?: string) { - + updateOptionsAndRestartService( + workspace: WorkspaceServiceInstance, + serverSettings: ServerSettings, + typeStubTargetImportName?: string + ) { AnalyzerServiceExecutor.runWithOptions(this.rootPath, workspace, serverSettings, typeStubTargetImportName); } private _convertDiagnostics(diags: AnalyzerDiagnostic[]): Diagnostic[] { return diags.map(diag => { - const severity = diag.category === DiagnosticCategory.Error ? - DiagnosticSeverity.Error : DiagnosticSeverity.Warning; + const severity = + diag.category === DiagnosticCategory.Error ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; let source = this._productName; const rule = diag.getRule(); if (rule) { - source = `${ source } (${ rule })`; + source = `${source} (${rule})`; } - const vsDiag = Diagnostic.create(diag.range, diag.message, severity, - undefined, source); + const vsDiag = Diagnostic.create(diag.range, diag.message, severity, undefined, source); if (diag.category === DiagnosticCategory.UnusedCode) { vsDiag.tags = [DiagnosticTag.Unnecessary]; diff --git a/server/src/languageService/analyzerServiceExecutor.ts b/server/src/languageService/analyzerServiceExecutor.ts index 0cbcfdee1..0db6e005c 100644 --- a/server/src/languageService/analyzerServiceExecutor.ts +++ b/server/src/languageService/analyzerServiceExecutor.ts @@ -12,20 +12,30 @@ import { combinePaths, normalizePath } from '../common/pathUtils'; import { ServerSettings, WorkspaceServiceInstance } from '../languageServerBase'; export class AnalyzerServiceExecutor { - static runWithOptions(languageServiceRootPath: string, workspace: WorkspaceServiceInstance, - serverSettings: ServerSettings, typeStubTargetImportName?: string): void { - - const commandLineOptions = GetCommandLineOptions(languageServiceRootPath, workspace.rootPath, - serverSettings, typeStubTargetImportName); + static runWithOptions( + languageServiceRootPath: string, + workspace: WorkspaceServiceInstance, + serverSettings: ServerSettings, + typeStubTargetImportName?: string + ): void { + const commandLineOptions = GetCommandLineOptions( + languageServiceRootPath, + workspace.rootPath, + serverSettings, + typeStubTargetImportName + ); // setting option cause analyzer service to re-analyze everything workspace.serviceInstance.setOptions(commandLineOptions); } } -function GetCommandLineOptions(languageServiceRootPath: string, workspaceRootPath: string, - serverSettings: ServerSettings, typeStubTargetImportName?: string) { - +function GetCommandLineOptions( + languageServiceRootPath: string, + workspaceRootPath: string, + serverSettings: ServerSettings, + typeStubTargetImportName?: string +) { const commandLineOptions = new CommandLineOptions(workspaceRootPath, true); commandLineOptions.checkOnlyOpenFiles = serverSettings.openFilesOnly; commandLineOptions.useLibraryCodeForTypes = serverSettings.useLibraryCodeForTypes; @@ -37,8 +47,10 @@ function GetCommandLineOptions(languageServiceRootPath: string, workspaceRootPat commandLineOptions.watch = !commandLineOptions.checkOnlyOpenFiles; if (serverSettings.venvPath) { - commandLineOptions.venvPath = combinePaths(workspaceRootPath || languageServiceRootPath, - normalizePath(_expandPathVariables(languageServiceRootPath, serverSettings.venvPath))); + commandLineOptions.venvPath = combinePaths( + workspaceRootPath || languageServiceRootPath, + normalizePath(_expandPathVariables(languageServiceRootPath, serverSettings.venvPath)) + ); } if (serverSettings.pythonPath) { @@ -46,8 +58,10 @@ function GetCommandLineOptions(languageServiceRootPath: string, workspaceRootPat // the local python interpreter should be used rather than interpreting the // setting value as a path to the interpreter. We'll simply ignore it in this case. if (serverSettings.pythonPath.trim() !== 'python') { - commandLineOptions.pythonPath = combinePaths(workspaceRootPath || languageServiceRootPath, - normalizePath(_expandPathVariables(languageServiceRootPath, serverSettings.pythonPath))); + commandLineOptions.pythonPath = combinePaths( + workspaceRootPath || languageServiceRootPath, + normalizePath(_expandPathVariables(languageServiceRootPath, serverSettings.pythonPath)) + ); } } diff --git a/server/src/languageService/codeActionProvider.ts b/server/src/languageService/codeActionProvider.ts index 31772e4d2..5f9ce569b 100644 --- a/server/src/languageService/codeActionProvider.ts +++ b/server/src/languageService/codeActionProvider.ts @@ -14,8 +14,10 @@ import { WorkspaceServiceInstance } from '../languageServerBase'; export class CodeActionProvider { static getCodeActionsForPosition(workspace: WorkspaceServiceInstance, filePath: string, range: Range) { const sortImportsCodeAction = CodeAction.create( - 'Organize Imports', Command.create('Organize Imports', Commands.orderImports), - CodeActionKind.SourceOrganizeImports); + 'Organize Imports', + Command.create('Organize Imports', Commands.orderImports), + CodeActionKind.SourceOrganizeImports + ); const codeActions: CodeAction[] = [sortImportsCodeAction]; if (!workspace.disableLanguageServices) { @@ -26,14 +28,21 @@ export class CodeActionProvider { }); if (typeStubDiag) { - const action = typeStubDiag.getActions()!.find( - a => a.action === Commands.createTypeStub) as CreateTypeStubFileAction; + const action = typeStubDiag + .getActions()! + .find(a => a.action === Commands.createTypeStub) as CreateTypeStubFileAction; if (action) { const createTypeStubAction = CodeAction.create( - `Create Type Stub For ‘${ action.moduleName }’`, - Command.create('Create Type Stub', Commands.createTypeStub, - workspace.rootPath, action.moduleName, filePath), - CodeActionKind.QuickFix); + `Create Type Stub For '${action.moduleName}'`, + Command.create( + 'Create Type Stub', + Commands.createTypeStub, + workspace.rootPath, + action.moduleName, + filePath + ), + CodeActionKind.QuickFix + ); codeActions.push(createTypeStubAction); } } @@ -44,14 +53,19 @@ export class CodeActionProvider { }); if (addOptionalDiag) { - const action = addOptionalDiag.getActions()!.find( - a => a.action === Commands.addMissingOptionalToParam) as AddMissingOptionalToParamAction; + const action = addOptionalDiag + .getActions()! + .find(a => a.action === Commands.addMissingOptionalToParam) as AddMissingOptionalToParamAction; if (action) { const addMissingOptionalAction = CodeAction.create( `Add 'Optional' to type annotation`, - Command.create(`Add 'Optional' to type annotation`, Commands.addMissingOptionalToParam, - action.offsetOfTypeNode), - CodeActionKind.QuickFix); + Command.create( + `Add 'Optional' to type annotation`, + Commands.addMissingOptionalToParam, + action.offsetOfTypeNode + ), + CodeActionKind.QuickFix + ); codeActions.push(addMissingOptionalAction); } } diff --git a/server/src/languageService/completionProvider.ts b/server/src/languageService/completionProvider.ts index 1d0ca2395..7f7c126a7 100644 --- a/server/src/languageService/completionProvider.ts +++ b/server/src/languageService/completionProvider.ts @@ -1,15 +1,14 @@ /* -* completionProvider.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic that maps a position within a Python program file into -* a list of zero or more text completions that apply in the context. -*/ + * completionProvider.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic that maps a position within a Python program file into + * a list of zero or more text completions that apply in the context. + */ -import { CompletionItem, CompletionItemKind, CompletionList, - MarkupKind, Range, TextEdit } from 'vscode-languageserver'; +import { CompletionItem, CompletionItemKind, CompletionList, MarkupKind, Range, TextEdit } from 'vscode-languageserver'; import { ImportLookup } from '../analyzer/analyzerFileInfo'; import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo'; @@ -22,8 +21,8 @@ import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; import { Symbol, SymbolTable } from '../analyzer/symbol'; import * as SymbolNameUtils from '../analyzer/symbolNameUtils'; import { getLastTypedDeclaredForSymbol } from '../analyzer/symbolUtils'; -import { CallSignatureInfo,TypeEvaluator } from '../analyzer/typeEvaluator'; -import { ClassType, FunctionType, Type,TypeCategory } from '../analyzer/types'; +import { CallSignatureInfo, TypeEvaluator } from '../analyzer/typeEvaluator'; +import { ClassType, FunctionType, Type, TypeCategory } from '../analyzer/types'; import { doForSubtypes, getMembersForClass, getMembersForModule } from '../analyzer/typeUtils'; import { ConfigOptions } from '../common/configOptions'; import { TextEditAction } from '../common/editAction'; @@ -32,9 +31,20 @@ import { convertOffsetToPosition, convertPositionToOffset } from '../common/posi import * as StringUtils from '../common/stringUtils'; import { comparePositions, Position } from '../common/textRange'; import { TextRange } from '../common/textRange'; -import { ErrorExpressionCategory, ErrorNode, ExpressionNode, - FunctionNode, ImportFromNode, isExpressionNode, ModuleNameNode, NameNode, - ParameterCategory, ParseNode, ParseNodeType, StringNode } from '../parser/parseNodes'; +import { + ErrorExpressionCategory, + ErrorNode, + ExpressionNode, + FunctionNode, + ImportFromNode, + isExpressionNode, + ModuleNameNode, + NameNode, + ParameterCategory, + ParseNode, + ParseNodeType, + StringNode +} from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; const _keywords: string[] = [ @@ -161,8 +171,8 @@ export class CompletionProvider { private _configOptions: ConfigOptions, private _importLookup: ImportLookup, private _evaluator: TypeEvaluator, - private _moduleSymbolsCallback: () => ModuleSymbolMap) { - } + private _moduleSymbolsCallback: () => ModuleSymbolMap + ) {} getCompletionsForPosition(): CompletionList | undefined { const offset = convertPositionToOffset(this._position, this._parseResults.tokenizerOutput.lines); @@ -244,8 +254,7 @@ export class CompletionProvider { } if (curNode.nodeType === ParseNodeType.Error) { - return this._getExpressionErrorCompletions(curNode, priorWord, - priorText, postText); + return this._getExpressionErrorCompletions(curNode, priorWord, priorText, postText); } if (curNode.nodeType === ParseNodeType.MemberAccess) { @@ -268,8 +277,7 @@ export class CompletionProvider { } } } else if (curNode.parent && curNode.parent.nodeType === ParseNodeType.MemberAccess) { - return this._getMemberAccessCompletions( - curNode.parent.leftExpression, priorWord); + return this._getMemberAccessCompletions(curNode.parent.leftExpression, priorWord); } } @@ -308,8 +316,8 @@ export class CompletionProvider { } const curIndex = CompletionProvider._mostRecentCompletions.findIndex( - item => item.label === label && - item.autoImportText === autoImportText); + item => item.label === label && item.autoImportText === autoImportText + ); if (curIndex > 0) { // If there's an existing entry with the same name that's not at the @@ -356,9 +364,12 @@ export class CompletionProvider { return !!priorText.match(/#/); } - private _getExpressionErrorCompletions(node: ErrorNode, priorWord: string, - priorText: string, postText: string): CompletionList | undefined { - + private _getExpressionErrorCompletions( + node: ErrorNode, + priorWord: string, + priorText: string, + postText: string + ): CompletionList | undefined { // Is the error due to a missing member access name? If so, // we can evaluate the left side of the member access expression // to determine its type and offer suggestions based on it. @@ -400,8 +411,7 @@ export class CompletionProvider { private _createSingleKeywordCompletionList(keyword: string): CompletionList { const completionItem = CompletionItem.create(keyword); completionItem.kind = CompletionItemKind.Keyword; - completionItem.sortText = - this._makeSortText(SortCategory.LikelyKeyword, keyword); + completionItem.sortText = this._makeSortText(SortCategory.LikelyKeyword, keyword); return CompletionList.create([completionItem]); } @@ -439,8 +449,7 @@ export class CompletionProvider { const methodSignature = this._printMethodSignature(decl.node) + ':'; const textEdit = TextEdit.replace(range, methodSignature); - this._addSymbol(name, symbol, partialName.value, completionList, - undefined, textEdit); + this._addSymbol(name, symbol, partialName.value, completionList, undefined, textEdit); } } }); @@ -449,24 +458,26 @@ export class CompletionProvider { } private _printMethodSignature(node: FunctionNode): string { - const paramList = node.parameters.map(param => { - let paramString = ''; - if (param.category === ParameterCategory.VarArgList) { - paramString += '*'; - } else if (param.category === ParameterCategory.VarArgDictionary) { - paramString += '**'; - } + const paramList = node.parameters + .map(param => { + let paramString = ''; + if (param.category === ParameterCategory.VarArgList) { + paramString += '*'; + } else if (param.category === ParameterCategory.VarArgDictionary) { + paramString += '**'; + } - if (param.name) { - paramString += param.name.value; - } + if (param.name) { + paramString += param.name.value; + } - if (param.typeAnnotation) { - paramString += ': ' + ParseTreeUtils.printExpression(param.typeAnnotation); - } + if (param.typeAnnotation) { + paramString += ': ' + ParseTreeUtils.printExpression(param.typeAnnotation); + } - return paramString; - }).join(', '); + return paramString; + }) + .join(', '); let methodSignature = node.name.value + '(' + paramList + ')'; @@ -477,9 +488,7 @@ export class CompletionProvider { return methodSignature; } - private _getMemberAccessCompletions(leftExprNode: ExpressionNode, - priorWord: string): CompletionList | undefined { - + private _getMemberAccessCompletions(leftExprNode: ExpressionNode, priorWord: string): CompletionList | undefined { const leftType = this._evaluator.getType(leftExprNode); const symbolTable = new Map(); @@ -498,23 +507,27 @@ export class CompletionProvider { } const completionList = CompletionList.create(); - this._addSymbolsForSymbolTable(symbolTable, _ => true, - priorWord, completionList); + this._addSymbolsForSymbolTable(symbolTable, _ => true, priorWord, completionList); return completionList; } - private _getStatementCompletions(parseNode: ParseNode, priorWord: string, - priorText: string, postText: string): CompletionList | undefined { - + private _getStatementCompletions( + parseNode: ParseNode, + priorWord: string, + priorText: string, + postText: string + ): CompletionList | undefined { // For now, use the same logic for expressions and statements. - return this._getExpressionCompletions(parseNode, priorWord, - priorText, postText); + return this._getExpressionCompletions(parseNode, priorWord, priorText, postText); } - private _getExpressionCompletions(parseNode: ParseNode, priorWord: string, - priorText: string, postText: string): CompletionList | undefined { - + private _getExpressionCompletions( + parseNode: ParseNode, + priorWord: string, + priorText: string, + postText: string + ): CompletionList | undefined { // If the user typed a "." as part of a number, don't present // any completion options. if (parseNode.nodeType === ParseNodeType.Number) { @@ -534,8 +547,7 @@ export class CompletionProvider { const completionItem = CompletionItem.create(keyword); completionItem.kind = CompletionItemKind.Keyword; completionList.items.push(completionItem); - completionItem.sortText = - this._makeSortText(SortCategory.Keyword, keyword); + completionItem.sortText = this._makeSortText(SortCategory.Keyword, keyword); }); // Add auto-import suggestions from other modules. @@ -551,11 +563,11 @@ export class CompletionProvider { } else if (parseNode.category === ErrorExpressionCategory.MissingExpression) { if (parseNode.parent && parseNode.parent.nodeType === ParseNodeType.Assignment) { const declaredTypeOfTarget = this._evaluator.getDeclaredTypeForExpression( - parseNode.parent.leftExpression); + parseNode.parent.leftExpression + ); if (declaredTypeOfTarget) { - this._addLiteralValuesForTargetType(declaredTypeOfTarget, - priorText, postText, completionList); + this._addLiteralValuesForTargetType(declaredTypeOfTarget, priorText, postText, completionList); } } } @@ -564,20 +576,23 @@ export class CompletionProvider { return completionList; } - private _addCallArgumentCompletions(parseNode: ParseNode, priorWord: string, priorText: string, - postText: string, completionList: CompletionList) { - + private _addCallArgumentCompletions( + parseNode: ParseNode, + priorWord: string, + priorText: string, + postText: string, + completionList: CompletionList + ) { // If we're within the argument list of a call, add parameter names. - const offset = convertPositionToOffset(this._position, - this._parseResults.tokenizerOutput.lines)!; - const signatureInfo = this._evaluator.getCallSignatureInfo(parseNode, offset) + const offset = convertPositionToOffset(this._position, this._parseResults.tokenizerOutput.lines)!; + const signatureInfo = this._evaluator.getCallSignatureInfo(parseNode, offset); if (signatureInfo) { // Are we past the call expression and within the argument list? const callNameEnd = convertOffsetToPosition( - signatureInfo.callNode.leftExpression.start + - signatureInfo.callNode.leftExpression.length, - this._parseResults.tokenizerOutput.lines); + signatureInfo.callNode.leftExpression.start + signatureInfo.callNode.leftExpression.length, + this._parseResults.tokenizerOutput.lines + ); if (comparePositions(this._position, callNameEnd) > 0) { this._addNamedParameters(signatureInfo, priorWord, completionList); @@ -588,15 +603,19 @@ export class CompletionProvider { } } - private _addLiteralValuesForArgument(signatureInfo: CallSignatureInfo, - priorText: string, postText: string, completionList: CompletionList) { - + private _addLiteralValuesForArgument( + signatureInfo: CallSignatureInfo, + priorText: string, + postText: string, + completionList: CompletionList + ) { signatureInfo.signatures.forEach(signature => { let paramIndex = -1; if (signatureInfo.activeArgumentName !== undefined) { paramIndex = signature.details.parameters.findIndex(param => { - param.name === signatureInfo.activeArgumentName}); + param.name === signatureInfo.activeArgumentName; + }); } else if (signatureInfo.activeArgumentIndex < signature.details.parameters.length) { paramIndex = signatureInfo.activeArgumentIndex; } @@ -611,17 +630,24 @@ export class CompletionProvider { }); } - private _addLiteralValuesForTargetType(type: Type, priorText: string, postText: string, - completionList: CompletionList) { - + private _addLiteralValuesForTargetType( + type: Type, + priorText: string, + postText: string, + completionList: CompletionList + ) { const quoteValue = this._getQuoteValueFromPriorText(priorText); doForSubtypes(type, subtype => { if (subtype.category === TypeCategory.Object) { if (ClassType.isBuiltIn(subtype.classType, 'str')) { if (subtype.literalValue !== undefined) { - this._addStringLiteralToCompletionList(subtype.literalValue as string, - quoteValue.stringValue, postText, quoteValue.quoteCharacter, - completionList); + this._addStringLiteralToCompletionList( + subtype.literalValue as string, + quoteValue.stringValue, + postText, + quoteValue.quoteCharacter, + completionList + ); } } } @@ -630,9 +656,12 @@ export class CompletionProvider { }); } - private _getStringLiteralCompletions(parseNode: StringNode, priorWord: string, - priorText: string, postText: string): CompletionList | undefined { - + private _getStringLiteralCompletions( + parseNode: StringNode, + priorWord: string, + priorText: string, + postText: string + ): CompletionList | undefined { let parentNode: ParseNode | undefined = parseNode.parent; if (!parentNode || parentNode.nodeType !== ParseNodeType.StringList || parentNode.strings.length > 1) { return undefined; @@ -655,7 +684,7 @@ export class CompletionProvider { if (!baseType || baseType.category !== TypeCategory.Object) { return undefined; } - + // We currently handle only TypedDict objects. const classType = baseType.classType; if (!ClassType.isTypedDictClass(classType)) { @@ -666,16 +695,19 @@ export class CompletionProvider { const quoteValue = this._getQuoteValueFromPriorText(priorText); entries.forEach((_, key) => { - this._addStringLiteralToCompletionList(key, quoteValue.stringValue, postText, - quoteValue.quoteCharacter, completionList); + this._addStringLiteralToCompletionList( + key, + quoteValue.stringValue, + postText, + quoteValue.quoteCharacter, + completionList + ); }); } else if (parentNode.nodeType === ParseNodeType.Assignment) { - const declaredTypeOfTarget = this._evaluator.getDeclaredTypeForExpression( - parentNode.leftExpression); + const declaredTypeOfTarget = this._evaluator.getDeclaredTypeForExpression(parentNode.leftExpression); if (declaredTypeOfTarget) { - this._addLiteralValuesForTargetType(declaredTypeOfTarget, - priorText, postText, completionList); + this._addLiteralValuesForTargetType(declaredTypeOfTarget, priorText, postText, completionList); } } else { this._addCallArgumentCompletions(parseNode, priorWord, priorText, postText, completionList); @@ -689,14 +721,14 @@ export class CompletionProvider { // (either starting with a single or double quote). Returns the quote // type and the string literal value after the starting quote. private _getQuoteValueFromPriorText(priorText: string) { - const lastSingleQuote = priorText.lastIndexOf('\''); + const lastSingleQuote = priorText.lastIndexOf("'"); const lastDoubleQuote = priorText.lastIndexOf('"'); let quoteCharacter = this._parseResults.tokenizerOutput.predominantSingleQuoteCharacter; let stringValue = undefined; if (lastSingleQuote > lastDoubleQuote) { - quoteCharacter = '\''; + quoteCharacter = "'"; stringValue = priorText.substr(lastSingleQuote + 1); } else if (lastDoubleQuote > lastSingleQuote) { quoteCharacter = '"'; @@ -729,18 +761,26 @@ export class CompletionProvider { const entries = this._evaluator.getTypedDictMembersForClass(classType); entries.forEach((_, key) => { - this._addStringLiteralToCompletionList(key, undefined, undefined, + this._addStringLiteralToCompletionList( + key, + undefined, + undefined, this._parseResults.tokenizerOutput.predominantSingleQuoteCharacter, - completionList); + completionList + ); }); } - private _addStringLiteralToCompletionList(value: string, priorString: string | undefined, - postText: string | undefined, quoteCharacter: string, completionList: CompletionList) { - + private _addStringLiteralToCompletionList( + value: string, + priorString: string | undefined, + postText: string | undefined, + quoteCharacter: string, + completionList: CompletionList + ) { const isSimilar = StringUtils.computeCompletionSimilarity(priorString || '', value) > similarityLimit; if (isSimilar) { - const valueWithQuotes = `${ quoteCharacter }${ value }${ quoteCharacter }`; + const valueWithQuotes = `${quoteCharacter}${value}${quoteCharacter}`; const completionItem = CompletionItem.create(valueWithQuotes); completionItem.kind = CompletionItemKind.Text; @@ -767,12 +807,11 @@ export class CompletionProvider { completionList.items.push(completionItem); } - } + } private _getAutoImportCompletions(priorWord: string, completionList: CompletionList) { const moduleSymbolMap = this._moduleSymbolsCallback(); - const importStatements = ImportStatementUtils.getTopLevelImports( - this._parseResults.parseTree); + const importStatements = ImportStatementUtils.getTopLevelImports(this._parseResults.parseTree); moduleSymbolMap.forEach((symbolTable, filePath) => { const fileName = stripFileExtension(getFileName(filePath)); @@ -784,9 +823,10 @@ export class CompletionProvider { // For very short matching strings, we will require an exact match. Otherwise // we will tend to return a list that's too long. Once we get beyond two // characters, we can do a fuzzy match. - const isSimilar = priorWord.length > 2 ? - StringUtils.computeCompletionSimilarity(priorWord, name) > similarityLimit : - priorWord.length > 0 && name.startsWith(priorWord); + const isSimilar = + priorWord.length > 2 + ? StringUtils.computeCompletionSimilarity(priorWord, name) > similarityLimit + : priorWord.length > 0 && name.startsWith(priorWord); if (isSimilar) { if (!symbol.isExternallyHidden()) { @@ -794,7 +834,8 @@ export class CompletionProvider { // this name, don't add an auto-import suggestion with // the same name. const localDuplicate = completionList.items.find( - item => item.label === name && !item.data.autoImport); + item => item.label === name && !item.data.autoImport + ); const declarations = symbol.getDeclarations(); if (declarations && declarations.length > 0 && localDuplicate === undefined) { // Don't include imported symbols, only those that @@ -812,11 +853,22 @@ export class CompletionProvider { } const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath( - name, importStatements, filePath, importSource, - moduleNameAndType ? moduleNameAndType.importType : ImportType.Local); + name, + importStatements, + filePath, + importSource, + moduleNameAndType ? moduleNameAndType.importType : ImportType.Local + ); - this._addSymbol(name, symbol, priorWord, - completionList, importSource, undefined, autoImportTextEdits); + this._addSymbol( + name, + symbol, + priorWord, + completionList, + importSource, + undefined, + autoImportTextEdits + ); } } } @@ -832,10 +884,9 @@ export class CompletionProvider { // file, we can use that directory name as an implicit import target. if (moduleSymbolMap.has(initPathPy) || moduleSymbolMap.has(initPathPyi)) { const name = getFileName(fileDir); - const moduleNameAndType = this._getModuleNameAndTypeFromFilePath( - getDirectoryPath(fileDir)); + const moduleNameAndType = this._getModuleNameAndTypeFromFilePath(getDirectoryPath(fileDir)); if (moduleNameAndType.moduleName) { - const autoImportText = `Auto-import from ${ moduleNameAndType.moduleName }`; + const autoImportText = `Auto-import from ${moduleNameAndType.moduleName}`; const isDuplicateEntry = completionList.items.find(item => { if (item.label === name) { @@ -855,10 +906,23 @@ export class CompletionProvider { if (!isDuplicateEntry) { const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath( - name, importStatements, filePath, moduleNameAndType.moduleName, - moduleNameAndType ? moduleNameAndType.importType : ImportType.Local); - this._addNameToCompletionList(name, CompletionItemKind.Module, priorWord, completionList, - name, '', autoImportText, undefined, autoImportTextEdits); + name, + importStatements, + filePath, + moduleNameAndType.moduleName, + moduleNameAndType ? moduleNameAndType.importType : ImportType.Local + ); + this._addNameToCompletionList( + name, + CompletionItemKind.Module, + priorWord, + completionList, + name, + '', + autoImportText, + undefined, + autoImportTextEdits + ); } } } @@ -871,28 +935,36 @@ export class CompletionProvider { // 'import from' statement. private _getModuleNameAndTypeFromFilePath(filePath: string): ModuleNameAndType { const execEnvironment = this._configOptions.findExecEnvironment(this._filePath); - return this._importResolver.getModuleNameForImport( - filePath, execEnvironment); + return this._importResolver.getModuleNameForImport(filePath, execEnvironment); } - private _getTextEditsForAutoImportByFilePath(symbolName: string, - importStatements: ImportStatementUtils.ImportStatements, filePath: string, - moduleName: string, importType: ImportType): TextEditAction[] { - + private _getTextEditsForAutoImportByFilePath( + symbolName: string, + importStatements: ImportStatementUtils.ImportStatements, + filePath: string, + moduleName: string, + importType: ImportType + ): TextEditAction[] { // Does an 'import from' statement already exist? If so, we'll reuse it. const importStatement = importStatements.mapByFilePath.get(filePath); if (importStatement && importStatement.node.nodeType === ParseNodeType.ImportFrom) { return ImportStatementUtils.getTextEditsForAutoImportSymbolAddition( - symbolName, importStatement, this._parseResults); + symbolName, + importStatement, + this._parseResults + ); } - return ImportStatementUtils.getTextEditsForAutoImportInsertion(symbolName, - importStatements, moduleName, importType, this._parseResults); + return ImportStatementUtils.getTextEditsForAutoImportInsertion( + symbolName, + importStatements, + moduleName, + importType, + this._parseResults + ); } - private _getImportFromCompletions(importFromNode: ImportFromNode, - priorWord: string): CompletionList | undefined { - + private _getImportFromCompletions(importFromNode: ImportFromNode, priorWord: string): CompletionList | undefined { // Don't attempt to provide completions for "from X import *". if (importFromNode.isWildcardImport) { return undefined; @@ -907,25 +979,26 @@ export class CompletionProvider { const completionList = CompletionList.create(); - const resolvedPath = importInfo.resolvedPaths.length > 0 ? - importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1] : ''; + const resolvedPath = + importInfo.resolvedPaths.length > 0 ? importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1] : ''; const lookupResults = this._importLookup(resolvedPath); if (lookupResults) { - this._addSymbolsForSymbolTable(lookupResults.symbolTable, + this._addSymbolsForSymbolTable( + lookupResults.symbolTable, name => { // Don't suggest symbols that have already been imported. - return !importFromNode.imports.find( - imp => imp.name.value === name); + return !importFromNode.imports.find(imp => imp.name.value === name); }, - priorWord, completionList); + priorWord, + completionList + ); } // Add the implicit imports. importInfo.implicitImports.forEach(implImport => { if (!importFromNode.imports.find(imp => imp.name.value === implImport.name)) { - this._addNameToCompletionList(implImport.name, CompletionItemKind.Module, - priorWord, completionList); + this._addNameToCompletionList(implImport.name, CompletionItemKind.Module, priorWord, completionList); } }); @@ -997,8 +1070,7 @@ export class CompletionProvider { let scope = AnalyzerNodeInfo.getScope(curNode); if (scope) { while (scope) { - this._addSymbolsForSymbolTable(scope.symbolTable, - () => true, priorWord, completionList); + this._addSymbolsForSymbolTable(scope.symbolTable, () => true, priorWord, completionList); scope = scope.parent; } break; @@ -1008,10 +1080,12 @@ export class CompletionProvider { } } - private _addSymbolsForSymbolTable(symbolTable: SymbolTable, - includeSymbolCallback: (name: string) => boolean, priorWord: string, - completionList: CompletionList) { - + private _addSymbolsForSymbolTable( + symbolTable: SymbolTable, + includeSymbolCallback: (name: string) => boolean, + priorWord: string, + completionList: CompletionList + ) { symbolTable.forEach((symbol, name) => { // If there are no declarations or the symbol is not // exported from this scope, don't include it in the @@ -1022,9 +1096,15 @@ export class CompletionProvider { }); } - private _addSymbol(name: string, symbol: Symbol, priorWord: string, completionList: CompletionList, - autoImportSource?: string, textEdit?: TextEdit, additionalTextEdits?: TextEditAction[]) { - + private _addSymbol( + name: string, + symbol: Symbol, + priorWord: string, + completionList: CompletionList, + autoImportSource?: string, + textEdit?: TextEdit, + additionalTextEdits?: TextEditAction[] + ) { let primaryDecl = getLastTypedDeclaredForSymbol(symbol); if (!primaryDecl) { const declarations = symbol.getDeclarations(); @@ -1063,8 +1143,9 @@ export class CompletionProvider { case DeclarationType.Function: if (type.category === TypeCategory.OverloadedFunction) { - typeDetail = type.overloads.map(overload => - name + this._evaluator.printType(overload)).join('\n'); + typeDetail = type.overloads + .map(overload => name + this._evaluator.printType(overload)) + .join('\n'); } else { typeDetail = name + ': ' + this._evaluator.printType(type); } @@ -1121,29 +1202,54 @@ export class CompletionProvider { let autoImportText: string | undefined; if (autoImportSource) { - autoImportText = `Auto-import from ${ autoImportSource }`; + autoImportText = `Auto-import from ${autoImportSource}`; } - this._addNameToCompletionList(name, itemKind, priorWord, completionList, - undefined, undefined, autoImportText, textEdit, - additionalTextEdits, symbol.id); + this._addNameToCompletionList( + name, + itemKind, + priorWord, + completionList, + undefined, + undefined, + autoImportText, + textEdit, + additionalTextEdits, + symbol.id + ); } else { // Does the symbol have no declaration but instead has a synthesized type? const synthesizedType = symbol.getSynthesizedType(); if (synthesizedType) { - const itemKind: CompletionItemKind = CompletionItemKind.Variable; - this._addNameToCompletionList(name, itemKind, priorWord, completionList, - undefined, undefined, undefined, textEdit, - additionalTextEdits, symbol.id); + const itemKind: CompletionItemKind = CompletionItemKind.Variable; + this._addNameToCompletionList( + name, + itemKind, + priorWord, + completionList, + undefined, + undefined, + undefined, + textEdit, + additionalTextEdits, + symbol.id + ); } } } - private _addNameToCompletionList(name: string, itemKind: CompletionItemKind, - filter: string, completionList: CompletionList, typeDetail?: string, - documentation?: string, autoImportText?: string, textEdit?: TextEdit, - additionalTextEdits?: TextEditAction[], symbolId?: number) { - + private _addNameToCompletionList( + name: string, + itemKind: CompletionItemKind, + filter: string, + completionList: CompletionList, + typeDetail?: string, + documentation?: string, + autoImportText?: string, + textEdit?: TextEdit, + additionalTextEdits?: TextEditAction[], + symbolId?: number + ) { const similarity = StringUtils.computeCompletionSimilarity(filter, name); if (similarity > similarityLimit) { @@ -1159,22 +1265,18 @@ export class CompletionProvider { if (autoImportText) { // Force auto-import entries to the end. - completionItem.sortText = - this._makeSortText(SortCategory.AutoImport, name, autoImportText); + completionItem.sortText = this._makeSortText(SortCategory.AutoImport, name, autoImportText); completionItemData.autoImportText = autoImportText; } else if (SymbolNameUtils.isDunderName(name)) { // Force dunder-named symbols to appear after all other symbols. - completionItem.sortText = - this._makeSortText(SortCategory.DunderSymbol, name); - } else if (filter === '' && (SymbolNameUtils.isPrivateOrProtectedName(name))) { + completionItem.sortText = this._makeSortText(SortCategory.DunderSymbol, name); + } else if (filter === '' && SymbolNameUtils.isPrivateOrProtectedName(name)) { // Distinguish between normal and private symbols only if there is // currently no filter text. Once we get a single character to filter // upon, we'll no longer differentiate. - completionItem.sortText = - this._makeSortText(SortCategory.PrivateSymbol, name); + completionItem.sortText = this._makeSortText(SortCategory.PrivateSymbol, name); } else { - completionItem.sortText = - this._makeSortText(SortCategory.NormalSymbol, name); + completionItem.sortText = this._makeSortText(SortCategory.NormalSymbol, name); } if (symbolId !== undefined) { @@ -1228,13 +1330,11 @@ export class CompletionProvider { private _getRecentListIndex(name: string, autoImportText: string) { return CompletionProvider._mostRecentCompletions.findIndex( - item => item.label === name && - item.autoImportText === autoImportText); + item => item.label === name && item.autoImportText === autoImportText + ); } - private _makeSortText(sortCategory: SortCategory, name: string, - autoImportText = ''): string { - + private _makeSortText(sortCategory: SortCategory, name: string, autoImportText = ''): string { const recentListIndex = this._getRecentListIndex(name, autoImportText); // If the label is in the recent list, modify the category @@ -1244,10 +1344,12 @@ export class CompletionProvider { sortCategory = SortCategory.RecentAutoImport; } else if (sortCategory === SortCategory.ImportModuleName) { sortCategory = SortCategory.RecentImportModuleName; - } else if (sortCategory === SortCategory.Keyword || - sortCategory === SortCategory.NormalSymbol || - sortCategory === SortCategory.PrivateSymbol || - sortCategory === SortCategory.DunderSymbol) { + } else if ( + sortCategory === SortCategory.Keyword || + sortCategory === SortCategory.NormalSymbol || + sortCategory === SortCategory.PrivateSymbol || + sortCategory === SortCategory.DunderSymbol + ) { sortCategory = SortCategory.RecentKeywordOrSymbol; } } @@ -1256,9 +1358,7 @@ export class CompletionProvider { // XX.YYYY.name // where XX is the sort category // and YYYY is the index of the item in the MRU list - return this._formatInteger(sortCategory, 2) + '.' + - this._formatInteger(recentListIndex, 4) + '.' + - name; + return this._formatInteger(sortCategory, 2) + '.' + this._formatInteger(recentListIndex, 4) + '.' + name; } private _formatInteger(val: number, digits: number): string { @@ -1292,13 +1392,12 @@ export class CompletionProvider { return CompletionItemKind.Variable; case DeclarationType.Variable: - return resolvedDeclaration.isConstant || resolvedDeclaration.isFinal ? - CompletionItemKind.Constant : - CompletionItemKind.Variable; + return resolvedDeclaration.isConstant || resolvedDeclaration.isFinal + ? CompletionItemKind.Constant + : CompletionItemKind.Variable; case DeclarationType.Function: - return resolvedDeclaration.isMethod ? - CompletionItemKind.Method : CompletionItemKind.Function; + return resolvedDeclaration.isMethod ? CompletionItemKind.Method : CompletionItemKind.Function; case DeclarationType.Class: case DeclarationType.SpecialBuiltInClass: @@ -1318,30 +1417,35 @@ export class CompletionProvider { importedSymbols: [] }; - const completions = this._importResolver.getCompletionSuggestions(this._filePath, - execEnvironment, moduleDescriptor, similarityLimit); + const completions = this._importResolver.getCompletionSuggestions( + this._filePath, + execEnvironment, + moduleDescriptor, + similarityLimit + ); const completionList = CompletionList.create(); // If we're in the middle of a "from X import Y" statement, offer // the "import" keyword as a completion. - if (!node.hasTrailingDot && node.parent && node.parent.nodeType === ParseNodeType.ImportFrom && - node.parent.missingImportKeyword) { - + if ( + !node.hasTrailingDot && + node.parent && + node.parent.nodeType === ParseNodeType.ImportFrom && + node.parent.missingImportKeyword + ) { const keyword = 'import'; const completionItem = CompletionItem.create(keyword); completionItem.kind = CompletionItemKind.Keyword; completionList.items.push(completionItem); - completionItem.sortText = - this._makeSortText(SortCategory.Keyword, keyword); + completionItem.sortText = this._makeSortText(SortCategory.Keyword, keyword); } completions.forEach(completionName => { const completionItem = CompletionItem.create(completionName); completionItem.kind = CompletionItemKind.Module; completionList.items.push(completionItem); - completionItem.sortText = - this._makeSortText(SortCategory.ImportModuleName, completionName); + completionItem.sortText = this._makeSortText(SortCategory.ImportModuleName, completionName); }); return completionList; diff --git a/server/src/languageService/definitionProvider.ts b/server/src/languageService/definitionProvider.ts index 912cd3d38..c9e879486 100644 --- a/server/src/languageService/definitionProvider.ts +++ b/server/src/languageService/definitionProvider.ts @@ -1,14 +1,14 @@ /* -* definitionProvider.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic that maps a position within a Python program file into -* a "definition" of the item that is referred to at that position. -* For example, if the location is within an import name, the -* definition is the top of the resolved import file. -*/ + * definitionProvider.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic that maps a position within a Python program file into + * a "definition" of the item that is referred to at that position. + * For example, if the location is within an import name, the + * definition is the top of the resolved import file. + */ import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; import { TypeEvaluator } from '../analyzer/typeEvaluator'; @@ -18,9 +18,11 @@ import { ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; export class DefinitionProvider { - static getDefinitionsForPosition(parseResults: ParseResults, position: Position, - evaluator: TypeEvaluator): DocumentRange[] | undefined { - + static getDefinitionsForPosition( + parseResults: ParseResults, + position: Position, + evaluator: TypeEvaluator + ): DocumentRange[] | undefined { const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines); if (offset === undefined) { return undefined; diff --git a/server/src/languageService/documentSymbolProvider.ts b/server/src/languageService/documentSymbolProvider.ts index 3a83a5e80..604081580 100644 --- a/server/src/languageService/documentSymbolProvider.ts +++ b/server/src/languageService/documentSymbolProvider.ts @@ -1,12 +1,12 @@ /* -* documentSymbolProvider.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic that enumerates all of the symbols within a specified -* source file document. -*/ + * documentSymbolProvider.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic that enumerates all of the symbols within a specified + * source file document. + */ import { DocumentSymbol, Location, SymbolInformation, SymbolKind } from 'vscode-languageserver'; import { URI } from 'vscode-uri'; @@ -34,9 +34,13 @@ class FindSymbolTreeWalker extends ParseTreeWalker { private _query: string | undefined; private _evaluator: TypeEvaluator; - constructor(filePath: string, parseResults: ParseResults, symbolInfoResults: SymbolInformation[], - query: string | undefined, evaluator: TypeEvaluator) { - + constructor( + filePath: string, + parseResults: ParseResults, + symbolInfoResults: SymbolInformation[], + query: string | undefined, + evaluator: TypeEvaluator + ) { super(); this._filePath = filePath; this._parseResults = parseResults; @@ -100,9 +104,7 @@ class FindSymbolTreeWalker extends ParseTreeWalker { }); } - private _addSymbolInformationFromDeclaration(name: string, declaration: Declaration, - containerName?: string) { - + private _addSymbolInformationFromDeclaration(name: string, declaration: Declaration, containerName?: string) { if (declaration.path !== this._filePath) { return; } @@ -179,8 +181,7 @@ function getSymbolKind(name: string, declaration: Declaration, evaluator: TypeEv if (name === '_') { return; } - symbolKind = declaration.isConstant || declaration.isFinal ? - SymbolKind.Constant : SymbolKind.Variable; + symbolKind = declaration.isConstant || declaration.isFinal ? SymbolKind.Constant : SymbolKind.Variable; break; default: @@ -191,10 +192,12 @@ function getSymbolKind(name: string, declaration: Declaration, evaluator: TypeEv return symbolKind; } -function getDocumentSymbolsRecursive(node: AnalyzerNodeInfo.ScopedNode, - docSymbolResults: DocumentSymbol[], parseResults: ParseResults, - evaluator: TypeEvaluator) { - +function getDocumentSymbolsRecursive( + node: AnalyzerNodeInfo.ScopedNode, + docSymbolResults: DocumentSymbol[], + parseResults: ParseResults, + evaluator: TypeEvaluator +) { const scope = AnalyzerNodeInfo.getScope(node); if (!scope) { return; @@ -218,10 +221,13 @@ function getDocumentSymbolsRecursive(node: AnalyzerNodeInfo.ScopedNode, }); } -function getDocumentSymbolRecursive(name: string, declaration: Declaration, - evaluator: TypeEvaluator, parseResults: ParseResults, - docSymbolResults: DocumentSymbol[]) { - +function getDocumentSymbolRecursive( + name: string, + declaration: Declaration, + evaluator: TypeEvaluator, + parseResults: ParseResults, + docSymbolResults: DocumentSymbol[] +) { if (declaration.type === DeclarationType.Alias) { return; } @@ -235,14 +241,14 @@ function getDocumentSymbolRecursive(name: string, declaration: Declaration, let range = selectionRange; const children: DocumentSymbol[] = []; - if (declaration.type === DeclarationType.Class || - declaration.type === DeclarationType.Function) { - + if (declaration.type === DeclarationType.Class || declaration.type === DeclarationType.Function) { getDocumentSymbolsRecursive(declaration.node, children, parseResults, evaluator); - const nameRange = convertOffsetsToRange(declaration.node.start, + const nameRange = convertOffsetsToRange( + declaration.node.start, declaration.node.name.start + declaration.node.length, - parseResults.tokenizerOutput.lines); + parseResults.tokenizerOutput.lines + ); range = nameRange; } @@ -258,17 +264,22 @@ function getDocumentSymbolRecursive(name: string, declaration: Declaration, } export class DocumentSymbolProvider { - static addSymbolsForDocument(symbolList: SymbolInformation[], query: string | undefined, - filePath: string, parseResults: ParseResults, evaluator: TypeEvaluator) { - - const symbolTreeWalker = new FindSymbolTreeWalker(filePath, parseResults, - symbolList, query, evaluator); + static addSymbolsForDocument( + symbolList: SymbolInformation[], + query: string | undefined, + filePath: string, + parseResults: ParseResults, + evaluator: TypeEvaluator + ) { + const symbolTreeWalker = new FindSymbolTreeWalker(filePath, parseResults, symbolList, query, evaluator); symbolTreeWalker.findSymbols(); } - static addHierarchicalSymbolsForDocument(symbolList: DocumentSymbol[], - parseResults: ParseResults, evaluator: TypeEvaluator) { - + static addHierarchicalSymbolsForDocument( + symbolList: DocumentSymbol[], + parseResults: ParseResults, + evaluator: TypeEvaluator + ) { getDocumentSymbolsRecursive(parseResults.parseTree, symbolList, parseResults, evaluator); } } diff --git a/server/src/languageService/hoverProvider.ts b/server/src/languageService/hoverProvider.ts index f362fea69..58e224e0f 100644 --- a/server/src/languageService/hoverProvider.ts +++ b/server/src/languageService/hoverProvider.ts @@ -1,13 +1,13 @@ /* -* hoverProvider.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic that maps a position within a Python program file into -* markdown text that is displayed when the user hovers over that -* position within a smart editor. -*/ + * hoverProvider.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic that maps a position within a Python program file into + * markdown text that is displayed when the user hovers over that + * position within a smart editor. + */ import { Declaration, DeclarationType } from '../analyzer/declaration'; import { convertDocStringToMarkdown } from '../analyzer/docStringUtils'; @@ -32,9 +32,11 @@ export interface HoverResults { } export class HoverProvider { - static getHoverForPosition(parseResults: ParseResults, position: Position, - evaluator: TypeEvaluator): HoverResults | undefined { - + static getHoverForPosition( + parseResults: ParseResults, + position: Position, + evaluator: TypeEvaluator + ): HoverResults | undefined { const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines); if (offset === undefined) { return undefined; @@ -84,36 +86,34 @@ export class HoverProvider { return results.parts.length > 0 ? results : undefined; } - private static _addResultsForDeclaration(parts: HoverTextPart[], declaration: Declaration, - node: NameNode, evaluator: TypeEvaluator): void { - + private static _addResultsForDeclaration( + parts: HoverTextPart[], + declaration: Declaration, + node: NameNode, + evaluator: TypeEvaluator + ): void { const resolvedDecl = evaluator.resolveAliasDeclaration(declaration); if (!resolvedDecl) { - this._addResultsPart(parts, `(import) ` + node.value + - this._getTypeText(node, evaluator), true); + this._addResultsPart(parts, `(import) ` + node.value + this._getTypeText(node, evaluator), true); return; } switch (resolvedDecl.type) { case DeclarationType.Intrinsic: { - this._addResultsPart(parts, node.value + - this._getTypeText(node, evaluator), true); + this._addResultsPart(parts, node.value + this._getTypeText(node, evaluator), true); this._addDocumentationPart(parts, node, evaluator); break; } case DeclarationType.Variable: { - const label = resolvedDecl.isConstant || resolvedDecl.isFinal ? - 'constant' : 'variable'; - this._addResultsPart(parts, `(${ label }) ` + node.value + - this._getTypeText(node, evaluator), true); + const label = resolvedDecl.isConstant || resolvedDecl.isFinal ? 'constant' : 'variable'; + this._addResultsPart(parts, `(${label}) ` + node.value + this._getTypeText(node, evaluator), true); this._addDocumentationPart(parts, node, evaluator); break; } case DeclarationType.Parameter: { - this._addResultsPart(parts, '(parameter) ' + node.value + - this._getTypeText(node, evaluator), true); + this._addResultsPart(parts, '(parameter) ' + node.value + this._getTypeText(node, evaluator), true); this._addDocumentationPart(parts, node, evaluator); break; } @@ -129,12 +129,10 @@ export class HoverProvider { let label = 'function'; if (resolvedDecl.isMethod) { const declaredType = evaluator.getTypeForDeclaration(resolvedDecl); - label = declaredType && isProperty(declaredType) ? - 'property' : 'method'; + label = declaredType && isProperty(declaredType) ? 'property' : 'method'; } - this._addResultsPart(parts, `(${ label }) ` + node.value + - this._getTypeText(node, evaluator), true); + this._addResultsPart(parts, `(${label}) ` + node.value + this._getTypeText(node, evaluator), true); this._addDocumentationPart(parts, node, evaluator); break; } diff --git a/server/src/languageService/importSorter.ts b/server/src/languageService/importSorter.ts index d515d42d0..5b84e058c 100644 --- a/server/src/languageService/importSorter.ts +++ b/server/src/languageService/importSorter.ts @@ -1,12 +1,12 @@ /* -* importSorter.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Provides code that sorts and formats import statements within a -* python source file. -*/ + * importSorter.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Provides code that sorts and formats import statements within a + * python source file. + */ import { ImportType } from '../analyzer/importResult'; import * as ImportStatementUtils from '../analyzer/importStatementUtils'; @@ -33,11 +33,11 @@ export class ImportSorter { sort(): TextEditAction[] { const actions: TextEditAction[] = []; - const importStatements = ImportStatementUtils.getTopLevelImports( - this._parseResults.parseTree); + const importStatements = ImportStatementUtils.getTopLevelImports(this._parseResults.parseTree); - const sortedStatements = importStatements.orderedImports. - map(s => s).sort((a, b) => { + const sortedStatements = importStatements.orderedImports + .map(s => s) + .sort((a, b) => { return this._compareImportStatements(a, b); }); @@ -46,23 +46,19 @@ export class ImportSorter { return []; } - const primaryRange = this._getPrimaryReplacementRange( - importStatements.orderedImports); + const primaryRange = this._getPrimaryReplacementRange(importStatements.orderedImports); actions.push({ range: primaryRange, replacementText: this._generateSortedImportText(sortedStatements) }); - this._addSecondaryReplacementRanges( - importStatements.orderedImports, actions); + this._addSecondaryReplacementRanges(importStatements.orderedImports, actions); return actions; } - private _compareImportStatements(a: ImportStatementUtils.ImportStatement, - b: ImportStatementUtils.ImportStatement) { - + private _compareImportStatements(a: ImportStatementUtils.ImportStatement, b: ImportStatementUtils.ImportStatement) { const aImportGroup = this._getImportGroup(a); const bImportGroup = this._getImportGroup(b); @@ -72,16 +68,17 @@ export class ImportSorter { return 1; } - return (a.moduleName < b.moduleName) ? -1 : 1; + return a.moduleName < b.moduleName ? -1 : 1; } private _getImportGroup(statement: ImportStatementUtils.ImportStatement): ImportGroup { if (statement.importResult) { if (statement.importResult.importType === ImportType.BuiltIn) { return ImportGroup.BuiltIn; - } else if (statement.importResult.importType === ImportType.ThirdParty || - statement.importResult.isLocalTypingsFile) { - + } else if ( + statement.importResult.importType === ImportType.ThirdParty || + statement.importResult.isLocalTypingsFile + ) { return ImportGroup.ThirdParty; } @@ -98,9 +95,7 @@ export class ImportSorter { // Determines the text range for the existing primary block of import statements. // If there are other blocks of import statements separated by other statements, // we'll ignore these other blocks for now. - private _getPrimaryReplacementRange(statements: ImportStatementUtils.ImportStatement[]): - Range { - + private _getPrimaryReplacementRange(statements: ImportStatementUtils.ImportStatement[]): Range { let statementLimit = statements.findIndex(s => s.followsNonImportStatement); if (statementLimit < 0) { statementLimit = statements.length; @@ -108,18 +103,17 @@ export class ImportSorter { const lastStatement = statements[statementLimit - 1].node; return { - start: convertOffsetToPosition( - statements[0].node.start, this._parseResults.tokenizerOutput.lines), - end: convertOffsetToPosition( - TextRange.getEnd(lastStatement), this._parseResults.tokenizerOutput.lines) + start: convertOffsetToPosition(statements[0].node.start, this._parseResults.tokenizerOutput.lines), + end: convertOffsetToPosition(TextRange.getEnd(lastStatement), this._parseResults.tokenizerOutput.lines) }; } // If import statements are separated by other statements, we will remove the old // secondary blocks. - private _addSecondaryReplacementRanges(statements: ImportStatementUtils.ImportStatement[], - actions: TextEditAction[]) { - + private _addSecondaryReplacementRanges( + statements: ImportStatementUtils.ImportStatement[], + actions: TextEditAction[] + ) { let secondaryBlockStart = statements.findIndex(s => s.followsNonImportStatement); if (secondaryBlockStart < 0) { return; @@ -127,7 +121,8 @@ export class ImportSorter { while (true) { let secondaryBlockLimit = statements.findIndex( - (s, index) => index > secondaryBlockStart && s.followsNonImportStatement); + (s, index) => index > secondaryBlockStart && s.followsNonImportStatement + ); if (secondaryBlockLimit < 0) { secondaryBlockLimit = statements.length; } @@ -136,10 +131,12 @@ export class ImportSorter { range: { start: convertOffsetToPosition( statements[secondaryBlockStart].node.start, - this._parseResults.tokenizerOutput.lines), + this._parseResults.tokenizerOutput.lines + ), end: convertOffsetToPosition( TextRange.getEnd(statements[secondaryBlockLimit - 1].node), - this._parseResults.tokenizerOutput.lines) + this._parseResults.tokenizerOutput.lines + ) }, replacementText: '' }); @@ -165,11 +162,9 @@ export class ImportSorter { let importLine: string; if (statement.node.nodeType === ParseNodeType.Import) { - importLine = this._formatImportNode(statement.subnode!, - statement.moduleName); + importLine = this._formatImportNode(statement.subnode!, statement.moduleName); } else { - importLine = this._formatImportFromNode(statement.node, - statement.moduleName); + importLine = this._formatImportFromNode(statement.node, statement.moduleName); } // If this isn't the last statement, add a newline. @@ -184,27 +179,27 @@ export class ImportSorter { } private _formatImportNode(subnode: ImportAsNode, moduleName: string): string { - let importText = `import ${ moduleName }`; + let importText = `import ${moduleName}`; if (subnode.alias) { - importText += ` as ${ subnode.alias.value }`; + importText += ` as ${subnode.alias.value}`; } return importText; } private _formatImportFromNode(node: ImportFromNode, moduleName: string): string { - const symbols = node.imports. - sort((a, b) => this._compareSymbols(a, b)). - map(symbol => { + const symbols = node.imports + .sort((a, b) => this._compareSymbols(a, b)) + .map(symbol => { let symbolText = symbol.name.value; if (symbol.alias) { - symbolText += ` as ${ symbol.alias.value }`; + symbolText += ` as ${symbol.alias.value}`; } return symbolText; }); - let cumulativeText = `from ${ moduleName } import `; + let cumulativeText = `from ${moduleName} import `; const symbolText = symbols.join(', '); if (cumulativeText.length + symbolText.length <= _maxLineLength) { return cumulativeText + symbolText; diff --git a/server/src/languageService/quickActions.ts b/server/src/languageService/quickActions.ts index 01adb781f..a665c5820 100644 --- a/server/src/languageService/quickActions.ts +++ b/server/src/languageService/quickActions.ts @@ -1,11 +1,11 @@ /* -* quickActions.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Provides support for miscellaneous quick actions. -*/ + * quickActions.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Provides support for miscellaneous quick actions. + */ import { ImportType } from '../analyzer/importResult'; import * as ImportStatementUtils from '../analyzer/importStatementUtils'; @@ -18,9 +18,7 @@ import { ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; import { ImportSorter } from './importSorter'; -export function performQuickAction(command: string, args: any[], - parseResults: ParseResults) { - +export function performQuickAction(command: string, args: any[], parseResults: ParseResults) { if (command === Commands.orderImports) { const importSorter = new ImportSorter(parseResults); return importSorter.sort(); @@ -36,9 +34,7 @@ export function performQuickAction(command: string, args: any[], // Inserts text into the document to wrap an existing type annotation // with "Optional[X]". -function _addMissingOptionalToParam(parseResults: ParseResults, - offset: number): TextEditAction[] { - +function _addMissingOptionalToParam(parseResults: ParseResults, offset: number): TextEditAction[] { let node: ParseNode | undefined = ParseTreeUtils.findNodeByOffset(parseResults.parseTree, offset); while (node) { if (node.nodeType === ParseNodeType.Parameter) { @@ -54,10 +50,8 @@ function _addMissingOptionalToParam(parseResults: ParseResults, const editActions: TextEditAction[] = []; - const startPos = convertOffsetToPosition( - node.typeAnnotation.start, parseResults.tokenizerOutput.lines); - const endPos = convertOffsetToPosition( - TextRange.getEnd(node.typeAnnotation), parseResults.tokenizerOutput.lines); + const startPos = convertOffsetToPosition(node.typeAnnotation.start, parseResults.tokenizerOutput.lines); + const endPos = convertOffsetToPosition(TextRange.getEnd(node.typeAnnotation), parseResults.tokenizerOutput.lines); editActions.push({ range: { start: startPos, end: startPos }, @@ -69,19 +63,25 @@ function _addMissingOptionalToParam(parseResults: ParseResults, }); // Add the import statement if necessary. - const importStatements = ImportStatementUtils.getTopLevelImports( - parseResults.parseTree); - const importStatement = importStatements.orderedImports.find( - imp => imp.moduleName === 'typing'); + const importStatements = ImportStatementUtils.getTopLevelImports(parseResults.parseTree); + const importStatement = importStatements.orderedImports.find(imp => imp.moduleName === 'typing'); // If there's an existing import statement, insert into it. if (importStatement && importStatement.node.nodeType === ParseNodeType.ImportFrom) { const additionalEditActions = ImportStatementUtils.getTextEditsForAutoImportSymbolAddition( - 'Optional', importStatement, parseResults); + 'Optional', + importStatement, + parseResults + ); editActions.push(...additionalEditActions); } else { const additionalEditActions = ImportStatementUtils.getTextEditsForAutoImportInsertion( - 'Optional', importStatements, 'typing', ImportType.BuiltIn, parseResults); + 'Optional', + importStatements, + 'typing', + ImportType.BuiltIn, + parseResults + ); editActions.push(...additionalEditActions); } diff --git a/server/src/languageService/referencesProvider.ts b/server/src/languageService/referencesProvider.ts index 0a7c8f4f8..877ef0f68 100644 --- a/server/src/languageService/referencesProvider.ts +++ b/server/src/languageService/referencesProvider.ts @@ -1,12 +1,12 @@ /* -* referencesProvider.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic that finds all of the references to a symbol specified -* by a location within a file. -*/ + * referencesProvider.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic that finds all of the references to a symbol specified + * by a location within a file. + */ import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo'; import { Declaration, DeclarationType } from '../analyzer/declaration'; @@ -15,7 +15,7 @@ import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; import { ParseTreeWalker } from '../analyzer/parseTreeWalker'; import { TypeEvaluator } from '../analyzer/typeEvaluator'; import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils'; -import { DocumentRange,Position } from '../common/textRange'; +import { DocumentRange, Position } from '../common/textRange'; import { TextRange } from '../common/textRange'; import { NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; @@ -34,10 +34,13 @@ class FindReferencesTreeWalker extends ParseTreeWalker { private _includeDeclaration: boolean; private _evaluator: TypeEvaluator; - constructor(parseResults: ParseResults, filePath: string, - referencesResult: ReferencesResult, includeDeclaration: boolean, - evaluator: TypeEvaluator) { - + constructor( + parseResults: ParseResults, + filePath: string, + referencesResult: ReferencesResult, + includeDeclaration: boolean, + evaluator: TypeEvaluator + ) { super(); this._parseResults = parseResults; this._filePath = filePath; @@ -68,7 +71,10 @@ class FindReferencesTreeWalker extends ParseTreeWalker { path: this._filePath, range: { start: convertOffsetToPosition(node.start, this._parseResults.tokenizerOutput.lines), - end: convertOffsetToPosition(TextRange.getEnd(node), this._parseResults.tokenizerOutput.lines) + end: convertOffsetToPosition( + TextRange.getEnd(node), + this._parseResults.tokenizerOutput.lines + ) } }); } @@ -87,15 +93,19 @@ class FindReferencesTreeWalker extends ParseTreeWalker { // The reference results declarations are already resolved, so we don't // need to call resolveAliasDeclaration on them. return this._referencesResult.declarations.some(decl => - DeclarationUtils.areDeclarationsSame(decl, resolvedDecl)); + DeclarationUtils.areDeclarationsSame(decl, resolvedDecl) + ); } } export class ReferencesProvider { - static getReferencesForPosition(parseResults: ParseResults, filePath: string, - position: Position, includeDeclaration: boolean, - evaluator: TypeEvaluator): ReferencesResult | undefined { - + static getReferencesForPosition( + parseResults: ParseResults, + filePath: string, + position: Position, + includeDeclaration: boolean, + evaluator: TypeEvaluator + ): ReferencesResult | undefined { const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines); if (offset === undefined) { return undefined; @@ -133,7 +143,8 @@ export class ReferencesProvider { // Parameters are local to a scope, so they don't require a global search. // If it's a named argument referring to a parameter, we still need to perform // the global search. - const requiresGlobalSearch = symbolDeclType !== DeclarationType.Parameter || + const requiresGlobalSearch = + symbolDeclType !== DeclarationType.Parameter || (node.parent !== undefined && node.parent.nodeType === ParseNodeType.Argument); const results: ReferencesResult = { @@ -143,19 +154,32 @@ export class ReferencesProvider { locations: [] }; - const refTreeWalker = new FindReferencesTreeWalker(parseResults, - filePath, results, includeDeclaration, evaluator); + const refTreeWalker = new FindReferencesTreeWalker( + parseResults, + filePath, + results, + includeDeclaration, + evaluator + ); refTreeWalker.findReferences(); return results; } - static addReferences(parseResults: ParseResults, filePath: string, - referencesResult: ReferencesResult, includeDeclaration: boolean, - evaluator: TypeEvaluator): void { - - const refTreeWalker = new FindReferencesTreeWalker(parseResults, - filePath, referencesResult, includeDeclaration, evaluator); + static addReferences( + parseResults: ParseResults, + filePath: string, + referencesResult: ReferencesResult, + includeDeclaration: boolean, + evaluator: TypeEvaluator + ): void { + const refTreeWalker = new FindReferencesTreeWalker( + parseResults, + filePath, + referencesResult, + includeDeclaration, + evaluator + ); refTreeWalker.findReferences(); } } diff --git a/server/src/languageService/signatureHelpProvider.ts b/server/src/languageService/signatureHelpProvider.ts index aa4c77e27..f85ff597f 100644 --- a/server/src/languageService/signatureHelpProvider.ts +++ b/server/src/languageService/signatureHelpProvider.ts @@ -1,13 +1,13 @@ /* -* signatureHelpProvider.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Logic that maps a position within a Python call node into info -* that can be presented to the developer to help fill in the remaining -* arguments for the call. -*/ + * signatureHelpProvider.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Logic that maps a position within a Python call node into info + * that can be presented to the developer to help fill in the remaining + * arguments for the call. + */ import { extractParameterDocumentation } from '../analyzer/docStringUtils'; import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; @@ -36,10 +36,11 @@ export interface SignatureHelpResults { } export class SignatureHelpProvider { - static getSignatureHelpForPosition(parseResults: ParseResults, position: Position, - evaluator: TypeEvaluator): - SignatureHelpResults | undefined { - + static getSignatureHelpForPosition( + parseResults: ParseResults, + position: Position, + evaluator: TypeEvaluator + ): SignatureHelpResults | undefined { const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines); if (offset === undefined) { return undefined; diff --git a/server/src/parser/characterStream.ts b/server/src/parser/characterStream.ts index 2db11eb86..e37b19d58 100644 --- a/server/src/parser/characterStream.ts +++ b/server/src/parser/characterStream.ts @@ -1,14 +1,14 @@ /* -* characterStream.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Based on code from vscode-python repository: -* https://github.com/Microsoft/vscode-python -* -* Class that represents a stream of characters. -*/ + * characterStream.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Based on code from vscode-python repository: + * https://github.com/Microsoft/vscode-python + * + * Class that represents a stream of characters. + */ import Char from 'typescript-char'; diff --git a/server/src/parser/characters.ts b/server/src/parser/characters.ts index 9aad2ec0e..131cfe78e 100644 --- a/server/src/parser/characters.ts +++ b/server/src/parser/characters.ts @@ -1,14 +1,14 @@ /* -* characters.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Based on code from vscode-python repository: -* https://github.com/Microsoft/vscode-python -* -* Utility routines used by tokenizer. -*/ + * characters.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Based on code from vscode-python repository: + * https://github.com/Microsoft/vscode-python + * + * Utility routines used by tokenizer. + */ import Char from 'typescript-char'; @@ -51,12 +51,16 @@ export function isIdentifierStartChar(ch: number) { export function isIdentifierChar(ch: number) { if (ch < _identifierCharFastTableSize) { - return _identifierCharFastTable[ch] === CharCategory.StartIdentifierChar || - _identifierCharFastTable[ch] === CharCategory.IdentifierChar; + return ( + _identifierCharFastTable[ch] === CharCategory.StartIdentifierChar || + _identifierCharFastTable[ch] === CharCategory.IdentifierChar + ); } - return _identifierCharMap[ch] === CharCategory.StartIdentifierChar || - _identifierCharMap[ch] === CharCategory.IdentifierChar; + return ( + _identifierCharMap[ch] === CharCategory.StartIdentifierChar || + _identifierCharMap[ch] === CharCategory.IdentifierChar + ); } export function isWhiteSpace(ch: number): boolean { @@ -68,11 +72,11 @@ export function isLineBreak(ch: number): boolean { } export function isNumber(ch: number): boolean { - return ch >= Char._0 && ch <= Char._9 || ch === Char.Underscore; + return (ch >= Char._0 && ch <= Char._9) || ch === Char.Underscore; } export function isDecimal(ch: number): boolean { - return ch >= Char._0 && ch <= Char._9 || ch === Char.Underscore; + return (ch >= Char._0 && ch <= Char._9) || ch === Char.Underscore; } export function isHex(ch: number): boolean { @@ -80,7 +84,7 @@ export function isHex(ch: number): boolean { } export function isOctal(ch: number): boolean { - return ch >= Char._0 && ch <= Char._7 || ch === Char.Underscore; + return (ch >= Char._0 && ch <= Char._7) || ch === Char.Underscore; } export function isBinary(ch: number): boolean { @@ -89,21 +93,55 @@ export function isBinary(ch: number): boolean { // Underscore is explicitly allowed to start an identifier. // Characters with the Other_ID_Start property. -const _specialStartIdentifierChars: unicode.UnicodeRangeTable = [Char.Underscore, - 0x1885, 0x1886, 0x2118, 0x212E, 0x309B, 0x309C]; +const _specialStartIdentifierChars: unicode.UnicodeRangeTable = [ + Char.Underscore, + 0x1885, + 0x1886, + 0x2118, + 0x212e, + 0x309b, + 0x309c +]; -const _startIdentifierCharRanges = [_specialStartIdentifierChars, unicode.unicodeLu, - unicode.unicodeLl, unicode.unicodeLt, unicode.unicodeLo, unicode.unicodeLm, unicode.unicodeNl]; +const _startIdentifierCharRanges = [ + _specialStartIdentifierChars, + unicode.unicodeLu, + unicode.unicodeLl, + unicode.unicodeLt, + unicode.unicodeLo, + unicode.unicodeLm, + unicode.unicodeNl +]; // Characters with the Other_ID_Start property. -const _specialIdentifierChars: unicode.UnicodeRangeTable = [0x00B7, 0x0387, 0x1369, - 0x136A, 0x136B, 0x136C, 0x136D, 0x136E, 0x136F, 0x1370, 0x1371, 0x19DA]; +const _specialIdentifierChars: unicode.UnicodeRangeTable = [ + 0x00b7, + 0x0387, + 0x1369, + 0x136a, + 0x136b, + 0x136c, + 0x136d, + 0x136e, + 0x136f, + 0x1370, + 0x1371, + 0x19da +]; -const _identifierCharRanges = [_specialIdentifierChars, unicode.unicodeMn, unicode.unicodeMc, - unicode.unicodeNd, unicode.unicodePc]; +const _identifierCharRanges = [ + _specialIdentifierChars, + unicode.unicodeMn, + unicode.unicodeMc, + unicode.unicodeNd, + unicode.unicodePc +]; -function _buildIdentifierLookupTableFromUnicodeRangeTable(table: unicode.UnicodeRangeTable, - category: CharCategory, fastTableOnly: boolean) { +function _buildIdentifierLookupTableFromUnicodeRangeTable( + table: unicode.UnicodeRangeTable, + category: CharCategory, + fastTableOnly: boolean +) { for (let entryIndex = 0; entryIndex < table.length; entryIndex++) { const entry = table[entryIndex]; let rangeStart: number; @@ -135,13 +173,11 @@ function _buildIdentifierLookupTable(fastTableOnly: boolean) { _identifierCharFastTable.fill(CharCategory.NotIdentifierChar); _identifierCharRanges.forEach(table => { - _buildIdentifierLookupTableFromUnicodeRangeTable( - table, CharCategory.IdentifierChar, fastTableOnly); + _buildIdentifierLookupTableFromUnicodeRangeTable(table, CharCategory.IdentifierChar, fastTableOnly); }); _startIdentifierCharRanges.forEach(table => { - _buildIdentifierLookupTableFromUnicodeRangeTable( - table, CharCategory.StartIdentifierChar, fastTableOnly); + _buildIdentifierLookupTableFromUnicodeRangeTable(table, CharCategory.StartIdentifierChar, fastTableOnly); }); } diff --git a/server/src/parser/parseNodes.ts b/server/src/parser/parseNodes.ts index 5d62c80cc..8fcc217fc 100644 --- a/server/src/parser/parseNodes.ts +++ b/server/src/parser/parseNodes.ts @@ -1,16 +1,24 @@ /* -* parseNodes.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Definition of parse nodes that make up the Python abstract -* syntax tree (AST). -*/ + * parseNodes.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Definition of parse nodes that make up the Python abstract + * syntax tree (AST). + */ import { TextRange } from '../common/textRange'; -import { IdentifierToken, KeywordToken, KeywordType, - NumberToken, OperatorType, StringToken, Token, TokenType } from './tokenizerTypes'; +import { + IdentifierToken, + KeywordToken, + KeywordType, + NumberToken, + OperatorType, + StringToken, + Token, + TokenType +} from './tokenizerTypes'; export const enum ParseNodeType { Error, // 0 @@ -172,9 +180,12 @@ export interface IfNode extends ParseNodeBase { } export namespace IfNode { - export function create(ifOrElifToken: Token, testExpression: ExpressionNode, - ifSuite: SuiteNode, elseSuite?: SuiteNode) { - + export function create( + ifOrElifToken: Token, + testExpression: ExpressionNode, + ifSuite: SuiteNode, + elseSuite?: SuiteNode + ) { const node: IfNode = { start: ifOrElifToken.start, length: ifOrElifToken.length, @@ -236,9 +247,12 @@ export interface ForNode extends ParseNodeBase { } export namespace ForNode { - export function create(forToken: Token, targetExpression: ExpressionNode, - iterableExpression: ExpressionNode, forSuite: SuiteNode) { - + export function create( + forToken: Token, + targetExpression: ExpressionNode, + iterableExpression: ExpressionNode, + forSuite: SuiteNode + ) { const node: ForNode = { start: forToken.start, length: forToken.length, @@ -588,18 +602,57 @@ export namespace StatementListNode { } } -export type StatementNode = IfNode | WhileNode | ForNode | TryNode | - FunctionNode | ClassNode | WithNode | StatementListNode | ErrorNode; +export type StatementNode = + | IfNode + | WhileNode + | ForNode + | TryNode + | FunctionNode + | ClassNode + | WithNode + | StatementListNode + | ErrorNode; -export type SmallStatementNode = ExpressionNode | DelNode | PassNode | - ImportNode | GlobalNode | NonlocalNode | AssertNode; +export type SmallStatementNode = + | ExpressionNode + | DelNode + | PassNode + | ImportNode + | GlobalNode + | NonlocalNode + | AssertNode; -export type ExpressionNode = ErrorNode | UnaryOperationNode | BinaryOperationNode | - AssignmentNode | TypeAnnotationNode | AssignmentExpressionNode | AugmentedAssignmentNode | - AwaitNode | TernaryNode | UnpackNode | TupleNode | CallNode | ListComprehensionNode | IndexNode | - SliceNode | YieldNode | YieldFromNode | MemberAccessNode | LambdaNode | NameNode | ConstantNode | - EllipsisNode | NumberNode | StringNode | FormatStringNode | StringListNode | DictionaryNode | - DictionaryExpandEntryNode | ListNode | SetNode; +export type ExpressionNode = + | ErrorNode + | UnaryOperationNode + | BinaryOperationNode + | AssignmentNode + | TypeAnnotationNode + | AssignmentExpressionNode + | AugmentedAssignmentNode + | AwaitNode + | TernaryNode + | UnpackNode + | TupleNode + | CallNode + | ListComprehensionNode + | IndexNode + | SliceNode + | YieldNode + | YieldFromNode + | MemberAccessNode + | LambdaNode + | NameNode + | ConstantNode + | EllipsisNode + | NumberNode + | StringNode + | FormatStringNode + | StringListNode + | DictionaryNode + | DictionaryExpandEntryNode + | ListNode + | SetNode; export function isExpressionNode(node: ParseNode) { switch (node.nodeType) { @@ -645,9 +698,7 @@ export interface ErrorNode extends ParseNodeBase { } export namespace ErrorNode { - export function create(initialRange: TextRange, category: ErrorExpressionCategory, - child?: ExpressionNode) { - + export function create(initialRange: TextRange, category: ErrorExpressionCategory, child?: ExpressionNode) { const node: ErrorNode = { start: initialRange.start, length: initialRange.length, @@ -699,9 +750,7 @@ export interface BinaryOperationNode extends ParseNodeBase { } export namespace BinaryOperationNode { - export function create(leftExpression: ExpressionNode, rightExpression: ExpressionNode, - operator: OperatorType) { - + export function create(leftExpression: ExpressionNode, rightExpression: ExpressionNode, operator: OperatorType) { const node: BinaryOperationNode = { start: leftExpression.start, length: leftExpression.length, @@ -813,9 +862,12 @@ export interface AugmentedAssignmentNode extends ParseNodeBase { } export namespace AugmentedAssignmentNode { - export function create(leftExpression: ExpressionNode, rightExpression: ExpressionNode, - operator: OperatorType, destExpression: ExpressionNode) { - + export function create( + leftExpression: ExpressionNode, + rightExpression: ExpressionNode, + operator: OperatorType, + destExpression: ExpressionNode + ) { const node: AugmentedAssignmentNode = { start: leftExpression.start, length: leftExpression.length, @@ -868,9 +920,11 @@ export interface TernaryNode extends ParseNodeBase { } export namespace TernaryNode { - export function create(ifExpression: ExpressionNode, testExpression: ExpressionNode, - elseExpression: ExpressionNode) { - + export function create( + ifExpression: ExpressionNode, + testExpression: ExpressionNode, + elseExpression: ExpressionNode + ) { const node: TernaryNode = { start: ifExpression.start, length: ifExpression.length, @@ -1260,9 +1314,12 @@ export interface FormatStringNode extends ParseNodeBase { } export namespace FormatStringNode { - export function create(token: StringToken, unescapedValue: string, hasUnescapeErrors: boolean, - expressions: ExpressionNode[]) { - + export function create( + token: StringToken, + unescapedValue: string, + hasUnescapeErrors: boolean, + expressions: ExpressionNode[] + ) { const node: FormatStringNode = { start: token.start, length: token.length, @@ -1738,19 +1795,71 @@ export namespace RaiseNode { } } -export type ParseNode = ErrorNode | ArgumentNode | AssertNode | AssignmentExpressionNode | - AssignmentNode | AugmentedAssignmentNode | AwaitNode | BinaryOperationNode | - BreakNode | CallNode | ClassNode | ConstantNode | ContinueNode | - DecoratorNode | DelNode | DictionaryNode | DictionaryEntryNode | DictionaryExpandEntryNode | - DictionaryKeyEntryNode | EllipsisNode | IfNode | ImportNode | ImportAsNode | ImportFromNode | - ImportFromAsNode | IndexNode | IndexItemsNode | ExceptNode | ForNode | FormatStringNode | - FunctionNode | GlobalNode | LambdaNode | ListNode | ListComprehensionNode | ListComprehensionForNode | - ListComprehensionIfNode | MemberAccessNode | ModuleNameNode | ModuleNode | NameNode | - NonlocalNode | NumberNode | ParameterNode | PassNode | RaiseNode | ReturnNode | SetNode | - SliceNode | StatementListNode | StringListNode | StringNode | SuiteNode | - TernaryNode | TupleNode | TryNode | TypeAnnotationNode | - UnaryOperationNode | UnpackNode | WhileNode | WithNode | WithItemNode | - YieldNode | YieldFromNode; +export type ParseNode = + | ErrorNode + | ArgumentNode + | AssertNode + | AssignmentExpressionNode + | AssignmentNode + | AugmentedAssignmentNode + | AwaitNode + | BinaryOperationNode + | BreakNode + | CallNode + | ClassNode + | ConstantNode + | ContinueNode + | DecoratorNode + | DelNode + | DictionaryNode + | DictionaryEntryNode + | DictionaryExpandEntryNode + | DictionaryKeyEntryNode + | EllipsisNode + | IfNode + | ImportNode + | ImportAsNode + | ImportFromNode + | ImportFromAsNode + | IndexNode + | IndexItemsNode + | ExceptNode + | ForNode + | FormatStringNode + | FunctionNode + | GlobalNode + | LambdaNode + | ListNode + | ListComprehensionNode + | ListComprehensionForNode + | ListComprehensionIfNode + | MemberAccessNode + | ModuleNameNode + | ModuleNode + | NameNode + | NonlocalNode + | NumberNode + | ParameterNode + | PassNode + | RaiseNode + | ReturnNode + | SetNode + | SliceNode + | StatementListNode + | StringListNode + | StringNode + | SuiteNode + | TernaryNode + | TupleNode + | TryNode + | TypeAnnotationNode + | UnaryOperationNode + | UnpackNode + | WhileNode + | WithNode + | WithItemNode + | YieldNode + | YieldFromNode; export type EvaluationScopeNode = LambdaNode | FunctionNode | ModuleNode | ClassNode | ListComprehensionNode; export type ExecutionScopeNode = LambdaNode | FunctionNode | ModuleNode; diff --git a/server/src/parser/parser.ts b/server/src/parser/parser.ts index 3c8e02466..3aba010c1 100644 --- a/server/src/parser/parser.ts +++ b/server/src/parser/parser.ts @@ -1,15 +1,15 @@ /* -* parser.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Based on code from python-language-server repository: -* https://github.com/Microsoft/python-language-server -* -* Parser for the Python language. Converts a stream of tokens -* into an abstract syntax tree (AST). -*/ + * parser.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Based on code from python-language-server repository: + * https://github.com/Microsoft/python-language-server + * + * Parser for the Python language. Converts a stream of tokens + * into an abstract syntax tree (AST). + */ import { assert } from '../common/debug'; import { Diagnostic, DiagnosticAddendum } from '../common/diagnostic'; @@ -19,24 +19,97 @@ import { latestStablePythonVersion, PythonVersion } from '../common/pythonVersio import { TextRange } from '../common/textRange'; import { TextRangeCollection } from '../common/textRangeCollection'; import { timingStats } from '../common/timing'; -import { ArgumentCategory, ArgumentNode, AssertNode, AssignmentExpressionNode, AssignmentNode, - AugmentedAssignmentNode, AwaitNode, BinaryOperationNode, BreakNode, CallNode, - ClassNode, ConstantNode, ContinueNode, DecoratorNode, DelNode, DictionaryEntryNode, - DictionaryExpandEntryNode, DictionaryKeyEntryNode, DictionaryNode, EllipsisNode, - ErrorExpressionCategory, ErrorNode, ExceptNode, ExpressionNode, extendRange, - FormatStringNode, ForNode, FunctionNode, getNextNodeId, GlobalNode, IfNode, ImportAsNode, - ImportFromAsNode, ImportFromNode, ImportNode, IndexItemsNode, IndexNode, LambdaNode, - ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionIterNode, - ListComprehensionNode, ListNode, MemberAccessNode, ModuleNameNode, - ModuleNode, NameNode, NonlocalNode, NumberNode, ParameterCategory, ParameterNode, - ParseNode, ParseNodeType, PassNode, RaiseNode, ReturnNode, SetNode, SliceNode, - StatementListNode, StatementNode, StringListNode, StringNode, SuiteNode, TernaryNode, - TryNode, TupleNode, TypeAnnotationNode, UnaryOperationNode, UnpackNode, - WhileNode, WithItemNode, WithNode, YieldFromNode, YieldNode } from './parseNodes'; +import { + ArgumentCategory, + ArgumentNode, + AssertNode, + AssignmentExpressionNode, + AssignmentNode, + AugmentedAssignmentNode, + AwaitNode, + BinaryOperationNode, + BreakNode, + CallNode, + ClassNode, + ConstantNode, + ContinueNode, + DecoratorNode, + DelNode, + DictionaryEntryNode, + DictionaryExpandEntryNode, + DictionaryKeyEntryNode, + DictionaryNode, + EllipsisNode, + ErrorExpressionCategory, + ErrorNode, + ExceptNode, + ExpressionNode, + extendRange, + FormatStringNode, + ForNode, + FunctionNode, + getNextNodeId, + GlobalNode, + IfNode, + ImportAsNode, + ImportFromAsNode, + ImportFromNode, + ImportNode, + IndexItemsNode, + IndexNode, + LambdaNode, + ListComprehensionForNode, + ListComprehensionIfNode, + ListComprehensionIterNode, + ListComprehensionNode, + ListNode, + MemberAccessNode, + ModuleNameNode, + ModuleNode, + NameNode, + NonlocalNode, + NumberNode, + ParameterCategory, + ParameterNode, + ParseNode, + ParseNodeType, + PassNode, + RaiseNode, + ReturnNode, + SetNode, + SliceNode, + StatementListNode, + StatementNode, + StringListNode, + StringNode, + SuiteNode, + TernaryNode, + TryNode, + TupleNode, + TypeAnnotationNode, + UnaryOperationNode, + UnpackNode, + WhileNode, + WithItemNode, + WithNode, + YieldFromNode, + YieldNode +} from './parseNodes'; import * as StringTokenUtils from './stringTokenUtils'; import { Tokenizer, TokenizerOutput } from './tokenizer'; -import { DedentToken, IdentifierToken, KeywordToken, KeywordType, NumberToken, OperatorToken, - OperatorType, StringToken, StringTokenFlags, Token, TokenType } from './tokenizerTypes'; +import { + DedentToken, + IdentifierToken, + KeywordToken, + KeywordType, + NumberToken, + OperatorToken, + OperatorType, + StringToken, + StringTokenFlags, + Token, + TokenType +} from './tokenizerTypes'; interface ExpressionListResult { list: ExpressionNode[]; @@ -95,12 +168,9 @@ export class Parser { private _containsWildcardImport = false; private _assignmentExpressionsAllowed = true; - parseSourceFile(fileContents: string, parseOptions: ParseOptions, - diagSink: DiagnosticSink): ParseResults { - + parseSourceFile(fileContents: string, parseOptions: ParseOptions, diagSink: DiagnosticSink): ParseResults { timingStats.tokenizeFileTime.timeOperation(() => { - this._startNewParse(fileContents, 0, fileContents.length, - parseOptions, diagSink); + this._startNewParse(fileContents, 0, fileContents.length, parseOptions, diagSink); }); const moduleNode = ModuleNode.create({ start: 0, length: fileContents.length }); @@ -139,9 +209,13 @@ export class Parser { }; } - parseTextExpression(fileContents: string, textOffset: number, textLength: number, - parseOptions: ParseOptions, parseTypeAnnotation: boolean): ParseExpressionTextResults { - + parseTextExpression( + fileContents: string, + textOffset: number, + textLength: number, + parseOptions: ParseOptions, + parseTypeAnnotation: boolean + ): ParseExpressionTextResults { const diagSink = new DiagnosticSink(); this._startNewParse(fileContents, textOffset, textLength, parseOptions, diagSink); @@ -169,9 +243,13 @@ export class Parser { }; } - private _startNewParse(fileContents: string, textOffset: number, textLength: number, - parseOptions: ParseOptions, diagSink: DiagnosticSink) { - + private _startNewParse( + fileContents: string, + textOffset: number, + textLength: number, + parseOptions: ParseOptions, + diagSink: DiagnosticSink + ) { this._fileContents = fileContents; this._parseOptions = parseOptions; this._diagSink = diagSink; @@ -240,8 +318,7 @@ export class Parser { return this._parseForStatement(asyncToken); } - this._addError('Expected "def", "with" or "for" to follow "async".', - asyncToken); + this._addError('Expected "def", "with" or "for" to follow "async".', asyncToken); return undefined; } @@ -353,12 +430,13 @@ export class Parser { let elseSuite: SuiteNode | undefined; if (!this._consumeTokenIfKeyword(KeywordType.In)) { - seqExpr = this._handleExpressionParseError( - ErrorExpressionCategory.MissingIn, 'Expected "in"'); + seqExpr = this._handleExpressionParseError(ErrorExpressionCategory.MissingIn, 'Expected "in"'); forSuite = SuiteNode.create(this._peekToken()); } else { seqExpr = this._parseTestListAsExpression( - ErrorExpressionCategory.MissingExpression, 'Expected expression after "in"'); + ErrorExpressionCategory.MissingExpression, + 'Expected expression after "in"' + ); forSuite = this._parseLoopSuite(); if (this._consumeTokenIfKeyword(KeywordType.Else)) { @@ -436,16 +514,14 @@ export class Parser { let seqExpr: ExpressionNode | undefined; if (!this._consumeTokenIfKeyword(KeywordType.In)) { - seqExpr = this._handleExpressionParseError( - ErrorExpressionCategory.MissingIn, 'Expected "in"'); + seqExpr = this._handleExpressionParseError(ErrorExpressionCategory.MissingIn, 'Expected "in"'); } else { this._disallowAssignmentExpression(() => { seqExpr = this._parseOrTest(); }); } - const compForNode = ListComprehensionForNode.create(asyncToken || forToken, - targetExpr, seqExpr!); + const compForNode = ListComprehensionForNode.create(asyncToken || forToken, targetExpr, seqExpr!); if (asyncToken) { compForNode.isAsync = true; @@ -473,9 +549,7 @@ export class Parser { private _parseWhileStatement(): WhileNode { const whileToken = this._getKeywordToken(KeywordType.While); - const whileNode = WhileNode.create(whileToken, - this._parseTestExpression(true), - this._parseLoopSuite()); + const whileNode = WhileNode.create(whileToken, this._parseTestExpression(true), this._parseLoopSuite()); if (this._consumeTokenIfKeyword(KeywordType.Else)) { whileNode.elseSuite = this._parseSuite(); @@ -533,8 +607,7 @@ export class Parser { sawCatchAllExcept = true; } else { if (sawCatchAllExcept) { - this._addError('A named except clause cannot appear after catch-all except clause', - typeExpr); + this._addError('A named except clause cannot appear after catch-all except clause', typeExpr); } } @@ -575,23 +648,22 @@ export class Parser { // funcdef: 'def' NAME parameters ['->' test] ':' suite // parameters: '(' [typedargslist] ')' - private _parseFunctionDef(asyncToken?: KeywordToken, decorators?: DecoratorNode[]): - FunctionNode | ErrorNode { - + private _parseFunctionDef(asyncToken?: KeywordToken, decorators?: DecoratorNode[]): FunctionNode | ErrorNode { const defToken = this._getKeywordToken(KeywordType.Def); const nameToken = this._getTokenIfIdentifier(); if (!nameToken) { this._addError('Expected function name after "def"', defToken); - return ErrorNode.create(defToken, - ErrorExpressionCategory.MissingFunctionParameterList); + return ErrorNode.create(defToken, ErrorExpressionCategory.MissingFunctionParameterList); } if (!this._consumeTokenIfType(TokenType.OpenParenthesis)) { this._addError('Expected "("', this._peekToken()); - return ErrorNode.create(nameToken, + return ErrorNode.create( + nameToken, ErrorExpressionCategory.MissingFunctionParameterList, - NameNode.create(nameToken)); + NameNode.create(nameToken) + ); } const paramList = this._parseVarArgsList(TokenType.CloseParenthesis, true); @@ -677,7 +749,7 @@ export class Parser { if (param.name) { const name = param.name.value; if (!paramMap.set(name, name)) { - this._addError(`Duplicate parameter '${ name }'`, param.name); + this._addError(`Duplicate parameter '${name}'`, param.name); } } @@ -807,8 +879,7 @@ export class Parser { extendRange(paramNode, paramNode.defaultValue); if (starCount > 0) { - this._addError(`Parameter with '*' or '**' cannot have default value`, - paramNode.defaultValue); + this._addError(`Parameter with '*' or '**' cannot have default value`, paramNode.defaultValue); } } @@ -913,7 +984,8 @@ export class Parser { } else { callNameExpr = ErrorNode.create( this._peekToken(), - ErrorExpressionCategory.MissingDecoratorCallName); + ErrorExpressionCategory.MissingDecoratorCallName + ); } break; } @@ -1004,8 +1076,7 @@ export class Parser { const breakToken = this._getKeywordToken(KeywordType.Break); if (!this._isInLoop) { - this._addError('"break" can be used only within a loop', - breakToken); + this._addError('"break" can be used only within a loop', breakToken); } return BreakNode.create(breakToken); @@ -1015,11 +1086,9 @@ export class Parser { const continueToken = this._getKeywordToken(KeywordType.Continue); if (!this._isInLoop) { - this._addError('"continue" can be used only within a loop', - continueToken); + this._addError('"continue" can be used only within a loop', continueToken); } else if (this._isInFinally) { - this._addError('"continue" cannot be used within a finally clause', - continueToken); + this._addError('"continue" cannot be used within a finally clause', continueToken); } return ContinueNode.create(continueToken); @@ -1032,9 +1101,11 @@ export class Parser { const returnNode = ReturnNode.create(returnToken); if (!this._isNextTokenNeverExpression()) { - const returnExpr = this._parseTestOrStarListAsExpression(true, + const returnExpr = this._parseTestOrStarListAsExpression( + true, ErrorExpressionCategory.MissingExpression, - 'Expected expression after "return"'); + 'Expected expression after "return"' + ); this._reportConditionalErrorForStarTupleElement(returnExpr); returnNode.returnExpression = returnExpr; returnNode.returnExpression.parent = returnNode; @@ -1056,9 +1127,8 @@ export class Parser { // Handle imports from __future__ specially because they can // change the way we interpret the rest of the file. - const isFutureImport = modName.leadingDots === 0 && - modName.nameParts.length === 1 && - modName.nameParts[0].value === '__future__'; + const isFutureImport = + modName.leadingDots === 0 && modName.nameParts.length === 1 && modName.nameParts[0].value === '__future__'; const possibleInputToken = this._peekToken(); if (!this._consumeTokenIfKeyword(KeywordType.Import)) { @@ -1351,18 +1421,18 @@ export class Parser { const nextToken = this._peekToken(); if (this._consumeTokenIfKeyword(KeywordType.From)) { if (this._getLanguageVersion() < PythonVersion.V33) { - this._addError( - `Use of 'yield from' requires Python 3.3 or newer`, - nextToken); + this._addError(`Use of 'yield from' requires Python 3.3 or newer`, nextToken); } return YieldFromNode.create(yieldToken, this._parseTestExpression(true)); } let exprList: ExpressionNode | undefined; if (!this._isNextTokenNeverExpression()) { - exprList = this._parseTestOrStarListAsExpression(true, + exprList = this._parseTestOrStarListAsExpression( + true, ErrorExpressionCategory.MissingExpression, - 'Expected expression in yield statement'); + 'Expected expression in yield statement' + ); this._reportConditionalErrorForStarTupleElement(exprList); } @@ -1389,7 +1459,7 @@ export class Parser { // Remove any non-printable characters. const cleanedText = text.replace(/[\S\W]/g, ''); - this._addError(`Invalid character in token: "${ cleanedText }"`, invalidToken); + this._addError(`Invalid character in token: "${cleanedText}"`, invalidToken); this._consumeTokensUntilType(TokenType.NewLine); break; } @@ -1417,8 +1487,7 @@ export class Parser { } if (!this._consumeTokenIfType(TokenType.NewLine)) { - this._addError('Statements must be separated by newlines or semicolons', - this._peekToken()); + this._addError('Statements must be separated by newlines or semicolons', this._peekToken()); } return statement; @@ -1478,8 +1547,8 @@ export class Parser { // To accommodate empty tuples ("()"), we will reach back to get // the opening parenthesis as the opening token. - const tupleStartRange: TextRange = exprListResult.list.length > 0 ? - exprListResult.list[0] : this._peekToken(-1); + const tupleStartRange: TextRange = + exprListResult.list.length > 0 ? exprListResult.list[0] : this._peekToken(-1); const tupleNode = TupleNode.create(tupleStartRange); tupleNode.expressions = exprListResult.list; @@ -1493,9 +1562,7 @@ export class Parser { return tupleNode; } - private _parseTestListAsExpression(errorCategory: ErrorExpressionCategory, - errorString: string): ExpressionNode { - + private _parseTestListAsExpression(errorCategory: ErrorExpressionCategory, errorString: string): ExpressionNode { if (this._isNextTokenNeverExpression()) { return this._handleExpressionParseError(errorCategory, errorString); } @@ -1507,9 +1574,11 @@ export class Parser { return this._makeExpressionOrTuple(exprListResult); } - private _parseTestOrStarListAsExpression(allowAssignmentExpression: boolean, - errorCategory: ErrorExpressionCategory, errorString: string): ExpressionNode { - + private _parseTestOrStarListAsExpression( + allowAssignmentExpression: boolean, + errorCategory: ErrorExpressionCategory, + errorString: string + ): ExpressionNode { if (this._isNextTokenNeverExpression()) { return this._handleExpressionParseError(errorCategory, errorString); } @@ -1531,8 +1600,9 @@ export class Parser { } private _parseTestOrStarExpressionList(allowAssignmentExpression: boolean): ExpressionListResult { - const exprListResult = this._parseExpressionListGeneric( - () => this._parseTestOrStarExpression(allowAssignmentExpression)); + const exprListResult = this._parseExpressionListGeneric(() => + this._parseTestOrStarExpression(allowAssignmentExpression) + ); if (!exprListResult.parseError) { // Make sure that we don't have more than one star expression in the list. @@ -1579,8 +1649,7 @@ export class Parser { return this._parseLambdaExpression(); } - const ifExpr = allowAssignmentExpression ? - this._parseAssignmentExpression() : this._parseOrTest(); + const ifExpr = allowAssignmentExpression ? this._parseAssignmentExpression() : this._parseOrTest(); if (ifExpr.nodeType === ParseNodeType.Error) { return ifExpr; } @@ -1595,8 +1664,7 @@ export class Parser { } if (!this._consumeTokenIfKeyword(KeywordType.Else)) { - return this._handleExpressionParseError( - ErrorExpressionCategory.MissingElse, 'Expected "else"'); + return this._handleExpressionParseError(ErrorExpressionCategory.MissingElse, 'Expected "else"'); } const elseExpr = this._parseTestExpression(true); @@ -1624,15 +1692,11 @@ export class Parser { } if (!this._assignmentExpressionsAllowed) { - this._addError( - `Operator ':=' not allowed in this context`, - walrusToken); + this._addError(`Operator ':=' not allowed in this context`, walrusToken); } if (this._getLanguageVersion() < PythonVersion.V38) { - this._addError( - `Operator ':=' requires Python 3.8 or newer`, - walrusToken); + this._addError(`Operator ':=' requires Python 3.8 or newer`, walrusToken); } let rightExpr: ExpressionNode; @@ -1710,8 +1774,10 @@ export class Parser { } } else if (this._peekKeywordType() === KeywordType.Not) { const tokenAfterNot = this._peekToken(1); - if (tokenAfterNot.type === TokenType.Keyword && - (tokenAfterNot as KeywordToken).keywordType === KeywordType.In) { + if ( + tokenAfterNot.type === TokenType.Keyword && + (tokenAfterNot as KeywordToken).keywordType === KeywordType.In + ) { this._getNextToken(); this._getNextToken(); comparisonOperator = OperatorType.NotIn; @@ -1822,11 +1888,13 @@ export class Parser { } let nextOperator = this._peekOperatorType(); - while (nextOperator === OperatorType.Multiply || - nextOperator === OperatorType.MatrixMultiply || - nextOperator === OperatorType.Divide || - nextOperator === OperatorType.Mod || - nextOperator === OperatorType.FloorDivide) { + while ( + nextOperator === OperatorType.Multiply || + nextOperator === OperatorType.MatrixMultiply || + nextOperator === OperatorType.Divide || + nextOperator === OperatorType.Mod || + nextOperator === OperatorType.FloorDivide + ) { this._getNextToken(); const rightExpr = this._parseArithmeticFactor(); leftExpr = BinaryOperationNode.create(leftExpr, rightExpr, nextOperator); @@ -1841,9 +1909,11 @@ export class Parser { private _parseArithmeticFactor(): ExpressionNode { const nextToken = this._peekToken(); const nextOperator = this._peekOperatorType(); - if (nextOperator === OperatorType.Add || - nextOperator === OperatorType.Subtract || - nextOperator === OperatorType.BitwiseInvert) { + if ( + nextOperator === OperatorType.Add || + nextOperator === OperatorType.Subtract || + nextOperator === OperatorType.BitwiseInvert + ) { this._getNextToken(); const expression = this._parseArithmeticFactor(); return UnaryOperationNode.create(nextToken, expression, nextOperator); @@ -1869,9 +1939,7 @@ export class Parser { if (this._peekKeywordType() === KeywordType.Await && !this._isParsingTypeAnnotation) { awaitToken = this._getKeywordToken(KeywordType.Await); if (this._getLanguageVersion() < PythonVersion.V35) { - this._addError( - `Use of 'await' requires Python 3.5 or newer`, - awaitToken); + this._addError(`Use of 'await' requires Python 3.5 or newer`, awaitToken); } } @@ -1917,9 +1985,7 @@ export class Parser { if (atomExpression.nodeType === ParseNodeType.Name && atomExpression.value === 'type') { diag.addMessage('Use Type[T] instead'); } - this._addError( - `Function call not allowed in type annotation` + diag.getString(), - callNode); + this._addError(`Function call not allowed in type annotation` + diag.getString(), callNode); } atomExpression = callNode; @@ -1930,8 +1996,7 @@ export class Parser { // type annotations properly. We need to suspend treating strings as // type annotations within a Literal subscript. const isLiteralSubscript = - (atomExpression.nodeType === ParseNodeType.Name && - atomExpression.value === 'Literal') || + (atomExpression.nodeType === ParseNodeType.Name && atomExpression.value === 'Literal') || (atomExpression.nodeType === ParseNodeType.MemberAccess && atomExpression.leftExpression.nodeType === ParseNodeType.Name && atomExpression.leftExpression.value === 'typing' && @@ -1960,7 +2025,9 @@ export class Parser { if (!this._consumeTokenIfType(TokenType.CloseBracket)) { return this._handleExpressionParseError( ErrorExpressionCategory.MissingIndexCloseBracket, - 'Expected "]"', indexNode); + 'Expected "]"', + indexNode + ); } atomExpression = indexNode; @@ -1970,10 +2037,11 @@ export class Parser { if (!memberName) { return this._handleExpressionParseError( ErrorExpressionCategory.MissingMemberAccessName, - 'Expected member name after "."', atomExpression); + 'Expected member name after "."', + atomExpression + ); } - atomExpression = MemberAccessNode.create( - atomExpression, NameNode.create(memberName)); + atomExpression = MemberAccessNode.create(atomExpression, NameNode.create(memberName)); } else { break; } @@ -1988,14 +2056,17 @@ export class Parser { // subscriptlist: subscript (',' subscript)* [','] private _parseSubscriptList(): ExpressionNode { - const listResult = this._parseExpressionListGeneric(() => this._parseSubscript(), () => { - // Override the normal terminal check to exclude colons, - // which are a valid way to start subscription expressions. - if (this._peekTokenType() === TokenType.Colon) { - return false; + const listResult = this._parseExpressionListGeneric( + () => this._parseSubscript(), + () => { + // Override the normal terminal check to exclude colons, + // which are a valid way to start subscription expressions. + if (this._peekTokenType() === TokenType.Colon) { + return false; + } + return this._isNextTokenNeverExpression(); } - return this._isNextTokenNeverExpression(); - }); + ); if (listResult.parseError) { return listResult.parseError; @@ -2004,7 +2075,8 @@ export class Parser { if (listResult.list.length === 0) { return this._handleExpressionParseError( ErrorExpressionCategory.MissingIndexOrSlice, - 'Expected index or slice expression'); + 'Expected index or slice expression' + ); } return this._makeExpressionOrTuple(listResult); @@ -2020,8 +2092,7 @@ export class Parser { while (true) { const nextTokenType = this._peekTokenType(); - if (nextTokenType === TokenType.CloseBracket || - nextTokenType === TokenType.Comma) { + if (nextTokenType === TokenType.CloseBracket || nextTokenType === TokenType.Comma) { break; } @@ -2069,9 +2140,11 @@ export class Parser { while (true) { const nextTokenType = this._peekTokenType(); - if (nextTokenType === TokenType.CloseParenthesis || - nextTokenType === TokenType.NewLine || - nextTokenType === TokenType.EndOfStream) { + if ( + nextTokenType === TokenType.CloseParenthesis || + nextTokenType === TokenType.NewLine || + nextTokenType === TokenType.EndOfStream + ) { break; } @@ -2079,9 +2152,7 @@ export class Parser { if (arg.name) { sawKeywordArg = true; } else if (sawKeywordArg && arg.argumentCategory === ArgumentCategory.Simple) { - this._addError( - 'Positional argument cannot appear after named arguments', - arg); + this._addError('Positional argument cannot appear after named arguments', arg); } argList.push(arg); @@ -2151,9 +2222,7 @@ export class Parser { if (nextToken.type === TokenType.Number) { const numberNode = NumberNode.create(this._getNextToken() as NumberToken); if (this._isParsingTypeAnnotation) { - this._addError( - 'Numeric literal not allowed in type annotation', - numberNode); + this._addError('Numeric literal not allowed in type annotation', numberNode); } return numberNode; } @@ -2174,11 +2243,14 @@ export class Parser { // and emit an error. this._addError( 'Expressions surrounded by backticks are not supported in Python 3.x; use repr instead', - nextToken); - - const expressionNode = this._parseTestListAsExpression(ErrorExpressionCategory.MissingExpression, - 'Expected expression'); - + nextToken + ); + + const expressionNode = this._parseTestListAsExpression( + ErrorExpressionCategory.MissingExpression, + 'Expected expression' + ); + this._consumeTokenIfType(TokenType.Backtick); return expressionNode; } @@ -2191,9 +2263,7 @@ export class Parser { // a zero-length tuple. const diag = new DiagnosticAddendum(); diag.addMessage('Use Tuple[T1, ..., Tn] instead'); - this._addError( - 'Tuple expression not allowed in type annotation' + diag.getString(), - tupleNode); + this._addError('Tuple expression not allowed in type annotation' + diag.getString(), tupleNode); } return tupleNode; } else if (nextToken.type === TokenType.OpenBracket) { @@ -2201,9 +2271,7 @@ export class Parser { if (this._isParsingTypeAnnotation && !this._isParsingIndexTrailer) { const diag = new DiagnosticAddendum(); diag.addMessage('Use List[T] instead'); - this._addError( - 'List expression not allowed in type annotation' + diag.getString(), - listNode); + this._addError('List expression not allowed in type annotation' + diag.getString(), listNode); } return listNode; } else if (nextToken.type === TokenType.OpenCurlyBrace) { @@ -2211,27 +2279,24 @@ export class Parser { if (this._isParsingTypeAnnotation) { const diag = new DiagnosticAddendum(); diag.addMessage('Use Dict[T1, T2] instead'); - this._addError( - 'Dictionary expression not allowed in type annotation' + diag.getString(), - dictNode); + this._addError('Dictionary expression not allowed in type annotation' + diag.getString(), dictNode); } return dictNode; } if (nextToken.type === TokenType.Keyword) { const keywordToken = nextToken as KeywordToken; - if (keywordToken.keywordType === KeywordType.False || - keywordToken.keywordType === KeywordType.True || - keywordToken.keywordType === KeywordType.Debug || - keywordToken.keywordType === KeywordType.None) { - + if ( + keywordToken.keywordType === KeywordType.False || + keywordToken.keywordType === KeywordType.True || + keywordToken.keywordType === KeywordType.Debug || + keywordToken.keywordType === KeywordType.None + ) { const constNode = ConstantNode.create(this._getNextToken() as KeywordToken); if (this._isParsingTypeAnnotation) { if (keywordToken.keywordType !== KeywordType.None) { - this._addError( - 'Keyword not allowed in type annotation', - constNode); + this._addError('Keyword not allowed in type annotation', constNode); } } @@ -2245,18 +2310,18 @@ export class Parser { } } - return this._handleExpressionParseError( - ErrorExpressionCategory.MissingExpression, - 'Expected expression'); + return this._handleExpressionParseError(ErrorExpressionCategory.MissingExpression, 'Expected expression'); } // Allocates a dummy "error expression" and consumes the remainder // of the tokens on the line for error recovery. A partially-completed // child node can be passed to help the completion provider determine // what to do. - private _handleExpressionParseError(category: ErrorExpressionCategory, - errorMsg: string, childNode?: ExpressionNode): ErrorNode { - + private _handleExpressionParseError( + category: ErrorExpressionCategory, + errorMsg: string, + childNode?: ExpressionNode + ): ErrorNode { this._addError(errorMsg, this._peekToken()); const expr = ErrorNode.create(this._peekToken(), category, childNode); this._consumeTokensUntilType(TokenType.NewLine); @@ -2305,9 +2370,7 @@ export class Parser { const yieldExpr = this._tryParseYieldExpression(); if (yieldExpr) { if (this._peekTokenType() !== TokenType.CloseParenthesis) { - return this._handleExpressionParseError( - ErrorExpressionCategory.MissingTupleCloseParen, - 'Expected ")"'); + return this._handleExpressionParseError(ErrorExpressionCategory.MissingTupleCloseParen, 'Expected ")"'); } else { extendRange(yieldExpr, this._getNextToken()); } @@ -2319,9 +2382,7 @@ export class Parser { const tupleOrExpression = this._makeExpressionOrTuple(exprListResult); if (this._peekTokenType() !== TokenType.CloseParenthesis) { - return this._handleExpressionParseError( - ErrorExpressionCategory.MissingTupleCloseParen, - 'Expected ")"'); + return this._handleExpressionParseError(ErrorExpressionCategory.MissingTupleCloseParen, 'Expected ")"'); } else { extendRange(tupleOrExpression, this._getNextToken()); } @@ -2338,9 +2399,7 @@ export class Parser { const exprListResult = this._parseTestListWithComprehension(); const closeBracket: Token | undefined = this._peekToken(); if (!this._consumeTokenIfType(TokenType.CloseBracket)) { - return this._handleExpressionParseError( - ErrorExpressionCategory.MissingListCloseBracket, - 'Expected "]"'); + return this._handleExpressionParseError(ErrorExpressionCategory.MissingListCloseBracket, 'Expected "]"'); } const listAtom = ListNode.create(startBracket); @@ -2358,17 +2417,19 @@ export class Parser { private _parseTestListWithComprehension(): ExpressionListResult { let sawComprehension = false; - return this._parseExpressionListGeneric(() => { - let expr = this._parseTestOrStarExpression(true); - const listComp = this._tryParseListComprehension(expr); - if (listComp) { - expr = listComp; - sawComprehension = true; - } - return expr; - }, - () => this._isNextTokenNeverExpression(), - () => sawComprehension); + return this._parseExpressionListGeneric( + () => { + let expr = this._parseTestOrStarExpression(true); + const listComp = this._tryParseListComprehension(expr); + if (listComp) { + expr = listComp; + sawComprehension = true; + } + return expr; + }, + () => this._isNextTokenNeverExpression(), + () => sawComprehension + ); } // '{' [dictorsetmaker] '}' @@ -2501,11 +2562,11 @@ export class Parser { return dictionaryAtom; } - private _parseExpressionListGeneric(parser: () => ExpressionNode, - terminalCheck: () => boolean = () => this._isNextTokenNeverExpression(), - finalEntryCheck: () => boolean = () => false): - ExpressionListResult { - + private _parseExpressionListGeneric( + parser: () => ExpressionNode, + terminalCheck: () => boolean = () => this._isNextTokenNeverExpression(), + finalEntryCheck: () => boolean = () => false + ): ExpressionListResult { let trailingComma = false; const list: ExpressionNode[] = []; let parseError: ErrorNode | undefined; @@ -2545,8 +2606,11 @@ export class Parser { // augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | // '<<=' | '>>=' | '**=' | '//=') private _parseExpressionStatement(): ExpressionNode { - let leftExpr = this._parseTestOrStarListAsExpression(false, - ErrorExpressionCategory.MissingExpression, 'Expected expression'); + let leftExpr = this._parseTestOrStarListAsExpression( + false, + ErrorExpressionCategory.MissingExpression, + 'Expected expression' + ); let annotationExpr: ExpressionNode | undefined; if (leftExpr.nodeType === ParseNodeType.Error) { @@ -2560,8 +2624,7 @@ export class Parser { leftExpr = TypeAnnotationNode.create(leftExpr, annotationExpr); if (!this._parseOptions.isStubFile && this._getLanguageVersion() < PythonVersion.V36) { - this._addError('Type annotations for variables requires Python 3.6 or newer', - annotationExpr); + this._addError('Type annotations for variables requires Python 3.6 or newer', annotationExpr); } }); @@ -2581,17 +2644,18 @@ export class Parser { if (!annotationExpr && Tokenizer.isOperatorAssignment(this._peekOperatorType())) { const operatorToken = this._getNextToken() as OperatorToken; - const rightExpr = this._tryParseYieldExpression() || + const rightExpr = + this._tryParseYieldExpression() || this._parseTestListAsExpression( ErrorExpressionCategory.MissingExpression, - 'Expected expression to the right of operator'); + 'Expected expression to the right of operator' + ); // Make a shallow copy of the dest expression but give it a new ID. const destExpr = Object.assign({}, leftExpr); destExpr.id = getNextNodeId(); - return AugmentedAssignmentNode.create(leftExpr, rightExpr, - operatorToken.operatorType, destExpr); + return AugmentedAssignmentNode.create(leftExpr, rightExpr, operatorToken.operatorType, destExpr); } return leftExpr; @@ -2601,9 +2665,11 @@ export class Parser { let rightExpr: ExpressionNode | undefined; rightExpr = this._tryParseYieldExpression(); if (!rightExpr) { - rightExpr = this._parseTestOrStarListAsExpression(false, + rightExpr = this._parseTestOrStarListAsExpression( + false, ErrorExpressionCategory.MissingExpression, - 'Expected expression to the right of "="'); + 'Expected expression to the right of "="' + ); } if (rightExpr.nodeType === ParseNodeType.Error) { @@ -2683,8 +2749,7 @@ export class Parser { return undefined; } - const interTokenContents = this._fileContents!.substring( - curToken.start + curToken.length, nextToken.start); + const interTokenContents = this._fileContents!.substring(curToken.start + curToken.length, nextToken.start); const commentRegEx = /^(\s*#\s*type:\s*)([^\r\n]*)/; const match = interTokenContents.match(commentRegEx); if (!match) { @@ -2700,14 +2765,25 @@ export class Parser { } const tokenOffset = curToken.start + curToken.length + match[1].length; - const stringToken = StringToken.create(tokenOffset, - typeString.length, StringTokenFlags.None, typeString, 0, undefined); + const stringToken = StringToken.create( + tokenOffset, + typeString.length, + StringTokenFlags.None, + typeString, + 0, + undefined + ); const stringNode = this._makeStringNode(stringToken); const stringListNode = StringListNode.create([stringNode]); const parser = new Parser(); - const parseResults = parser.parseTextExpression(this._fileContents!, - tokenOffset, typeString.length, this._parseOptions, true); + const parseResults = parser.parseTextExpression( + this._fileContents!, + tokenOffset, + typeString.length, + this._parseOptions, + true + ); parseResults.diagnostics.forEach(diag => { this._addError(diag.message, stringListNode); @@ -2734,17 +2810,23 @@ export class Parser { // contains formatting directives that start with a ! or :. const segmentExprLength = this._getFormatStringExpressionLength(segment.value); - const parseResults = parser.parseTextExpression(this._fileContents!, - stringToken.start + stringToken.prefixLength + stringToken.quoteMarkLength + - segment.offset, segmentExprLength, this._parseOptions, false); + const parseResults = parser.parseTextExpression( + this._fileContents!, + stringToken.start + stringToken.prefixLength + stringToken.quoteMarkLength + segment.offset, + segmentExprLength, + this._parseOptions, + false + ); parseResults.diagnostics.forEach(diag => { - const textRangeStart = (diag.range ? - convertPositionToOffset(diag.range.start, parseResults.lines) : - stringToken.start) || stringToken.start; - const textRangeEnd = (diag.range ? - (convertPositionToOffset(diag.range.end, parseResults.lines) || 0) + 1 : - stringToken.start + stringToken.length) || (stringToken.start + stringToken.length); + const textRangeStart = + (diag.range + ? convertPositionToOffset(diag.range.start, parseResults.lines) + : stringToken.start) || stringToken.start; + const textRangeEnd = + (diag.range + ? (convertPositionToOffset(diag.range.end, parseResults.lines) || 0) + 1 + : stringToken.start + stringToken.length) || stringToken.start + stringToken.length; const textRange = { start: textRangeStart, length: textRangeEnd - textRangeStart }; this._addError(diag.message, textRange); }); @@ -2755,8 +2837,12 @@ export class Parser { } } - return FormatStringNode.create(stringToken, unescapedResult.value, - unescapedResult.unescapeErrors.length > 0, formatExpressions); + return FormatStringNode.create( + stringToken, + unescapedResult.value, + unescapedResult.unescapeErrors.length > 0, + formatExpressions + ); } private _getFormatStringExpressionLength(segmentValue: string): number { @@ -2774,8 +2860,8 @@ export class Parser { while (segmentExprLength < segmentValue.length) { const curChar = segmentValue[segmentExprLength]; - const ignoreSeparator = inSingleQuote || inDoubleQuote || braceCount > 0 || - parenCount > 0 || bracketCount > 0; + const ignoreSeparator = + inSingleQuote || inDoubleQuote || braceCount > 0 || parenCount > 0 || bracketCount > 0; const inString = inSingleQuote || inDoubleQuote; if (curChar === '=') { @@ -2788,12 +2874,14 @@ export class Parser { } else if (curChar === '!') { if (!ignoreSeparator) { // Allow !=, as per PEP 498 - if (segmentExprLength === segmentValue.length - 1 || - segmentValue[segmentExprLength + 1] !== '=') { + if ( + segmentExprLength === segmentValue.length - 1 || + segmentValue[segmentExprLength + 1] !== '=' + ) { break; } } - } else if (curChar === '\'') { + } else if (curChar === "'") { if (!inDoubleQuote) { inSingleQuote = !inSingleQuote; } @@ -2879,8 +2967,13 @@ export class Parser { this._addError('Type hints cannot contain escape characters', stringNode); } else { const parser = new Parser(); - const parseResults = parser.parseTextExpression(this._fileContents!, - tokenOffset + prefixLength, unescapedString.length, this._parseOptions, true); + const parseResults = parser.parseTextExpression( + this._fileContents!, + tokenOffset + prefixLength, + unescapedString.length, + this._parseOptions, + true + ); parseResults.diagnostics.forEach(diag => { this._addError(diag.message, stringNode); @@ -2997,8 +3090,7 @@ export class Parser { } if (this._tokenIndex + count >= this._tokenizerOutput!.tokens.count) { - return this._tokenizerOutput!.tokens.getItemAt( - this._tokenizerOutput!.tokens.count - 1); + return this._tokenizerOutput!.tokens.getItemAt(this._tokenizerOutput!.tokens.count - 1); } return this._tokenizerOutput!.tokens.getItemAt(this._tokenIndex + count); @@ -3036,8 +3128,7 @@ export class Parser { if (nextToken.type === TokenType.Invalid) { this._getNextToken(); this._addError(`Invalid character in identifier`, nextToken); - return IdentifierToken.create(nextToken.start, - nextToken.length, '', nextToken.comments); + return IdentifierToken.create(nextToken.start, nextToken.length, '', nextToken.comments); } // If keywords are allowed in this context, convert the keyword @@ -3047,8 +3138,7 @@ export class Parser { if (!disallowedKeywords.find(type => type === keywordType)) { const keywordText = this._fileContents!.substr(nextToken.start, nextToken.length); this._getNextToken(); - return IdentifierToken.create(nextToken.start, - nextToken.length, keywordText, nextToken.comments); + return IdentifierToken.create(nextToken.start, nextToken.length, keywordText, nextToken.comments); } } @@ -3113,8 +3203,9 @@ export class Parser { private _addError(message: string, range: TextRange) { assert(range !== undefined); - this._diagSink.addError(message, - convertOffsetsToRange(range.start, range.start + range.length, - this._tokenizerOutput!.lines)); + this._diagSink.addError( + message, + convertOffsetsToRange(range.start, range.start + range.length, this._tokenizerOutput!.lines) + ); } } diff --git a/server/src/parser/stringTokenUtils.ts b/server/src/parser/stringTokenUtils.ts index c8f290c8f..e2be1cc71 100644 --- a/server/src/parser/stringTokenUtils.ts +++ b/server/src/parser/stringTokenUtils.ts @@ -1,12 +1,12 @@ /* -* stringTokenUtils.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Methods that handle unescaping of escaped string token -* literal values. -*/ + * stringTokenUtils.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Methods that handle unescaping of escaped string token + * literal values. + */ import Char from 'typescript-char'; @@ -124,7 +124,7 @@ export function getUnescapedString(stringToken: StringToken): UnescapedString { const char = String.fromCharCode(charCode); output.value += char; formatSegment.value += char; - } + }; while (true) { let curChar = getEscapedCharacter(); @@ -378,7 +378,7 @@ export function getUnescapedString(stringToken: StringToken): UnescapedString { if (strChar === Char.EndOfText) { break; } - + if (strChar === Char.Backslash) { appendOutputChar(strChar); strOffset++; @@ -386,11 +386,11 @@ export function getUnescapedString(stringToken: StringToken): UnescapedString { appendOutputChar(strChar); continue; } - + if (strChar === Char.LineFeed || strChar === Char.CarriageReturn) { break; } - + if (strChar === quoteChar) { if (!isTriplicate) { strOffset++; diff --git a/server/src/parser/tokenizer.ts b/server/src/parser/tokenizer.ts index 6d1f722a1..40b3bddd5 100644 --- a/server/src/parser/tokenizer.ts +++ b/server/src/parser/tokenizer.ts @@ -1,14 +1,14 @@ /* -* tokenizer.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Based on code from vscode-python repository: -* https://github.com/Microsoft/vscode-python -* -* Converts a Python program text stream into a stream of tokens. -*/ + * tokenizer.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Based on code from vscode-python repository: + * https://github.com/Microsoft/vscode-python + * + * Converts a Python program text stream into a stream of tokens. + */ import Char from 'typescript-char'; @@ -16,47 +16,62 @@ import { TextRange } from '../common/textRange'; import { TextRangeCollection } from '../common/textRangeCollection'; import { isBinary, isDecimal, isHex, isIdentifierChar, isIdentifierStartChar, isOctal } from './characters'; import { CharacterStream } from './characterStream'; -import { Comment, DedentToken, IdentifierToken, IndentToken, KeywordToken, - KeywordType, NewLineToken, NewLineType, NumberToken, OperatorFlags, OperatorToken, - OperatorType, StringToken, StringTokenFlags, Token, TokenType } from './tokenizerTypes'; +import { + Comment, + DedentToken, + IdentifierToken, + IndentToken, + KeywordToken, + KeywordType, + NewLineToken, + NewLineType, + NumberToken, + OperatorFlags, + OperatorToken, + OperatorType, + StringToken, + StringTokenFlags, + Token, + TokenType +} from './tokenizerTypes'; const _keywords: { [key: string]: KeywordType } = { - 'and': KeywordType.And, - 'as': KeywordType.As, - 'assert': KeywordType.Assert, - 'async': KeywordType.Async, - 'await': KeywordType.Await, - 'break': KeywordType.Break, - 'class': KeywordType.Class, - 'continue': KeywordType.Continue, - '__debug__': KeywordType.Debug, - 'def': KeywordType.Def, - 'del': KeywordType.Del, - 'elif': KeywordType.Elif, - 'else': KeywordType.Else, - 'except': KeywordType.Except, - 'finally': KeywordType.Finally, - 'for': KeywordType.For, - 'from': KeywordType.From, - 'global': KeywordType.Global, - 'if': KeywordType.If, - 'import': KeywordType.Import, - 'in': KeywordType.In, - 'is': KeywordType.Is, - 'lambda': KeywordType.Lambda, - 'nonlocal': KeywordType.Nonlocal, - 'not': KeywordType.Not, - 'or': KeywordType.Or, - 'pass': KeywordType.Pass, - 'raise': KeywordType.Raise, - 'return': KeywordType.Return, - 'try': KeywordType.Try, - 'while': KeywordType.While, - 'with': KeywordType.With, - 'yield': KeywordType.Yield, - 'False': KeywordType.False, - 'None': KeywordType.None, - 'True': KeywordType.True + and: KeywordType.And, + as: KeywordType.As, + assert: KeywordType.Assert, + async: KeywordType.Async, + await: KeywordType.Await, + break: KeywordType.Break, + class: KeywordType.Class, + continue: KeywordType.Continue, + __debug__: KeywordType.Debug, + def: KeywordType.Def, + del: KeywordType.Del, + elif: KeywordType.Elif, + else: KeywordType.Else, + except: KeywordType.Except, + finally: KeywordType.Finally, + for: KeywordType.For, + from: KeywordType.From, + global: KeywordType.Global, + if: KeywordType.If, + import: KeywordType.Import, + in: KeywordType.In, + is: KeywordType.Is, + lambda: KeywordType.Lambda, + nonlocal: KeywordType.Nonlocal, + not: KeywordType.Not, + or: KeywordType.Or, + pass: KeywordType.Pass, + raise: KeywordType.Raise, + return: KeywordType.Return, + try: KeywordType.Try, + while: KeywordType.While, + with: KeywordType.With, + yield: KeywordType.Yield, + False: KeywordType.False, + None: KeywordType.None, + True: KeywordType.True }; const _operatorInfo: { [key: number]: OperatorFlags } = { @@ -104,7 +119,7 @@ const _operatorInfo: { [key: number]: OperatorFlags } = { [OperatorType.NotIn]: OperatorFlags.Binary }; -const _byteOrderMarker = 0xFEFF; +const _byteOrderMarker = 0xfeff; export interface TokenizerOutput { // List of all tokens. @@ -229,8 +244,7 @@ export class Tokenizer { } else if (this._indentCount > 0) { // Compute the average number of spaces per indent // to estimate the predominant tab value. - let averageSpacePerIndent = Math.round( - this._indentSpacesTotal / this._indentCount); + let averageSpacePerIndent = Math.round(this._indentSpacesTotal / this._indentCount); if (averageSpacePerIndent < 1) { averageSpacePerIndent = 1; } else if (averageSpacePerIndent > 8) { @@ -249,7 +263,7 @@ export class Tokenizer { typeIgnoreAll: this._typeIgnoreAll, predominantEndOfLineSequence, predominantTabSequence, - predominantSingleQuoteCharacter: this._singleQuoteCount >= this._doubleQuoteCount ? '\'' : '"' + predominantSingleQuoteCharacter: this._singleQuoteCount >= this._doubleQuoteCount ? "'" : '"' }; } @@ -319,8 +333,7 @@ export class Tokenizer { case Char.CarriageReturn: { const length = this._cs.nextChar === Char.LineFeed ? 2 : 1; - const newLineType = length === 2 ? - NewLineType.CarriageReturnLineFeed : NewLineType.CarriageReturn; + const newLineType = length === 2 ? NewLineType.CarriageReturnLineFeed : NewLineType.CarriageReturn; this._handleNewLine(length, newLineType); return true; } @@ -350,8 +363,7 @@ export class Tokenizer { case Char.OpenParenthesis: { this._parenDepth++; - this._tokens.push(Token.create(TokenType.OpenParenthesis, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.OpenParenthesis, this._cs.position, 1, this._getComments())); break; } @@ -359,15 +371,13 @@ export class Tokenizer { if (this._parenDepth > 0) { this._parenDepth--; } - this._tokens.push(Token.create(TokenType.CloseParenthesis, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.CloseParenthesis, this._cs.position, 1, this._getComments())); break; } case Char.OpenBracket: { this._parenDepth++; - this._tokens.push(Token.create(TokenType.OpenBracket, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.OpenBracket, this._cs.position, 1, this._getComments())); break; } @@ -375,15 +385,13 @@ export class Tokenizer { if (this._parenDepth > 0) { this._parenDepth--; } - this._tokens.push(Token.create(TokenType.CloseBracket, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.CloseBracket, this._cs.position, 1, this._getComments())); break; } case Char.OpenBrace: { this._parenDepth++; - this._tokens.push(Token.create(TokenType.OpenCurlyBrace, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.OpenCurlyBrace, this._cs.position, 1, this._getComments())); break; } @@ -391,38 +399,34 @@ export class Tokenizer { if (this._parenDepth > 0) { this._parenDepth--; } - this._tokens.push(Token.create(TokenType.CloseCurlyBrace, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.CloseCurlyBrace, this._cs.position, 1, this._getComments())); break; } case Char.Comma: { - this._tokens.push(Token.create(TokenType.Comma, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.Comma, this._cs.position, 1, this._getComments())); break; } case Char.Backtick: { - this._tokens.push(Token.create(TokenType.Backtick, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.Backtick, this._cs.position, 1, this._getComments())); break; } case Char.Semicolon: { - this._tokens.push(Token.create(TokenType.Semicolon, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.Semicolon, this._cs.position, 1, this._getComments())); break; } case Char.Colon: { if (this._cs.nextChar === Char.Equal) { - this._tokens.push(OperatorToken.create(this._cs.position, - 2, OperatorType.Walrus, this._getComments())); + this._tokens.push( + OperatorToken.create(this._cs.position, 2, OperatorType.Walrus, this._getComments()) + ); this._cs.advance(1); break; } - this._tokens.push(Token.create(TokenType.Colon, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.Colon, this._cs.position, 1, this._getComments())); break; } @@ -435,13 +439,11 @@ export class Tokenizer { if (this._cs.currentChar === Char.Period) { if (this._cs.nextChar === Char.Period && this._cs.lookAhead(2) === Char.Period) { - this._tokens.push(Token.create(TokenType.Ellipsis, - this._cs.position, 3, this._getComments())); + this._tokens.push(Token.create(TokenType.Ellipsis, this._cs.position, 3, this._getComments())); this._cs.advance(3); return true; } - this._tokens.push(Token.create(TokenType.Dot, - this._cs.position, 1, this._getComments())); + this._tokens.push(Token.create(TokenType.Dot, this._cs.position, 1, this._getComments())); break; } @@ -471,8 +473,7 @@ export class Tokenizer { // New lines are ignored within parentheses. // We'll also avoid adding multiple newlines in a row to simplify parsing. if (this._tokens.length === 0 || this._tokens[this._tokens.length - 1].type !== TokenType.NewLine) { - this._tokens.push(NewLineToken.create(this._cs.position, - length, newLineType, this._getComments())); + this._tokens.push(NewLineToken.create(this._cs.position, length, newLineType, this._getComments())); } } if (newLineType === NewLineType.CarriageReturn) { @@ -539,8 +540,7 @@ export class Tokenizer { this._indentSpacesTotal += spaceCount; this._indentAmounts.push(spaceCount); - this._tokens.push(IndentToken.create(this._cs.position, 0, - spaceCount, this._getComments())); + this._tokens.push(IndentToken.create(this._cs.position, 0, spaceCount, this._getComments())); } } else { if (this._indentAmounts[this._indentAmounts.length - 1] < spaceCount) { @@ -551,28 +551,29 @@ export class Tokenizer { this._indentSpacesTotal += spaceCount - this._indentAmounts[this._indentAmounts.length - 1]; this._indentAmounts.push(spaceCount); - this._tokens.push(IndentToken.create(this._cs.position, 0, - spaceCount, this._getComments())); + this._tokens.push(IndentToken.create(this._cs.position, 0, spaceCount, this._getComments())); } else { // The Python spec says that dedent amounts need to match the indent // amount exactly. An error is generated at runtime if it doesn't. // We'll record that error condition within the token, allowing the // parser to report it later. const dedentPoints: number[] = []; - while (this._indentAmounts.length > 0 && - this._indentAmounts[this._indentAmounts.length - 1] > spaceCount) { - dedentPoints.push(this._indentAmounts.length > 1 ? - this._indentAmounts[this._indentAmounts.length - 2] : 0); + while ( + this._indentAmounts.length > 0 && + this._indentAmounts[this._indentAmounts.length - 1] > spaceCount + ) { + dedentPoints.push( + this._indentAmounts.length > 1 ? this._indentAmounts[this._indentAmounts.length - 2] : 0 + ); this._indentAmounts.pop(); } dedentPoints.forEach((dedentAmount, index) => { - const matchesIndent = index < dedentPoints.length - 1 || - dedentAmount === spaceCount; - const actualDedentAmount = index < dedentPoints.length - 1 ? - dedentAmount : spaceCount; - this._tokens.push(DedentToken.create(this._cs.position, 0, actualDedentAmount, - matchesIndent, this._getComments())); + const matchesIndent = index < dedentPoints.length - 1 || dedentAmount === spaceCount; + const actualDedentAmount = index < dedentPoints.length - 1 ? dedentAmount : spaceCount; + this._tokens.push( + DedentToken.create(this._cs.position, 0, actualDedentAmount, matchesIndent, this._getComments()) + ); }); } } @@ -589,11 +590,11 @@ export class Tokenizer { if (this._cs.position > start) { const value = this._cs.getText().substr(start, this._cs.position - start); if (_keywords[value] !== undefined) { - this._tokens.push(KeywordToken.create(start, this._cs.position - start, - _keywords[value], this._getComments())); + this._tokens.push( + KeywordToken.create(start, this._cs.position - start, _keywords[value], this._getComments()) + ); } else { - this._tokens.push(IdentifierToken.create(start, this._cs.position - start, - value, this._getComments())); + this._tokens.push(IdentifierToken.create(start, this._cs.position - start, value, this._getComments())); } return true; } @@ -670,7 +671,10 @@ export class Tokenizer { mightBeFloatingPoint = true; this._cs.moveNext(); } - isDecimalInteger = this._cs.currentChar !== Char.Period && this._cs.currentChar !== Char.e && this._cs.currentChar !== Char.E; + isDecimalInteger = + this._cs.currentChar !== Char.Period && + this._cs.currentChar !== Char.e && + this._cs.currentChar !== Char.E; } // "0" (["_"] "0")* @@ -679,7 +683,10 @@ export class Tokenizer { while (this._cs.currentChar === Char._0 || this._cs.currentChar === Char.Underscore) { this._cs.moveNext(); } - isDecimalInteger = this._cs.currentChar !== Char.Period && this._cs.currentChar !== Char.e && this._cs.currentChar !== Char.E; + isDecimalInteger = + this._cs.currentChar !== Char.Period && + this._cs.currentChar !== Char.e && + this._cs.currentChar !== Char.E; } if (isDecimalInteger) { @@ -692,15 +699,19 @@ export class Tokenizer { text += String.fromCharCode(this._cs.currentChar); this._cs.moveNext(); } - this._tokens.push(NumberToken.create(start, text.length, value, true, isImaginary, this._getComments())); + this._tokens.push( + NumberToken.create(start, text.length, value, true, isImaginary, this._getComments()) + ); return true; } } // Floating point. Sign and leading digits were already skipped over. this._cs.position = start; - if (mightBeFloatingPoint || - (this._cs.currentChar === Char.Period && this._cs.nextChar >= Char._0 && this._cs.nextChar <= Char._9)) { + if ( + mightBeFloatingPoint || + (this._cs.currentChar === Char.Period && this._cs.nextChar >= Char._0 && this._cs.nextChar <= Char._9) + ) { if (this._skipFloatingPointCandidate()) { let text = this._cs.getText().substr(start, this._cs.position - start); const value = parseFloat(text); @@ -711,8 +722,16 @@ export class Tokenizer { text += String.fromCharCode(this._cs.currentChar); this._cs.moveNext(); } - this._tokens.push(NumberToken.create(start, this._cs.position - start, value, - false, isImaginary, this._getComments())); + this._tokens.push( + NumberToken.create( + start, + this._cs.position - start, + value, + false, + isImaginary, + this._getComments() + ) + ); return true; } } @@ -899,7 +918,10 @@ export class Tokenizer { } if (this._cs.lookAhead(2) === Char.SingleQuote || this._cs.lookAhead(2) === Char.DoubleQuote) { - const prefix = this._cs.getText().substr(this._cs.position, 2).toLowerCase(); + const prefix = this._cs + .getText() + .substr(this._cs.position, 2) + .toLowerCase(); switch (prefix) { case 'rf': case 'fr': @@ -973,12 +995,20 @@ export class Tokenizer { const end = this._cs.position; - this._tokens.push(StringToken.create(start, end - start, stringLiteralInfo.flags, - stringLiteralInfo.escapedValue, stringPrefixLength, this._getComments())); + this._tokens.push( + StringToken.create( + start, + end - start, + stringLiteralInfo.flags, + stringLiteralInfo.escapedValue, + stringPrefixLength, + this._getComments() + ) + ); } private _skipToEndOfStringLiteral(flags: StringTokenFlags): StringScannerOutput { - const quoteChar = (flags & StringTokenFlags.SingleQuote) ? Char.SingleQuote : Char.DoubleQuote; + const quoteChar = flags & StringTokenFlags.SingleQuote ? Char.SingleQuote : Char.DoubleQuote; const isTriplicate = (flags & StringTokenFlags.Triplicate) !== 0; let escapedValue = ''; @@ -1026,9 +1056,12 @@ export class Tokenizer { } else if (!isTriplicate && this._cs.currentChar === quoteChar) { this._cs.moveNext(); break; - } else if (isTriplicate && this._cs.currentChar === quoteChar && - this._cs.nextChar === quoteChar && this._cs.lookAhead(2) === quoteChar) { - + } else if ( + isTriplicate && + this._cs.currentChar === quoteChar && + this._cs.nextChar === quoteChar && + this._cs.lookAhead(2) === quoteChar + ) { this._cs.advance(3); break; } else { diff --git a/server/src/parser/tokenizerTypes.ts b/server/src/parser/tokenizerTypes.ts index ebd179e92..85a127655 100644 --- a/server/src/parser/tokenizerTypes.ts +++ b/server/src/parser/tokenizerTypes.ts @@ -1,15 +1,15 @@ /* -* tokenizerTypes.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Based on code from vscode-python repository: -* https://github.com/Microsoft/vscode-python -* -* Interface, enumeration and class definitions used within -* the Python tokenizer. -*/ + * tokenizerTypes.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Based on code from vscode-python repository: + * https://github.com/Microsoft/vscode-python + * + * Interface, enumeration and class definitions used within + * the Python tokenizer. + */ import { TextRange } from '../common/textRange'; @@ -97,10 +97,10 @@ export const enum OperatorType { } export const enum OperatorFlags { - Unary = 1 << 0, - Binary = 1 << 1, - Assignment = 1 << 2, - Comparison = 1 << 3 + Unary = 1 << 0, + Binary = 1 << 1, + Assignment = 1 << 2, + Comparison = 1 << 3 } export const enum KeywordType { @@ -143,21 +143,21 @@ export const enum KeywordType { } export const enum StringTokenFlags { - None = 0, + None = 0, // Quote types - SingleQuote = 1 << 0, - DoubleQuote = 1 << 1, - Triplicate = 1 << 2, + SingleQuote = 1 << 0, + DoubleQuote = 1 << 1, + Triplicate = 1 << 2, // String content format - Raw = 1 << 3, - Unicode = 1 << 4, - Bytes = 1 << 5, - Format = 1 << 6, + Raw = 1 << 3, + Unicode = 1 << 4, + Bytes = 1 << 5, + Format = 1 << 6, // Error conditions - Unterminated = 1 << 16 + Unterminated = 1 << 16 } export interface Comment extends TextRange { @@ -188,9 +188,7 @@ export interface TokenBase extends TextRange { export interface Token extends TokenBase {} export namespace Token { - export function create(type: TokenType, start: number, length: number, - comments: Comment[] | undefined) { - + export function create(type: TokenType, start: number, length: number, comments: Comment[] | undefined) { const token: Token = { start, length, @@ -208,9 +206,7 @@ export interface IndentToken extends Token { } export namespace IndentToken { - export function create(start: number, length: number, indentAmount: number, - comments: Comment[] | undefined) { - + export function create(start: number, length: number, indentAmount: number, comments: Comment[] | undefined) { const token: IndentToken = { start, length, @@ -230,9 +226,13 @@ export interface DedentToken extends Token { } export namespace DedentToken { - export function create(start: number, length: number, indentAmount: number, - matchesIndent: boolean, comments: Comment[] | undefined) { - + export function create( + start: number, + length: number, + indentAmount: number, + matchesIndent: boolean, + comments: Comment[] | undefined + ) { const token: DedentToken = { start, length, @@ -252,9 +252,7 @@ export interface NewLineToken extends Token { } export namespace NewLineToken { - export function create(start: number, length: number, newLineType: NewLineType, - comments: Comment[] | undefined) { - + export function create(start: number, length: number, newLineType: NewLineType, comments: Comment[] | undefined) { const token: NewLineToken = { start, length, @@ -273,9 +271,7 @@ export interface KeywordToken extends Token { } export namespace KeywordToken { - export function create(start: number, length: number, keywordType: KeywordType, - comments: Comment[] | undefined) { - + export function create(start: number, length: number, keywordType: KeywordType, comments: Comment[] | undefined) { const token: KeywordToken = { start, length, @@ -305,9 +301,14 @@ export interface StringToken extends Token { } export namespace StringToken { - export function create(start: number, length: number, flags: StringTokenFlags, escapedValue: string, - prefixLength: number, comments: Comment[] | undefined) { - + export function create( + start: number, + length: number, + flags: StringTokenFlags, + escapedValue: string, + prefixLength: number, + comments: Comment[] | undefined + ) { const token: StringToken = { start, length, @@ -315,7 +316,7 @@ export namespace StringToken { flags, escapedValue, prefixLength, - quoteMarkLength: (flags & StringTokenFlags.Triplicate) ? 3 : 1, + quoteMarkLength: flags & StringTokenFlags.Triplicate ? 3 : 1, comments }; @@ -331,9 +332,14 @@ export interface NumberToken extends Token { } export namespace NumberToken { - export function create(start: number, length: number, value: number, isInteger: boolean, - isImaginary: boolean, comments: Comment[] | undefined) { - + export function create( + start: number, + length: number, + value: number, + isInteger: boolean, + isImaginary: boolean, + comments: Comment[] | undefined + ) { const token: NumberToken = { start, length, @@ -354,9 +360,7 @@ export interface OperatorToken extends Token { } export namespace OperatorToken { - export function create(start: number, length: number, operatorType: OperatorType, - comments: Comment[] | undefined) { - + export function create(start: number, length: number, operatorType: OperatorType, comments: Comment[] | undefined) { const token: OperatorToken = { start, length, @@ -375,9 +379,7 @@ export interface IdentifierToken extends Token { } export namespace IdentifierToken { - export function create(start: number, length: number, value: string, - comments: Comment[] | undefined) { - + export function create(start: number, length: number, value: string, comments: Comment[] | undefined) { const token: IdentifierToken = { start, length, diff --git a/server/src/parser/unicode.ts b/server/src/parser/unicode.ts index 772190388..f05a25131 100644 --- a/server/src/parser/unicode.ts +++ b/server/src/parser/unicode.ts @@ -1,253 +1,2356 @@ /* -* unicode.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Tables that encode Unicode character codes for various Unicode- -* defined categories used in the Python spec. These tables were built -* from the npm package unicode, which contains the same information -* in a much more verbose form. -*/ + * unicode.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Tables that encode Unicode character codes for various Unicode- + * defined categories used in the Python spec. These tables were built + * from the npm package unicode, which contains the same information + * in a much more verbose form. + */ export type UnicodeRangeTable = ([number, number] | number)[]; -export const unicodeLu: UnicodeRangeTable = [[65, 90], [192, 214], [216, 222], 256, 258, 260, 262, 264, 266, - 268, 270, 272, 274, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, - 310, 313, 315, 317, 319, 321, 323, 325, 327, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348, 350, 352, - 354, 356, 358, 360, 362, 364, 366, 368, 370, 372, 374, [376, 377], 379, 381, [385, 386], 388, [390, 391], - [393, 395], [398, 401], [403, 404], [406, 408], [412, 413], [415, 416], 418, 420, [422, 423], 425, - 428, [430, 431], [433, 435], 437, [439, 440], 444, 452, 455, 458, 461, 463, 465, 467, 469, 471, 473, - 475, 478, 480, 482, 484, 486, 488, 490, 492, 494, 497, 500, [502, 504], 506, 508, 510, 512, 514, 516, - 518, 520, 522, 524, 526, 528, 530, 532, 534, 536, 538, 540, 542, 544, 546, 548, 550, 552, 554, 556, - 558, 560, 562, [570, 571], [573, 574], 577, [579, 582], 584, 586, 588, 590, 880, 882, 886, 895, 902, - [904, 906], 908, [910, 911], [913, 929], [931, 939], 975, [978, 980], 984, 986, 988, 990, 992, 994, - 996, 998, 1000, 1002, 1004, 1006, 1012, 1015, [1017, 1018], [1021, 1071], 1120, 1122, 1124, 1126, - 1128, 1130, 1132, 1134, 1136, 1138, 1140, 1142, 1144, 1146, 1148, 1150, 1152, 1162, 1164, 1166, 1168, - 1170, 1172, 1174, 1176, 1178, 1180, 1182, 1184, 1186, 1188, 1190, 1192, 1194, 1196, 1198, 1200, 1202, - 1204, 1206, 1208, 1210, 1212, 1214, [1216, 1217], 1219, 1221, 1223, 1225, 1227, 1229, 1232, 1234, - 1236, 1238, 1240, 1242, 1244, 1246, 1248, 1250, 1252, 1254, 1256, 1258, 1260, 1262, 1264, 1266, 1268, - 1270, 1272, 1274, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294, 1296, 1298, 1300, 1302, - 1304, 1306, 1308, 1310, 1312, 1314, 1316, 1318, 1320, 1322, 1324, 1326, [1329, 1366], [4256, 4293], - 4295, 4301, [5024, 5109], 7680, 7682, 7684, 7686, 7688, 7690, 7692, 7694, 7696, 7698, 7700, 7702, - 7704, 7706, 7708, 7710, 7712, 7714, 7716, 7718, 7720, 7722, 7724, 7726, 7728, 7730, 7732, 7734, 7736, - 7738, 7740, 7742, 7744, 7746, 7748, 7750, 7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, - 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, 7800, 7802, 7804, - 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822, 7824, 7826, 7828, 7838, 7840, 7842, 7844, 7846, - 7848, 7850, 7852, 7854, 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, 7876, 7878, 7880, - 7882, 7884, 7886, 7888, 7890, 7892, 7894, 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, - 7916, 7918, 7920, 7922, 7924, 7926, 7928, 7930, 7932, 7934, [7944, 7951], [7960, 7965], [7976, 7983], - [7992, 7999], [8008, 8013], 8025, 8027, 8029, 8031, [8040, 8047], [8120, 8123], [8136, 8139], [8152, 8155], - [8168, 8172], [8184, 8187], 8450, 8455, [8459, 8461], [8464, 8466], 8469, [8473, 8477], 8484, 8486, 8488, - [8490, 8493], [8496, 8499], [8510, 8511], 8517, 8579, [11264, 11310], 11360, [11362, 11364], 11367, 11369, - 11371, [11373, 11376], 11378, 11381, [11390, 11392], 11394, 11396, 11398, 11400, 11402, 11404, 11406, 11408, - 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, 11426, 11428, 11430, 11432, 11434, 11436, 11438, - 11440, 11442, 11444, 11446, 11448, 11450, 11452, 11454, 11456, 11458, 11460, 11462, 11464, 11466, 11468, - 11470, 11472, 11474, 11476, 11478, 11480, 11482, 11484, 11486, 11488, 11490, 11499, 11501, 11506, 42560, - 42562, 42564, 42566, 42568, 42570, 42572, 42574, 42576, 42578, 42580, 42582, 42584, 42586, 42588, 42590, - 42592, 42594, 42596, 42598, 42600, 42602, 42604, 42624, 42626, 42628, 42630, 42632, 42634, 42636, 42638, - 42640, 42642, 42644, 42646, 42648, 42650, 42786, 42788, 42790, 42792, 42794, 42796, 42798, 42802, 42804, - 42806, 42808, 42810, 42812, 42814, 42816, 42818, 42820, 42822, 42824, 42826, 42828, 42830, 42832, 42834, - 42836, 42838, 42840, 42842, 42844, 42846, 42848, 42850, 42852, 42854, 42856, 42858, 42860, 42862, 42873, - 42875, [42877, 42878], 42880, 42882, 42884, 42886, 42891, 42893, 42896, 42898, 42902, 42904, 42906, - 42908, 42910, 42912, 42914, 42916, 42918, 42920, [42922, 42926], [42928, 42932], 42934, [65313, 65338], - [66560, 66599], [66736, 66771], [68736, 68786], [71840, 71871], [119808, 119833], [119860, 119885], - [119912, 119937], 119964, [119966, 119967], 119970, [119973, 119974], [119977, 119980], [119982, 119989], - [120016, 120041], [120068, 120069], [120071, 120074], [120077, 120084], [120086, 120092], [120120, 120121], - [120123, 120126], [120128, 120132], 120134, [120138, 120144], [120172, 120197], [120224, 120249], - [120276, 120301], [120328, 120353], [120380, 120405], [120432, 120457], [120488, 120512], [120546, 120570], - [120604, 120628], [120662, 120686], [120720, 120744], 120778, [125184, 125217]]; +export const unicodeLu: UnicodeRangeTable = [ + [65, 90], + [192, 214], + [216, 222], + 256, + 258, + 260, + 262, + 264, + 266, + 268, + 270, + 272, + 274, + 276, + 278, + 280, + 282, + 284, + 286, + 288, + 290, + 292, + 294, + 296, + 298, + 300, + 302, + 304, + 306, + 308, + 310, + 313, + 315, + 317, + 319, + 321, + 323, + 325, + 327, + 330, + 332, + 334, + 336, + 338, + 340, + 342, + 344, + 346, + 348, + 350, + 352, + 354, + 356, + 358, + 360, + 362, + 364, + 366, + 368, + 370, + 372, + 374, + [376, 377], + 379, + 381, + [385, 386], + 388, + [390, 391], + [393, 395], + [398, 401], + [403, 404], + [406, 408], + [412, 413], + [415, 416], + 418, + 420, + [422, 423], + 425, + 428, + [430, 431], + [433, 435], + 437, + [439, 440], + 444, + 452, + 455, + 458, + 461, + 463, + 465, + 467, + 469, + 471, + 473, + 475, + 478, + 480, + 482, + 484, + 486, + 488, + 490, + 492, + 494, + 497, + 500, + [502, 504], + 506, + 508, + 510, + 512, + 514, + 516, + 518, + 520, + 522, + 524, + 526, + 528, + 530, + 532, + 534, + 536, + 538, + 540, + 542, + 544, + 546, + 548, + 550, + 552, + 554, + 556, + 558, + 560, + 562, + [570, 571], + [573, 574], + 577, + [579, 582], + 584, + 586, + 588, + 590, + 880, + 882, + 886, + 895, + 902, + [904, 906], + 908, + [910, 911], + [913, 929], + [931, 939], + 975, + [978, 980], + 984, + 986, + 988, + 990, + 992, + 994, + 996, + 998, + 1000, + 1002, + 1004, + 1006, + 1012, + 1015, + [1017, 1018], + [1021, 1071], + 1120, + 1122, + 1124, + 1126, + 1128, + 1130, + 1132, + 1134, + 1136, + 1138, + 1140, + 1142, + 1144, + 1146, + 1148, + 1150, + 1152, + 1162, + 1164, + 1166, + 1168, + 1170, + 1172, + 1174, + 1176, + 1178, + 1180, + 1182, + 1184, + 1186, + 1188, + 1190, + 1192, + 1194, + 1196, + 1198, + 1200, + 1202, + 1204, + 1206, + 1208, + 1210, + 1212, + 1214, + [1216, 1217], + 1219, + 1221, + 1223, + 1225, + 1227, + 1229, + 1232, + 1234, + 1236, + 1238, + 1240, + 1242, + 1244, + 1246, + 1248, + 1250, + 1252, + 1254, + 1256, + 1258, + 1260, + 1262, + 1264, + 1266, + 1268, + 1270, + 1272, + 1274, + 1276, + 1278, + 1280, + 1282, + 1284, + 1286, + 1288, + 1290, + 1292, + 1294, + 1296, + 1298, + 1300, + 1302, + 1304, + 1306, + 1308, + 1310, + 1312, + 1314, + 1316, + 1318, + 1320, + 1322, + 1324, + 1326, + [1329, 1366], + [4256, 4293], + 4295, + 4301, + [5024, 5109], + 7680, + 7682, + 7684, + 7686, + 7688, + 7690, + 7692, + 7694, + 7696, + 7698, + 7700, + 7702, + 7704, + 7706, + 7708, + 7710, + 7712, + 7714, + 7716, + 7718, + 7720, + 7722, + 7724, + 7726, + 7728, + 7730, + 7732, + 7734, + 7736, + 7738, + 7740, + 7742, + 7744, + 7746, + 7748, + 7750, + 7752, + 7754, + 7756, + 7758, + 7760, + 7762, + 7764, + 7766, + 7768, + 7770, + 7772, + 7774, + 7776, + 7778, + 7780, + 7782, + 7784, + 7786, + 7788, + 7790, + 7792, + 7794, + 7796, + 7798, + 7800, + 7802, + 7804, + 7806, + 7808, + 7810, + 7812, + 7814, + 7816, + 7818, + 7820, + 7822, + 7824, + 7826, + 7828, + 7838, + 7840, + 7842, + 7844, + 7846, + 7848, + 7850, + 7852, + 7854, + 7856, + 7858, + 7860, + 7862, + 7864, + 7866, + 7868, + 7870, + 7872, + 7874, + 7876, + 7878, + 7880, + 7882, + 7884, + 7886, + 7888, + 7890, + 7892, + 7894, + 7896, + 7898, + 7900, + 7902, + 7904, + 7906, + 7908, + 7910, + 7912, + 7914, + 7916, + 7918, + 7920, + 7922, + 7924, + 7926, + 7928, + 7930, + 7932, + 7934, + [7944, 7951], + [7960, 7965], + [7976, 7983], + [7992, 7999], + [8008, 8013], + 8025, + 8027, + 8029, + 8031, + [8040, 8047], + [8120, 8123], + [8136, 8139], + [8152, 8155], + [8168, 8172], + [8184, 8187], + 8450, + 8455, + [8459, 8461], + [8464, 8466], + 8469, + [8473, 8477], + 8484, + 8486, + 8488, + [8490, 8493], + [8496, 8499], + [8510, 8511], + 8517, + 8579, + [11264, 11310], + 11360, + [11362, 11364], + 11367, + 11369, + 11371, + [11373, 11376], + 11378, + 11381, + [11390, 11392], + 11394, + 11396, + 11398, + 11400, + 11402, + 11404, + 11406, + 11408, + 11410, + 11412, + 11414, + 11416, + 11418, + 11420, + 11422, + 11424, + 11426, + 11428, + 11430, + 11432, + 11434, + 11436, + 11438, + 11440, + 11442, + 11444, + 11446, + 11448, + 11450, + 11452, + 11454, + 11456, + 11458, + 11460, + 11462, + 11464, + 11466, + 11468, + 11470, + 11472, + 11474, + 11476, + 11478, + 11480, + 11482, + 11484, + 11486, + 11488, + 11490, + 11499, + 11501, + 11506, + 42560, + 42562, + 42564, + 42566, + 42568, + 42570, + 42572, + 42574, + 42576, + 42578, + 42580, + 42582, + 42584, + 42586, + 42588, + 42590, + 42592, + 42594, + 42596, + 42598, + 42600, + 42602, + 42604, + 42624, + 42626, + 42628, + 42630, + 42632, + 42634, + 42636, + 42638, + 42640, + 42642, + 42644, + 42646, + 42648, + 42650, + 42786, + 42788, + 42790, + 42792, + 42794, + 42796, + 42798, + 42802, + 42804, + 42806, + 42808, + 42810, + 42812, + 42814, + 42816, + 42818, + 42820, + 42822, + 42824, + 42826, + 42828, + 42830, + 42832, + 42834, + 42836, + 42838, + 42840, + 42842, + 42844, + 42846, + 42848, + 42850, + 42852, + 42854, + 42856, + 42858, + 42860, + 42862, + 42873, + 42875, + [42877, 42878], + 42880, + 42882, + 42884, + 42886, + 42891, + 42893, + 42896, + 42898, + 42902, + 42904, + 42906, + 42908, + 42910, + 42912, + 42914, + 42916, + 42918, + 42920, + [42922, 42926], + [42928, 42932], + 42934, + [65313, 65338], + [66560, 66599], + [66736, 66771], + [68736, 68786], + [71840, 71871], + [119808, 119833], + [119860, 119885], + [119912, 119937], + 119964, + [119966, 119967], + 119970, + [119973, 119974], + [119977, 119980], + [119982, 119989], + [120016, 120041], + [120068, 120069], + [120071, 120074], + [120077, 120084], + [120086, 120092], + [120120, 120121], + [120123, 120126], + [120128, 120132], + 120134, + [120138, 120144], + [120172, 120197], + [120224, 120249], + [120276, 120301], + [120328, 120353], + [120380, 120405], + [120432, 120457], + [120488, 120512], + [120546, 120570], + [120604, 120628], + [120662, 120686], + [120720, 120744], + 120778, + [125184, 125217] +]; -export const unicodeLl: UnicodeRangeTable = [[97, 122], 181, [223, 246], [248, 255], 257, 259, 261, 263, 265, - 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, - [311, 312], 314, 316, 318, 320, 322, 324, 326, [328, 329], 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, - 351, 353, 355, 357, 359, 361, 363, 365, 367, 369, 371, 373, 375, 378, 380, [382, 384], 387, 389, 392, [396, 397], - 402, 405, [409, 411], 414, 417, 419, 421, 424, [426, 427], 429, 432, 436, 438, [441, 442], [445, 447], 454, 457, - 460, 462, 464, 466, 468, 470, 472, 474, [476, 477], 479, 481, 483, 485, 487, 489, 491, 493, [495, 496], 499, - 501, 505, 507, 509, 511, 513, 515, 517, 519, 521, 523, 525, 527, 529, 531, 533, 535, 537, 539, 541, 543, - 545, 547, 549, 551, 553, 555, 557, 559, 561, [563, 569], 572, [575, 576], 578, 583, 585, 587, 589, [591, - 659], [661, 687], 881, 883, 887, [891, 893], 912, [940, 974], [976, 977], [981, 983], 985, 987, 989, 991, - 993, 995, 997, 999, 1001, 1003, 1005, [1007, 1011], 1013, 1016, [1019, 1020], [1072, 1119], 1121, 1123, 1125, - 1127, 1129, 1131, 1133, 1135, 1137, 1139, 1141, 1143, 1145, 1147, 1149, 1151, 1153, 1163, 1165, 1167, 1169, - 1171, 1173, 1175, 1177, 1179, 1181, 1183, 1185, 1187, 1189, 1191, 1193, 1195, 1197, 1199, 1201, 1203, 1205, - 1207, 1209, 1211, 1213, 1215, 1218, 1220, 1222, 1224, 1226, 1228, [1230, 1231], 1233, 1235, 1237, 1239, 1241, - 1243, 1245, 1247, 1249, 1251, 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1271, 1273, 1275, 1277, - 1279, 1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, 1297, 1299, 1301, 1303, 1305, 1307, 1309, 1311, 1313, - 1315, 1317, 1319, 1321, 1323, 1325, 1327, [1377, 1415], [5112, 5117], [7296, 7304], [7424, 7467], [7531, 7543], - [7545, 7578], 7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, - 7713, 7715, 7717, 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, - 7749, 7751, 7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, - 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, - 7821, 7823, 7825, 7827, [7829, 7837], 7839, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, - 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895, 7897, - 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, 7927, 7929, 7931, 7933, - [7935, 7943], [7952, 7957], [7968, 7975], [7984, 7991], [8000, 8005], [8016, 8023], [8032, 8039], [8048, 8061], - [8064, 8071], [8080, 8087], [8096, 8103], [8112, 8116], [8118, 8119], 8126, [8130, 8132], [8134, 8135], - [8144, 8147], [8150, 8151], [8160, 8167], [8178, 8180], [8182, 8183], 8458, [8462, 8463], 8467, 8495, 8500, - 8505, [8508, 8509], [8518, 8521], 8526, 8580, [11312, 11358], 11361, [11365, 11366], 11368, 11370, 11372, - 11377, [11379, 11380], [11382, 11387], 11393, 11395, 11397, 11399, 11401, 11403, 11405, 11407, 11409, 11411, - 11413, 11415, 11417, 11419, 11421, 11423, 11425, 11427, 11429, 11431, 11433, 11435, 11437, 11439, 11441, - 11443, 11445, 11447, 11449, 11451, 11453, 11455, 11457, 11459, 11461, 11463, 11465, 11467, 11469, 11471, - 11473, 11475, 11477, 11479, 11481, 11483, 11485, 11487, 11489, [11491, 11492], 11500, 11502, 11507, - [11520, 11557], 11559, 11565, 42561, 42563, 42565, 42567, 42569, 42571, 42573, 42575, 42577, 42579, - 2581, 42583, 42585, 42587, 42589, 42591, 42593, 42595, 42597, 42599, 42601, 42603, 42605, 42625, 42627, - 42629, 42631, 42633, 42635, 42637, 42639, 42641, 42643, 42645, 42647, 42649, 42651, 42787, 42789, 42791, - 42793, 42795, 42797, [42799, 42801], 42803, 42805, 42807, 42809, 42811, 42813, 42815, 42817, 42819, 42821, - 42823, 42825, 42827, 42829, 42831, 42833, 42835, 42837, 42839, 42841, 42843, 42845, 42847, 42849, 42851, - 42853, 42855, 42857, 42859, 42861, 42863, [42865, 42872], 42874, 42876, 42879, 42881, 42883, 42885, 42887, - 42892, 42894, 42897, [42899, 42901], 42903, 42905, 42907, 42909, 42911, 42913, 42915, 42917, 42919, 42921, - 42933, 42935, 43002, [43824, 43866], [43872, 43877], [43888, 43967], [64256, 64262], [64275, 64279], - [65345, 65370], [66600, 66639], [66776, 66811], [68800, 68850], [71872, 71903], [119834, 119859], - [119886, 119892], [119894, 119911], [119938, 119963], [119990, 119993], 119995, [119997, 120003], - [120005, 120015], [120042, 120067], [120094, 120119], [120146, 120171], [120198, 120223], [120250, 120275], - [120302, 120327], [120354, 120379], [120406, 120431], [120458, 120485], [120514, 120538], [120540, 120545], - [120572, 120596], [120598, 120603], [120630, 120654], [120656, 120661], [120688, 120712], [120714, 120719], - [120746, 120770], [120772, 120777], 120779, [125218, 125251]]; +export const unicodeLl: UnicodeRangeTable = [ + [97, 122], + 181, + [223, 246], + [248, 255], + 257, + 259, + 261, + 263, + 265, + 267, + 269, + 271, + 273, + 275, + 277, + 279, + 281, + 283, + 285, + 287, + 289, + 291, + 293, + 295, + 297, + 299, + 301, + 303, + 305, + 307, + 309, + [311, 312], + 314, + 316, + 318, + 320, + 322, + 324, + 326, + [328, 329], + 331, + 333, + 335, + 337, + 339, + 341, + 343, + 345, + 347, + 349, + 351, + 353, + 355, + 357, + 359, + 361, + 363, + 365, + 367, + 369, + 371, + 373, + 375, + 378, + 380, + [382, 384], + 387, + 389, + 392, + [396, 397], + 402, + 405, + [409, 411], + 414, + 417, + 419, + 421, + 424, + [426, 427], + 429, + 432, + 436, + 438, + [441, 442], + [445, 447], + 454, + 457, + 460, + 462, + 464, + 466, + 468, + 470, + 472, + 474, + [476, 477], + 479, + 481, + 483, + 485, + 487, + 489, + 491, + 493, + [495, 496], + 499, + 501, + 505, + 507, + 509, + 511, + 513, + 515, + 517, + 519, + 521, + 523, + 525, + 527, + 529, + 531, + 533, + 535, + 537, + 539, + 541, + 543, + 545, + 547, + 549, + 551, + 553, + 555, + 557, + 559, + 561, + [563, 569], + 572, + [575, 576], + 578, + 583, + 585, + 587, + 589, + [591, 659], + [661, 687], + 881, + 883, + 887, + [891, 893], + 912, + [940, 974], + [976, 977], + [981, 983], + 985, + 987, + 989, + 991, + 993, + 995, + 997, + 999, + 1001, + 1003, + 1005, + [1007, 1011], + 1013, + 1016, + [1019, 1020], + [1072, 1119], + 1121, + 1123, + 1125, + 1127, + 1129, + 1131, + 1133, + 1135, + 1137, + 1139, + 1141, + 1143, + 1145, + 1147, + 1149, + 1151, + 1153, + 1163, + 1165, + 1167, + 1169, + 1171, + 1173, + 1175, + 1177, + 1179, + 1181, + 1183, + 1185, + 1187, + 1189, + 1191, + 1193, + 1195, + 1197, + 1199, + 1201, + 1203, + 1205, + 1207, + 1209, + 1211, + 1213, + 1215, + 1218, + 1220, + 1222, + 1224, + 1226, + 1228, + [1230, 1231], + 1233, + 1235, + 1237, + 1239, + 1241, + 1243, + 1245, + 1247, + 1249, + 1251, + 1253, + 1255, + 1257, + 1259, + 1261, + 1263, + 1265, + 1267, + 1269, + 1271, + 1273, + 1275, + 1277, + 1279, + 1281, + 1283, + 1285, + 1287, + 1289, + 1291, + 1293, + 1295, + 1297, + 1299, + 1301, + 1303, + 1305, + 1307, + 1309, + 1311, + 1313, + 1315, + 1317, + 1319, + 1321, + 1323, + 1325, + 1327, + [1377, 1415], + [5112, 5117], + [7296, 7304], + [7424, 7467], + [7531, 7543], + [7545, 7578], + 7681, + 7683, + 7685, + 7687, + 7689, + 7691, + 7693, + 7695, + 7697, + 7699, + 7701, + 7703, + 7705, + 7707, + 7709, + 7711, + 7713, + 7715, + 7717, + 7719, + 7721, + 7723, + 7725, + 7727, + 7729, + 7731, + 7733, + 7735, + 7737, + 7739, + 7741, + 7743, + 7745, + 7747, + 7749, + 7751, + 7753, + 7755, + 7757, + 7759, + 7761, + 7763, + 7765, + 7767, + 7769, + 7771, + 7773, + 7775, + 7777, + 7779, + 7781, + 7783, + 7785, + 7787, + 7789, + 7791, + 7793, + 7795, + 7797, + 7799, + 7801, + 7803, + 7805, + 7807, + 7809, + 7811, + 7813, + 7815, + 7817, + 7819, + 7821, + 7823, + 7825, + 7827, + [7829, 7837], + 7839, + 7841, + 7843, + 7845, + 7847, + 7849, + 7851, + 7853, + 7855, + 7857, + 7859, + 7861, + 7863, + 7865, + 7867, + 7869, + 7871, + 7873, + 7875, + 7877, + 7879, + 7881, + 7883, + 7885, + 7887, + 7889, + 7891, + 7893, + 7895, + 7897, + 7899, + 7901, + 7903, + 7905, + 7907, + 7909, + 7911, + 7913, + 7915, + 7917, + 7919, + 7921, + 7923, + 7925, + 7927, + 7929, + 7931, + 7933, + [7935, 7943], + [7952, 7957], + [7968, 7975], + [7984, 7991], + [8000, 8005], + [8016, 8023], + [8032, 8039], + [8048, 8061], + [8064, 8071], + [8080, 8087], + [8096, 8103], + [8112, 8116], + [8118, 8119], + 8126, + [8130, 8132], + [8134, 8135], + [8144, 8147], + [8150, 8151], + [8160, 8167], + [8178, 8180], + [8182, 8183], + 8458, + [8462, 8463], + 8467, + 8495, + 8500, + 8505, + [8508, 8509], + [8518, 8521], + 8526, + 8580, + [11312, 11358], + 11361, + [11365, 11366], + 11368, + 11370, + 11372, + 11377, + [11379, 11380], + [11382, 11387], + 11393, + 11395, + 11397, + 11399, + 11401, + 11403, + 11405, + 11407, + 11409, + 11411, + 11413, + 11415, + 11417, + 11419, + 11421, + 11423, + 11425, + 11427, + 11429, + 11431, + 11433, + 11435, + 11437, + 11439, + 11441, + 11443, + 11445, + 11447, + 11449, + 11451, + 11453, + 11455, + 11457, + 11459, + 11461, + 11463, + 11465, + 11467, + 11469, + 11471, + 11473, + 11475, + 11477, + 11479, + 11481, + 11483, + 11485, + 11487, + 11489, + [11491, 11492], + 11500, + 11502, + 11507, + [11520, 11557], + 11559, + 11565, + 42561, + 42563, + 42565, + 42567, + 42569, + 42571, + 42573, + 42575, + 42577, + 42579, + 2581, + 42583, + 42585, + 42587, + 42589, + 42591, + 42593, + 42595, + 42597, + 42599, + 42601, + 42603, + 42605, + 42625, + 42627, + 42629, + 42631, + 42633, + 42635, + 42637, + 42639, + 42641, + 42643, + 42645, + 42647, + 42649, + 42651, + 42787, + 42789, + 42791, + 42793, + 42795, + 42797, + [42799, 42801], + 42803, + 42805, + 42807, + 42809, + 42811, + 42813, + 42815, + 42817, + 42819, + 42821, + 42823, + 42825, + 42827, + 42829, + 42831, + 42833, + 42835, + 42837, + 42839, + 42841, + 42843, + 42845, + 42847, + 42849, + 42851, + 42853, + 42855, + 42857, + 42859, + 42861, + 42863, + [42865, 42872], + 42874, + 42876, + 42879, + 42881, + 42883, + 42885, + 42887, + 42892, + 42894, + 42897, + [42899, 42901], + 42903, + 42905, + 42907, + 42909, + 42911, + 42913, + 42915, + 42917, + 42919, + 42921, + 42933, + 42935, + 43002, + [43824, 43866], + [43872, 43877], + [43888, 43967], + [64256, 64262], + [64275, 64279], + [65345, 65370], + [66600, 66639], + [66776, 66811], + [68800, 68850], + [71872, 71903], + [119834, 119859], + [119886, 119892], + [119894, 119911], + [119938, 119963], + [119990, 119993], + 119995, + [119997, 120003], + [120005, 120015], + [120042, 120067], + [120094, 120119], + [120146, 120171], + [120198, 120223], + [120250, 120275], + [120302, 120327], + [120354, 120379], + [120406, 120431], + [120458, 120485], + [120514, 120538], + [120540, 120545], + [120572, 120596], + [120598, 120603], + [120630, 120654], + [120656, 120661], + [120688, 120712], + [120714, 120719], + [120746, 120770], + [120772, 120777], + 120779, + [125218, 125251] +]; -export const unicodeLt: UnicodeRangeTable = [453, 456, 459, 498, [8072, 8079], [8088, 8095], [8104, 8111], - 8124, 8140, 8188]; +export const unicodeLt: UnicodeRangeTable = [ + 453, + 456, + 459, + 498, + [8072, 8079], + [8088, 8095], + [8104, 8111], + 8124, + 8140, + 8188 +]; -export const unicodeLo: UnicodeRangeTable = [170, 186, 443, [448, 451], 660, [1488, 1514], [1520, 1522], - [1568, 1599], [1601, 1610], [1646, 1647], [1649, 1747], 1749, [1774, 1775], [1786, 1788], 1791, 1808, - [1810, 1839], [1869, 1957], 1969, [1994, 2026], [2048, 2069], [2112, 2136], [2144, 2154], [2208, 2228], - [2230, 2237], [2308, 2361], 2365, 2384, [2392, 2401], [2418, 2432], [2437, 2444], [2447, 2448], [2451, 2472], - [2474, 2480], 2482, [2486, 2489], 2493, 2510, [2524, 2525], [2527, 2529], [2544, 2545], 2556, [2565, 2570], - [2575, 2576], [2579, 2600], [2602, 2608], [2610, 2611], [2613, 2614], [2616, 2617], [2649, 2652], 2654, - [2674, 2676], [2693, 2701], [2703, 2705], [2707, 2728], [2730, 2736], [2738, 2739], [2741, 2745], 2749, 2768, - [2784, 2785], 2809, [2821, 2828], [2831, 2832], [2835, 2856], [2858, 2864], [2866, 2867], [2869, 2873], - 2877, [2908, 2909], [2911, 2913], 2929, 2947, [2949, 2954], [2958, 2960], [2962, 2965], [2969, 2970], - 2972, [2974, 2975], [2979, 2980], [2984, 2986], [2990, 3001], 3024, [3077, 3084], [3086, 3088], - [3090, 3112], [3114, 3129], 3133, [3160, 3162], [3168, 3169], 3200, [3205, 3212], [3214, 3216], - [3218, 3240], [3242, 3251], [3253, 3257], 3261, 3294, [3296, 3297], [3313, 3314], [3333, 3340], - [3342, 3344], [3346, 3386], 3389, 3406, [3412, 3414], [3423, 3425], [3450, 3455], [3461, 3478], - [3482, 3505], [3507, 3515], 3517, [3520, 3526], [3585, 3632], [3634, 3635], [3648, 3653], [3713, 3714], - 3716, [3719, 3720], 3722, 3725, [3732, 3735], [3737, 3743], [3745, 3747], 3749, 3751, [3754, 3755], - [3757, 3760], [3762, 3763], 3773, [3776, 3780], [3804, 3807], 3840, [3904, 3911], [3913, 3948], - [3976, 3980], [4096, 4138], 4159, [4176, 4181], [4186, 4189], 4193, [4197, 4198], [4206, 4208], - [4213, 4225], 4238, [4304, 4346], [4349, 4680], [4682, 4685], [4688, 4694], 4696, [4698, 4701], - [4704, 4744], [4746, 4749], [4752, 4784], [4786, 4789], [4792, 4798], 4800, [4802, 4805], [4808, 4822], - [4824, 4880], [4882, 4885], [4888, 4954], [4992, 5007], [5121, 5740], [5743, 5759], [5761, 5786], - [5792, 5866], [5873, 5880], [5888, 5900], [5902, 5905], [5920, 5937], [5952, 5969], [5984, 5996], - [5998, 6000], [6016, 6067], 6108, [6176, 6210], [6212, 6263], [6272, 6276], [6279, 6312], 6314, - [6320, 6389], [6400, 6430], [6480, 6509], [6512, 6516], [6528, 6571], [6576, 6601], [6656, 6678], - [6688, 6740], [6917, 6963], [6981, 6987], [7043, 7072], [7086, 7087], [7098, 7141], [7168, 7203], - [7245, 7247], [7258, 7287], [7401, 7404], [7406, 7409], [7413, 7414], [8501, 8504], [11568, 11623], - [11648, 11670], [11680, 11686], [11688, 11694], [11696, 11702], [11704, 11710], [11712, 11718], - [11720, 11726], [11728, 11734], [11736, 11742], 12294, 12348, [12353, 12438], 12447, [12449, 12538], - 12543, [12549, 12590], [12593, 12686], [12704, 12730], [12784, 12799], [13312, 19893], [19968, 40938], - [40960, 40980], [40982, 42124], [42192, 42231], [42240, 42507], [42512, 42527], [42538, 42539], 42606, - [42656, 42725], 42895, 42999, [43003, 43009], [43011, 43013], [43015, 43018], [43020, 43042], - [43072, 43123], [43138, 43187], [43250, 43255], 43259, 43261, [43274, 43301], [43312, 43334], - [43360, 43388], [43396, 43442], [43488, 43492], [43495, 43503], [43514, 43518], [43520, 43560], - [43584, 43586], [43588, 43595], [43616, 43631], [43633, 43638], 43642, [43646, 43695], 43697, - [43701, 43702], [43705, 43709], 43712, 43714, [43739, 43740], [43744, 43754], 43762, [43777, 43782], - [43785, 43790], [43793, 43798], [43808, 43814], [43816, 43822], [43968, 44002], [44032, 55203], - [55216, 55238], [55243, 55291], [63744, 64109], [64112, 64217], 64285, [64287, 64296], [64298, 64310], - [64312, 64316], 64318, [64320, 64321], [64323, 64324], [64326, 64433], [64467, 64829], [64848, 64911], - [64914, 64967], [65008, 65019], [65136, 65140], [65142, 65276], [65382, 65391], [65393, 65437], - [65440, 65470], [65474, 65479], [65482, 65487], [65490, 65495], [65498, 65500], [65536, 65547], - [65549, 65574], [65576, 65594], [65596, 65597], [65599, 65613], [65616, 65629], [65664, 65786], - [66176, 66204], [66208, 66256], [66304, 66335], [66349, 66368], [66370, 66377], [66384, 66421], - [66432, 66461], [66464, 66499], [66504, 66511], [66640, 66717], [66816, 66855], [66864, 66915], - [67072, 67382], [67392, 67413], [67424, 67431], [67584, 67589], 67592, [67594, 67637], [67639, 67640], - 67644, [67647, 67669], [67680, 67702], [67712, 67742], [67808, 67826], [67828, 67829], [67840, 67861], - [67872, 67897], [67968, 68023], [68030, 68031], 68096, [68112, 68115], [68117, 68119], [68121, 68147], - [68192, 68220], [68224, 68252], [68288, 68295], [68297, 68324], [68352, 68405], [68416, 68437], - [68448, 68466], [68480, 68497], [68608, 68680], [69635, 69687], [69763, 69807], [69840, 69864], - [69891, 69926], [69968, 70002], 70006, [70019, 70066], [70081, 70084], 70106, 70108, [70144, 70161], - [70163, 70187], [70272, 70278], 70280, [70282, 70285], [70287, 70301], [70303, 70312], [70320, 70366], - [70405, 70412], [70415, 70416], [70419, 70440], [70442, 70448], [70450, 70451], [70453, 70457], 70461, - 70480, [70493, 70497], [70656, 70708], [70727, 70730], [70784, 70831], [70852, 70853], 70855, - [71040, 71086], [71128, 71131], [71168, 71215], 71236, [71296, 71338], [71424, 71449], 71935, 72192, - [72203, 72242], 72250, 72272, [72284, 72323], [72326, 72329], [72384, 72440], [72704, 72712], - [72714, 72750], 72768, [72818, 72847], [72960, 72966], [72968, 72969], [72971, 73008], 73030, - [73728, 74649], [74880, 75075], [77824, 78894], [82944, 83526], [92160, 92728], [92736, 92766], - [92880, 92909], [92928, 92975], [93027, 93047], [93053, 93071], [93952, 94020], 94032, [94208, 100332], - [100352, 101106], [110592, 110878], [110960, 111355], [113664, 113770], [113776, 113788], - [113792, 113800], [113808, 113817], [124928, 125124], [126464, 126467], [126469, 126495], - [126497, 126498], 126500, 126503, [126505, 126514], [126516, 126519], 126521, 126523, 126530, - 126535, 126537, 126539, [126541, 126543], [126545, 126546], 126548, 126551, 126553, 126555, 126557, - 126559, [126561, 126562], 126564, [126567, 126570], [126572, 126578], [126580, 126583], - [126585, 126588], 126590, [126592, 126601], [126603, 126619], [126625, 126627], [126629, 126633], - [126635, 126651], [131072, 173782], [173824, 177972], [177984, 178205], [178208, 183969], - [183984, 191456], [194560, 195101]]; +export const unicodeLo: UnicodeRangeTable = [ + 170, + 186, + 443, + [448, 451], + 660, + [1488, 1514], + [1520, 1522], + [1568, 1599], + [1601, 1610], + [1646, 1647], + [1649, 1747], + 1749, + [1774, 1775], + [1786, 1788], + 1791, + 1808, + [1810, 1839], + [1869, 1957], + 1969, + [1994, 2026], + [2048, 2069], + [2112, 2136], + [2144, 2154], + [2208, 2228], + [2230, 2237], + [2308, 2361], + 2365, + 2384, + [2392, 2401], + [2418, 2432], + [2437, 2444], + [2447, 2448], + [2451, 2472], + [2474, 2480], + 2482, + [2486, 2489], + 2493, + 2510, + [2524, 2525], + [2527, 2529], + [2544, 2545], + 2556, + [2565, 2570], + [2575, 2576], + [2579, 2600], + [2602, 2608], + [2610, 2611], + [2613, 2614], + [2616, 2617], + [2649, 2652], + 2654, + [2674, 2676], + [2693, 2701], + [2703, 2705], + [2707, 2728], + [2730, 2736], + [2738, 2739], + [2741, 2745], + 2749, + 2768, + [2784, 2785], + 2809, + [2821, 2828], + [2831, 2832], + [2835, 2856], + [2858, 2864], + [2866, 2867], + [2869, 2873], + 2877, + [2908, 2909], + [2911, 2913], + 2929, + 2947, + [2949, 2954], + [2958, 2960], + [2962, 2965], + [2969, 2970], + 2972, + [2974, 2975], + [2979, 2980], + [2984, 2986], + [2990, 3001], + 3024, + [3077, 3084], + [3086, 3088], + [3090, 3112], + [3114, 3129], + 3133, + [3160, 3162], + [3168, 3169], + 3200, + [3205, 3212], + [3214, 3216], + [3218, 3240], + [3242, 3251], + [3253, 3257], + 3261, + 3294, + [3296, 3297], + [3313, 3314], + [3333, 3340], + [3342, 3344], + [3346, 3386], + 3389, + 3406, + [3412, 3414], + [3423, 3425], + [3450, 3455], + [3461, 3478], + [3482, 3505], + [3507, 3515], + 3517, + [3520, 3526], + [3585, 3632], + [3634, 3635], + [3648, 3653], + [3713, 3714], + 3716, + [3719, 3720], + 3722, + 3725, + [3732, 3735], + [3737, 3743], + [3745, 3747], + 3749, + 3751, + [3754, 3755], + [3757, 3760], + [3762, 3763], + 3773, + [3776, 3780], + [3804, 3807], + 3840, + [3904, 3911], + [3913, 3948], + [3976, 3980], + [4096, 4138], + 4159, + [4176, 4181], + [4186, 4189], + 4193, + [4197, 4198], + [4206, 4208], + [4213, 4225], + 4238, + [4304, 4346], + [4349, 4680], + [4682, 4685], + [4688, 4694], + 4696, + [4698, 4701], + [4704, 4744], + [4746, 4749], + [4752, 4784], + [4786, 4789], + [4792, 4798], + 4800, + [4802, 4805], + [4808, 4822], + [4824, 4880], + [4882, 4885], + [4888, 4954], + [4992, 5007], + [5121, 5740], + [5743, 5759], + [5761, 5786], + [5792, 5866], + [5873, 5880], + [5888, 5900], + [5902, 5905], + [5920, 5937], + [5952, 5969], + [5984, 5996], + [5998, 6000], + [6016, 6067], + 6108, + [6176, 6210], + [6212, 6263], + [6272, 6276], + [6279, 6312], + 6314, + [6320, 6389], + [6400, 6430], + [6480, 6509], + [6512, 6516], + [6528, 6571], + [6576, 6601], + [6656, 6678], + [6688, 6740], + [6917, 6963], + [6981, 6987], + [7043, 7072], + [7086, 7087], + [7098, 7141], + [7168, 7203], + [7245, 7247], + [7258, 7287], + [7401, 7404], + [7406, 7409], + [7413, 7414], + [8501, 8504], + [11568, 11623], + [11648, 11670], + [11680, 11686], + [11688, 11694], + [11696, 11702], + [11704, 11710], + [11712, 11718], + [11720, 11726], + [11728, 11734], + [11736, 11742], + 12294, + 12348, + [12353, 12438], + 12447, + [12449, 12538], + 12543, + [12549, 12590], + [12593, 12686], + [12704, 12730], + [12784, 12799], + [13312, 19893], + [19968, 40938], + [40960, 40980], + [40982, 42124], + [42192, 42231], + [42240, 42507], + [42512, 42527], + [42538, 42539], + 42606, + [42656, 42725], + 42895, + 42999, + [43003, 43009], + [43011, 43013], + [43015, 43018], + [43020, 43042], + [43072, 43123], + [43138, 43187], + [43250, 43255], + 43259, + 43261, + [43274, 43301], + [43312, 43334], + [43360, 43388], + [43396, 43442], + [43488, 43492], + [43495, 43503], + [43514, 43518], + [43520, 43560], + [43584, 43586], + [43588, 43595], + [43616, 43631], + [43633, 43638], + 43642, + [43646, 43695], + 43697, + [43701, 43702], + [43705, 43709], + 43712, + 43714, + [43739, 43740], + [43744, 43754], + 43762, + [43777, 43782], + [43785, 43790], + [43793, 43798], + [43808, 43814], + [43816, 43822], + [43968, 44002], + [44032, 55203], + [55216, 55238], + [55243, 55291], + [63744, 64109], + [64112, 64217], + 64285, + [64287, 64296], + [64298, 64310], + [64312, 64316], + 64318, + [64320, 64321], + [64323, 64324], + [64326, 64433], + [64467, 64829], + [64848, 64911], + [64914, 64967], + [65008, 65019], + [65136, 65140], + [65142, 65276], + [65382, 65391], + [65393, 65437], + [65440, 65470], + [65474, 65479], + [65482, 65487], + [65490, 65495], + [65498, 65500], + [65536, 65547], + [65549, 65574], + [65576, 65594], + [65596, 65597], + [65599, 65613], + [65616, 65629], + [65664, 65786], + [66176, 66204], + [66208, 66256], + [66304, 66335], + [66349, 66368], + [66370, 66377], + [66384, 66421], + [66432, 66461], + [66464, 66499], + [66504, 66511], + [66640, 66717], + [66816, 66855], + [66864, 66915], + [67072, 67382], + [67392, 67413], + [67424, 67431], + [67584, 67589], + 67592, + [67594, 67637], + [67639, 67640], + 67644, + [67647, 67669], + [67680, 67702], + [67712, 67742], + [67808, 67826], + [67828, 67829], + [67840, 67861], + [67872, 67897], + [67968, 68023], + [68030, 68031], + 68096, + [68112, 68115], + [68117, 68119], + [68121, 68147], + [68192, 68220], + [68224, 68252], + [68288, 68295], + [68297, 68324], + [68352, 68405], + [68416, 68437], + [68448, 68466], + [68480, 68497], + [68608, 68680], + [69635, 69687], + [69763, 69807], + [69840, 69864], + [69891, 69926], + [69968, 70002], + 70006, + [70019, 70066], + [70081, 70084], + 70106, + 70108, + [70144, 70161], + [70163, 70187], + [70272, 70278], + 70280, + [70282, 70285], + [70287, 70301], + [70303, 70312], + [70320, 70366], + [70405, 70412], + [70415, 70416], + [70419, 70440], + [70442, 70448], + [70450, 70451], + [70453, 70457], + 70461, + 70480, + [70493, 70497], + [70656, 70708], + [70727, 70730], + [70784, 70831], + [70852, 70853], + 70855, + [71040, 71086], + [71128, 71131], + [71168, 71215], + 71236, + [71296, 71338], + [71424, 71449], + 71935, + 72192, + [72203, 72242], + 72250, + 72272, + [72284, 72323], + [72326, 72329], + [72384, 72440], + [72704, 72712], + [72714, 72750], + 72768, + [72818, 72847], + [72960, 72966], + [72968, 72969], + [72971, 73008], + 73030, + [73728, 74649], + [74880, 75075], + [77824, 78894], + [82944, 83526], + [92160, 92728], + [92736, 92766], + [92880, 92909], + [92928, 92975], + [93027, 93047], + [93053, 93071], + [93952, 94020], + 94032, + [94208, 100332], + [100352, 101106], + [110592, 110878], + [110960, 111355], + [113664, 113770], + [113776, 113788], + [113792, 113800], + [113808, 113817], + [124928, 125124], + [126464, 126467], + [126469, 126495], + [126497, 126498], + 126500, + 126503, + [126505, 126514], + [126516, 126519], + 126521, + 126523, + 126530, + 126535, + 126537, + 126539, + [126541, 126543], + [126545, 126546], + 126548, + 126551, + 126553, + 126555, + 126557, + 126559, + [126561, 126562], + 126564, + [126567, 126570], + [126572, 126578], + [126580, 126583], + [126585, 126588], + 126590, + [126592, 126601], + [126603, 126619], + [126625, 126627], + [126629, 126633], + [126635, 126651], + [131072, 173782], + [173824, 177972], + [177984, 178205], + [178208, 183969], + [183984, 191456], + [194560, 195101] +]; -export const unicodeLm: UnicodeRangeTable = [[688, 705], [710, 721], [736, 740], 748, 750, 884, 890, 1369, - 1600, [1765, 1766], [2036, 2037], 2042, 2074, 2084, 2088, 2417, 3654, 3782, 4348, 6103, 6211, 6823, - [7288, 7293], [7468, 7530], 7544, [7579, 7615], 8305, 8319, [8336, 8348], [11388, 11389], 11631, 11823, - 12293, [12337, 12341], 12347, [12445, 12446], [12540, 12542], 40981, [42232, 42237], 42508, 42623, - [42652, 42653], [42775, 42783], 42864, 42888, [43000, 43001], 43471, 43494, 43632, 43741, - [43763, 43764], [43868, 43871], 65392, [65438, 65439], [92992, 92995], [94099, 94111], [94176, 94177]]; +export const unicodeLm: UnicodeRangeTable = [ + [688, 705], + [710, 721], + [736, 740], + 748, + 750, + 884, + 890, + 1369, + 1600, + [1765, 1766], + [2036, 2037], + 2042, + 2074, + 2084, + 2088, + 2417, + 3654, + 3782, + 4348, + 6103, + 6211, + 6823, + [7288, 7293], + [7468, 7530], + 7544, + [7579, 7615], + 8305, + 8319, + [8336, 8348], + [11388, 11389], + 11631, + 11823, + 12293, + [12337, 12341], + 12347, + [12445, 12446], + [12540, 12542], + 40981, + [42232, 42237], + 42508, + 42623, + [42652, 42653], + [42775, 42783], + 42864, + 42888, + [43000, 43001], + 43471, + 43494, + 43632, + 43741, + [43763, 43764], + [43868, 43871], + 65392, + [65438, 65439], + [92992, 92995], + [94099, 94111], + [94176, 94177] +]; -export const unicodeNl: UnicodeRangeTable = [[5870, 5872], [8544, 8578], [8581, 8584], 12295, [12321, 12329], - [12344, 12346], [42726, 42735], [65856, 65908], 66369, 66378, [66513, 66517], [74752, 74862]]; +export const unicodeNl: UnicodeRangeTable = [ + [5870, 5872], + [8544, 8578], + [8581, 8584], + 12295, + [12321, 12329], + [12344, 12346], + [42726, 42735], + [65856, 65908], + 66369, + 66378, + [66513, 66517], + [74752, 74862] +]; -export const unicodeMn: UnicodeRangeTable = [[768, 879], [1155, 1159], [1425, 1469], 1471, [1473, 1474], - [1476, 1477], 1479, [1552, 1562], [1611, 1631], 1648, [1750, 1756], [1759, 1764], [1767, 1768], - [1770, 1773], 1809, [1840, 1866], [1958, 1968], [2027, 2035], [2070, 2073], [2075, 2083], [2085, 2087], - [2089, 2093], [2137, 2139], [2260, 2273], [2275, 2306], 2362, 2364, [2369, 2376], 2381, [2385, 2391], - [2402, 2403], 2433, 2492, [2497, 2500], 2509, [2530, 2531], [2561, 2562], 2620, [2625, 2626], [2631, 2632], - [2635, 2637], 2641, [2672, 2673], 2677, [2689, 2690], 2748, [2753, 2757], [2759, 2760], 2765, [2786, 2787], - [2810, 2815], 2817, 2876, 2879, [2881, 2884], 2893, 2902, [2914, 2915], 2946, 3008, 3021, 3072, - [3134, 3136], [3142, 3144], [3146, 3149], [3157, 3158], [3170, 3171], 3201, 3260, 3263, 3270, - [3276, 3277], [3298, 3299], [3328, 3329], [3387, 3388], [3393, 3396], 3405, [3426, 3427], 3530, - [3538, 3540], 3542, 3633, [3636, 3642], [3655, 3662], 3761, [3764, 3769], [3771, 3772], [3784, 3789], - [3864, 3865], 3893, 3895, 3897, [3953, 3966], [3968, 3972], [3974, 3975], [3981, 3991], [3993, 4028], - 4038, [4141, 4144], [4146, 4151], [4153, 4154], [4157, 4158], [4184, 4185], [4190, 4192], [4209, 4212], - 4226, [4229, 4230], 4237, 4253, [4957, 4959], [5906, 5908], [5938, 5940], [5970, 5971], [6002, 6003], - [6068, 6069], [6071, 6077], 6086, [6089, 6099], 6109, [6155, 6157], [6277, 6278], 6313, [6432, 6434], - [6439, 6440], 6450, [6457, 6459], [6679, 6680], 6683, 6742, [6744, 6750], 6752, 6754, [6757, 6764], - [6771, 6780], 6783, [6832, 6845], [6912, 6915], 6964, [6966, 6970], 6972, 6978, [7019, 7027], - [7040, 7041], [7074, 7077], [7080, 7081], [7083, 7085], 7142, [7144, 7145], 7149, [7151, 7153], - [7212, 7219], [7222, 7223], [7376, 7378], [7380, 7392], [7394, 7400], 7405, 7412, [7416, 7417], - [7616, 7673], [7675, 7679], [8400, 8412], 8417, [8421, 8432], [11503, 11505], 11647, [11744, 11775], - [12330, 12333], [12441, 12442], 42607, [42612, 42621], [42654, 42655], [42736, 42737], 43010, 43014, - 43019, [43045, 43046], [43204, 43205], [43232, 43249], [43302, 43309], [43335, 43345], [43392, 43394], - 43443, [43446, 43449], 43452, 43493, [43561, 43566], [43569, 43570], [43573, 43574], 43587, 43596, - 43644, 43696, [43698, 43700], [43703, 43704], [43710, 43711], 43713, [43756, 43757], 43766, 44005, - 44008, 44013, 64286, [65024, 65039], [65056, 65071], 66045, 66272, [66422, 66426], [68097, 68099], - [68101, 68102], [68108, 68111], [68152, 68154], 68159, [68325, 68326], 69633, [69688, 69702], - [69759, 69761], [69811, 69814], [69817, 69818], [69888, 69890], [69927, 69931], [69933, 69940], - 70003, [70016, 70017], [70070, 70078], [70090, 70092], [70191, 70193], 70196, [70198, 70199], 70206, - 70367, [70371, 70378], [70400, 70401], 70460, 70464, [70502, 70508], [70512, 70516], [70712, 70719], - [70722, 70724], 70726, [70835, 70840], 70842, [70847, 70848], [70850, 70851], [71090, 71093], - [71100, 71101], [71103, 71104], [71132, 71133], [71219, 71226], 71229, [71231, 71232], 71339, 71341, - [71344, 71349], 71351, [71453, 71455], [71458, 71461], [71463, 71467], [72193, 72198], [72201, 72202], - [72243, 72248], [72251, 72254], 72263, [72273, 72278], [72281, 72283], [72330, 72342], [72344, 72345], - [72752, 72758], [72760, 72765], 72767, [72850, 72871], [72874, 72880], [72882, 72883], [72885, 72886], - [73009, 73014], 73018, [73020, 73021], [73023, 73029], 73031, [92912, 92916], [92976, 92982], - [94095, 94098], [113821, 113822], [119143, 119145], [119163, 119170], [119173, 119179], [119210, 119213], - [119362, 119364], [121344, 121398], [121403, 121452], 121461, 121476, [121499, 121503], [121505, 121519], - [122880, 122886], [122888, 122904], [122907, 122913], [122915, 122916], [122918, 122922], [125136, 125142], - [125252, 125258], [917760, 917999]]; +export const unicodeMn: UnicodeRangeTable = [ + [768, 879], + [1155, 1159], + [1425, 1469], + 1471, + [1473, 1474], + [1476, 1477], + 1479, + [1552, 1562], + [1611, 1631], + 1648, + [1750, 1756], + [1759, 1764], + [1767, 1768], + [1770, 1773], + 1809, + [1840, 1866], + [1958, 1968], + [2027, 2035], + [2070, 2073], + [2075, 2083], + [2085, 2087], + [2089, 2093], + [2137, 2139], + [2260, 2273], + [2275, 2306], + 2362, + 2364, + [2369, 2376], + 2381, + [2385, 2391], + [2402, 2403], + 2433, + 2492, + [2497, 2500], + 2509, + [2530, 2531], + [2561, 2562], + 2620, + [2625, 2626], + [2631, 2632], + [2635, 2637], + 2641, + [2672, 2673], + 2677, + [2689, 2690], + 2748, + [2753, 2757], + [2759, 2760], + 2765, + [2786, 2787], + [2810, 2815], + 2817, + 2876, + 2879, + [2881, 2884], + 2893, + 2902, + [2914, 2915], + 2946, + 3008, + 3021, + 3072, + [3134, 3136], + [3142, 3144], + [3146, 3149], + [3157, 3158], + [3170, 3171], + 3201, + 3260, + 3263, + 3270, + [3276, 3277], + [3298, 3299], + [3328, 3329], + [3387, 3388], + [3393, 3396], + 3405, + [3426, 3427], + 3530, + [3538, 3540], + 3542, + 3633, + [3636, 3642], + [3655, 3662], + 3761, + [3764, 3769], + [3771, 3772], + [3784, 3789], + [3864, 3865], + 3893, + 3895, + 3897, + [3953, 3966], + [3968, 3972], + [3974, 3975], + [3981, 3991], + [3993, 4028], + 4038, + [4141, 4144], + [4146, 4151], + [4153, 4154], + [4157, 4158], + [4184, 4185], + [4190, 4192], + [4209, 4212], + 4226, + [4229, 4230], + 4237, + 4253, + [4957, 4959], + [5906, 5908], + [5938, 5940], + [5970, 5971], + [6002, 6003], + [6068, 6069], + [6071, 6077], + 6086, + [6089, 6099], + 6109, + [6155, 6157], + [6277, 6278], + 6313, + [6432, 6434], + [6439, 6440], + 6450, + [6457, 6459], + [6679, 6680], + 6683, + 6742, + [6744, 6750], + 6752, + 6754, + [6757, 6764], + [6771, 6780], + 6783, + [6832, 6845], + [6912, 6915], + 6964, + [6966, 6970], + 6972, + 6978, + [7019, 7027], + [7040, 7041], + [7074, 7077], + [7080, 7081], + [7083, 7085], + 7142, + [7144, 7145], + 7149, + [7151, 7153], + [7212, 7219], + [7222, 7223], + [7376, 7378], + [7380, 7392], + [7394, 7400], + 7405, + 7412, + [7416, 7417], + [7616, 7673], + [7675, 7679], + [8400, 8412], + 8417, + [8421, 8432], + [11503, 11505], + 11647, + [11744, 11775], + [12330, 12333], + [12441, 12442], + 42607, + [42612, 42621], + [42654, 42655], + [42736, 42737], + 43010, + 43014, + 43019, + [43045, 43046], + [43204, 43205], + [43232, 43249], + [43302, 43309], + [43335, 43345], + [43392, 43394], + 43443, + [43446, 43449], + 43452, + 43493, + [43561, 43566], + [43569, 43570], + [43573, 43574], + 43587, + 43596, + 43644, + 43696, + [43698, 43700], + [43703, 43704], + [43710, 43711], + 43713, + [43756, 43757], + 43766, + 44005, + 44008, + 44013, + 64286, + [65024, 65039], + [65056, 65071], + 66045, + 66272, + [66422, 66426], + [68097, 68099], + [68101, 68102], + [68108, 68111], + [68152, 68154], + 68159, + [68325, 68326], + 69633, + [69688, 69702], + [69759, 69761], + [69811, 69814], + [69817, 69818], + [69888, 69890], + [69927, 69931], + [69933, 69940], + 70003, + [70016, 70017], + [70070, 70078], + [70090, 70092], + [70191, 70193], + 70196, + [70198, 70199], + 70206, + 70367, + [70371, 70378], + [70400, 70401], + 70460, + 70464, + [70502, 70508], + [70512, 70516], + [70712, 70719], + [70722, 70724], + 70726, + [70835, 70840], + 70842, + [70847, 70848], + [70850, 70851], + [71090, 71093], + [71100, 71101], + [71103, 71104], + [71132, 71133], + [71219, 71226], + 71229, + [71231, 71232], + 71339, + 71341, + [71344, 71349], + 71351, + [71453, 71455], + [71458, 71461], + [71463, 71467], + [72193, 72198], + [72201, 72202], + [72243, 72248], + [72251, 72254], + 72263, + [72273, 72278], + [72281, 72283], + [72330, 72342], + [72344, 72345], + [72752, 72758], + [72760, 72765], + 72767, + [72850, 72871], + [72874, 72880], + [72882, 72883], + [72885, 72886], + [73009, 73014], + 73018, + [73020, 73021], + [73023, 73029], + 73031, + [92912, 92916], + [92976, 92982], + [94095, 94098], + [113821, 113822], + [119143, 119145], + [119163, 119170], + [119173, 119179], + [119210, 119213], + [119362, 119364], + [121344, 121398], + [121403, 121452], + 121461, + 121476, + [121499, 121503], + [121505, 121519], + [122880, 122886], + [122888, 122904], + [122907, 122913], + [122915, 122916], + [122918, 122922], + [125136, 125142], + [125252, 125258], + [917760, 917999] +]; -export const unicodeMc: UnicodeRangeTable = [2307, 2363, [2366, 2368], [2377, 2380], [2382, 2383], [2434, 2435], - [2494, 2496], [2503, 2504], [2507, 2508], 2519, 2563, [2622, 2624], 2691, [2750, 2752], 2761, [2763, 2764], - [2818, 2819], 2878, 2880, [2887, 2888], [2891, 2892], 2903, [3006, 3007], [3009, 3010], [3014, 3016], - [3018, 3020], 3031, [3073, 3075], [3137, 3140], [3202, 3203], 3262, [3264, 3268], [3271, 3272], - [3274, 3275], [3285, 3286], [3330, 3331], [3390, 3392], [3398, 3400], [3402, 3404], 3415, [3458, 3459], - [3535, 3537], [3544, 3551], [3570, 3571], [3902, 3903], 3967, [4139, 4140], 4145, 4152, [4155, 4156], - [4182, 4183], [4194, 4196], [4199, 4205], [4227, 4228], [4231, 4236], 4239, [4250, 4252], 6070, [6078, 6085], - [6087, 6088], [6435, 6438], [6441, 6443], [6448, 6449], [6451, 6456], [6681, 6682], 6741, 6743, 6753, - [6755, 6756], [6765, 6770], 6916, 6965, 6971, [6973, 6977], [6979, 6980], 7042, 7073, [7078, 7079], 7082, - 7143, [7146, 7148], 7150, [7154, 7155], [7204, 7211], [7220, 7221], 7393, [7410, 7411], 7415, [12334, 12335], - [43043, 43044], 43047, [43136, 43137], [43188, 43203], [43346, 43347], 43395, [43444, 43445], [43450, 43451], - [43453, 43456], [43567, 43568], [43571, 43572], 43597, 43643, 43645, 43755, [43758, 43759], 43765, - [44003, 44004], [44006, 44007], [44009, 44010], 44012, 69632, 69634, 69762, [69808, 69810], [69815, 69816], - 69932, 70018, [70067, 70069], [70079, 70080], [70188, 70190], [70194, 70195], 70197, [70368, 70370], - [70402, 70403], [70462, 70463], [70465, 70468], [70471, 70472], [70475, 70477], 70487, [70498, 70499], - [70709, 70711], [70720, 70721], 70725, [70832, 70834], 70841, [70843, 70846], 70849, [71087, 71089], - [71096, 71099], 71102, [71216, 71218], [71227, 71228], 71230, 71340, [71342, 71343], 71350, [71456, 71457], - 71462, [72199, 72200], 72249, [72279, 72280], 72343, 72751, 72766, 72873, 72881, 72884, [94033, 94078], - [119141, 119142], [119149, 119154]]; +export const unicodeMc: UnicodeRangeTable = [ + 2307, + 2363, + [2366, 2368], + [2377, 2380], + [2382, 2383], + [2434, 2435], + [2494, 2496], + [2503, 2504], + [2507, 2508], + 2519, + 2563, + [2622, 2624], + 2691, + [2750, 2752], + 2761, + [2763, 2764], + [2818, 2819], + 2878, + 2880, + [2887, 2888], + [2891, 2892], + 2903, + [3006, 3007], + [3009, 3010], + [3014, 3016], + [3018, 3020], + 3031, + [3073, 3075], + [3137, 3140], + [3202, 3203], + 3262, + [3264, 3268], + [3271, 3272], + [3274, 3275], + [3285, 3286], + [3330, 3331], + [3390, 3392], + [3398, 3400], + [3402, 3404], + 3415, + [3458, 3459], + [3535, 3537], + [3544, 3551], + [3570, 3571], + [3902, 3903], + 3967, + [4139, 4140], + 4145, + 4152, + [4155, 4156], + [4182, 4183], + [4194, 4196], + [4199, 4205], + [4227, 4228], + [4231, 4236], + 4239, + [4250, 4252], + 6070, + [6078, 6085], + [6087, 6088], + [6435, 6438], + [6441, 6443], + [6448, 6449], + [6451, 6456], + [6681, 6682], + 6741, + 6743, + 6753, + [6755, 6756], + [6765, 6770], + 6916, + 6965, + 6971, + [6973, 6977], + [6979, 6980], + 7042, + 7073, + [7078, 7079], + 7082, + 7143, + [7146, 7148], + 7150, + [7154, 7155], + [7204, 7211], + [7220, 7221], + 7393, + [7410, 7411], + 7415, + [12334, 12335], + [43043, 43044], + 43047, + [43136, 43137], + [43188, 43203], + [43346, 43347], + 43395, + [43444, 43445], + [43450, 43451], + [43453, 43456], + [43567, 43568], + [43571, 43572], + 43597, + 43643, + 43645, + 43755, + [43758, 43759], + 43765, + [44003, 44004], + [44006, 44007], + [44009, 44010], + 44012, + 69632, + 69634, + 69762, + [69808, 69810], + [69815, 69816], + 69932, + 70018, + [70067, 70069], + [70079, 70080], + [70188, 70190], + [70194, 70195], + 70197, + [70368, 70370], + [70402, 70403], + [70462, 70463], + [70465, 70468], + [70471, 70472], + [70475, 70477], + 70487, + [70498, 70499], + [70709, 70711], + [70720, 70721], + 70725, + [70832, 70834], + 70841, + [70843, 70846], + 70849, + [71087, 71089], + [71096, 71099], + 71102, + [71216, 71218], + [71227, 71228], + 71230, + 71340, + [71342, 71343], + 71350, + [71456, 71457], + 71462, + [72199, 72200], + 72249, + [72279, 72280], + 72343, + 72751, + 72766, + 72873, + 72881, + 72884, + [94033, 94078], + [119141, 119142], + [119149, 119154] +]; -export const unicodeNd: UnicodeRangeTable = [[48, 57], [1632, 1641], [1776, 1785], [1984, 1993], [2406, 2415], - [2534, 2543], [2662, 2671], [2790, 2799], [2918, 2927], [3046, 3055], [3174, 3183], [3302, 3311], [3430, 3439], - [3558, 3567], [3664, 3673], [3792, 3801], [3872, 3881], [4160, 4169], [4240, 4249], [6112, 6121], [6160, 6169], - [6470, 6479], [6608, 6617], [6784, 6793], [6800, 6809], [6992, 7001], [7088, 7097], [7232, 7241], [7248, 7257], - [42528, 42537], [43216, 43225], [43264, 43273], [43472, 43481], [43504, 43513], [43600, 43609], [44016, 44025], - [65296, 65305], [66720, 66729], [69734, 69743], [69872, 69881], [69942, 69951], [70096, 70105], [70384, 70393], - [70736, 70745], [70864, 70873], [71248, 71257], [71360, 71369], [71472, 71481], [71904, 71913], [72784, 72793], - [73040, 73049], [92768, 92777], [93008, 93017], [120782, 120831], [125264, 125273]]; +export const unicodeNd: UnicodeRangeTable = [ + [48, 57], + [1632, 1641], + [1776, 1785], + [1984, 1993], + [2406, 2415], + [2534, 2543], + [2662, 2671], + [2790, 2799], + [2918, 2927], + [3046, 3055], + [3174, 3183], + [3302, 3311], + [3430, 3439], + [3558, 3567], + [3664, 3673], + [3792, 3801], + [3872, 3881], + [4160, 4169], + [4240, 4249], + [6112, 6121], + [6160, 6169], + [6470, 6479], + [6608, 6617], + [6784, 6793], + [6800, 6809], + [6992, 7001], + [7088, 7097], + [7232, 7241], + [7248, 7257], + [42528, 42537], + [43216, 43225], + [43264, 43273], + [43472, 43481], + [43504, 43513], + [43600, 43609], + [44016, 44025], + [65296, 65305], + [66720, 66729], + [69734, 69743], + [69872, 69881], + [69942, 69951], + [70096, 70105], + [70384, 70393], + [70736, 70745], + [70864, 70873], + [71248, 71257], + [71360, 71369], + [71472, 71481], + [71904, 71913], + [72784, 72793], + [73040, 73049], + [92768, 92777], + [93008, 93017], + [120782, 120831], + [125264, 125273] +]; export const unicodePc: UnicodeRangeTable = [95, [8255, 8256], 8276, [65075, 65076], [65101, 65103], 65343]; diff --git a/server/src/pyright.ts b/server/src/pyright.ts index bcdfd571d..994f5dfb7 100644 --- a/server/src/pyright.ts +++ b/server/src/pyright.ts @@ -1,11 +1,11 @@ /* -* pyright.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* Author: Eric Traut -* -* Command-line entry point for pyright type checker. -*/ + * pyright.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * Author: Eric Traut + * + * Command-line entry point for pyright type checker. + */ // Add the start timer at the very top of the file, before we import other modules. @@ -87,11 +87,11 @@ function processArgs() { } catch (err) { const argErr: { name: string; optionName: string } = err; if (argErr && argErr.optionName) { - console.error(`Unexpected option ${ argErr.optionName }.\n${ toolName } --help for usage`); + console.error(`Unexpected option ${argErr.optionName}.\n${toolName} --help for usage`); return; } - console.error(`Unexpected error\n${ toolName } --help for usage`); + console.error(`Unexpected error\n${toolName} --help for usage`); return; } @@ -109,7 +109,7 @@ function processArgs() { const incompatibleArgs = ['watch', 'stats', 'verbose', 'createstub', 'dependencies']; for (const arg of incompatibleArgs) { if (args[arg] !== undefined) { - console.error(`'outputjson' option cannot be used with '${ arg }' option`); + console.error(`'outputjson' option cannot be used with '${arg}' option`); return; } } @@ -168,8 +168,11 @@ function processArgs() { let errorCount = 0; if (results.diagnostics.length > 0 && !args.createstub) { if (args.outputjson) { - const report = reportDiagnosticsAsJson(results.diagnostics, - results.filesInProgram, results.elapsedTime); + const report = reportDiagnosticsAsJson( + results.diagnostics, + results.filesInProgram, + results.elapsedTime + ); errorCount += report.errorCount; } else { const report = reportDiagnosticsAsText(results.diagnostics); @@ -181,7 +184,7 @@ function processArgs() { try { service.writeTypeStub(); service.dispose(); - console.log(`Type stub was created for '${ args.createstub }'`); + console.log(`Type stub was created for '${args.createstub}'`); } catch (err) { let errMessage = ''; if (err instanceof Error) { @@ -209,10 +212,7 @@ function processArgs() { } if (!watch) { - process.exit( - errorCount > 0 ? - ExitStatus.ErrorsReported : - ExitStatus.NoErrors); + process.exit(errorCount > 0 ? ExitStatus.ErrorsReported : ExitStatus.NoErrors); } else { console.log('Watching for file changes...'); } @@ -230,20 +230,22 @@ function processArgs() { function printUsage() { console.log( - 'Usage: ' + toolName + ' [options] files...\n' + - ' Options:\n' + - ' --createstub IMPORT Create type stub file(s) for import\n' + - ' --dependencies Emit import dependency information\n' + - ' -h,--help Show this help message\n' + - ' --lib Use library code to infer types when stubs are missing\n' + - ' --outputjson Output results in JSON format\n' + - ' -p,--project FILE OR DIRECTORY Use the configuration file at this location\n' + - ' --stats Print detailed performance stats\n' + - ' -t,--typeshed-path DIRECTORY Use typeshed type stubs at this location\n' + - ' -v,--venv-path DIRECTORY Directory that contains virtual environments\n' + - ' --verbose Emit verbose diagnostics\n' + - ' --version Print Pyright version\n' + - ' -w,--watch Continue to run and watch for changes\n' + 'Usage: ' + + toolName + + ' [options] files...\n' + + ' Options:\n' + + ' --createstub IMPORT Create type stub file(s) for import\n' + + ' --dependencies Emit import dependency information\n' + + ' -h,--help Show this help message\n' + + ' --lib Use library code to infer types when stubs are missing\n' + + ' --outputjson Output results in JSON format\n' + + ' -p,--project FILE OR DIRECTORY Use the configuration file at this location\n' + + ' --stats Print detailed performance stats\n' + + ' -t,--typeshed-path DIRECTORY Use typeshed type stubs at this location\n' + + ' -v,--venv-path DIRECTORY Directory that contains virtual environments\n' + + ' --verbose Emit verbose diagnostics\n' + + ' --version Print Pyright version\n' + + ' -w,--watch Continue to run and watch for changes\n' ); } @@ -253,12 +255,14 @@ function getVersionString() { } function printVersion() { - console.log(`${ toolName } ${ getVersionString() }`); + console.log(`${toolName} ${getVersionString()}`); } -function reportDiagnosticsAsJson(fileDiagnostics: FileDiagnostics[], filesInProgram: number, - timeInSec: number): DiagnosticResult { - +function reportDiagnosticsAsJson( + fileDiagnostics: FileDiagnostics[], + filesInProgram: number, + timeInSec: number +): DiagnosticResult { const report: PyrightJsonResults = { version: getVersionString(), time: Date.now().toString(), @@ -312,24 +316,27 @@ function reportDiagnosticsAsText(fileDiagnostics: FileDiagnostics[]): Diagnostic fileDiagnostics.forEach(fileDiagnostics => { // Don't report unused code diagnostics. const fileErrorsAndWarnings = fileDiagnostics.diagnostics.filter( - diag => diag.category !== DiagnosticCategory.UnusedCode); + diag => diag.category !== DiagnosticCategory.UnusedCode + ); if (fileErrorsAndWarnings.length > 0) { - console.log(`${ fileDiagnostics.filePath }`); + console.log(`${fileDiagnostics.filePath}`); fileErrorsAndWarnings.forEach(diag => { let message = ' '; if (diag.range) { - message += chalk.yellow(`${ diag.range.start.line + 1 }`) + ':' + - chalk.yellow(`${ diag.range.start.character + 1 }`) + ' - '; + message += + chalk.yellow(`${diag.range.start.line + 1}`) + + ':' + + chalk.yellow(`${diag.range.start.character + 1}`) + + ' - '; } - message += diag.category === DiagnosticCategory.Error ? - chalk.red('error') : chalk.green('warning'); - message += `: ${ diag.message }`; + message += diag.category === DiagnosticCategory.Error ? chalk.red('error') : chalk.green('warning'); + message += `: ${diag.message}`; const rule = diag.getRule(); if (rule) { - message += chalk.gray(` (${ rule })`); + message += chalk.gray(` (${rule})`); } console.log(message); @@ -344,8 +351,9 @@ function reportDiagnosticsAsText(fileDiagnostics: FileDiagnostics[]): Diagnostic }); console.log( - `${ errorCount.toString() } ${ errorCount === 1 ? 'error' : 'errors' }, ` + - `${ warningCount.toString() } ${ warningCount === 1 ? 'warning' : 'warnings' } `); + `${errorCount.toString()} ${errorCount === 1 ? 'error' : 'errors'}, ` + + `${warningCount.toString()} ${warningCount === 1 ? 'warning' : 'warnings'} ` + ); return { errorCount, diff --git a/server/src/server.ts b/server/src/server.ts index 46e0328de..f9bbfa513 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -49,7 +49,7 @@ class Server extends LanguageServerBase { serverSettings.disableLanguageServices = false; } } catch (error) { - this.console.log(`Error reading settings: ${ error }`); + this.console.log(`Error reading settings: ${error}`); } return serverSettings; } diff --git a/server/src/tests/checker.test.ts b/server/src/tests/checker.test.ts index 82ccdd2ca..9d64c02e3 100644 --- a/server/src/tests/checker.test.ts +++ b/server/src/tests/checker.test.ts @@ -29,37 +29,157 @@ test('Builtins1', () => { // `dir(builtins)` // Remove True, False, None, _, __build_class__, __debug__, __doc__ const expectedBuiltinsSymbols = [ - 'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', - 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', - 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', - 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', - 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', - 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', - 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', - 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', - 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'ModuleNotFoundError', - 'MemoryError', 'NameError', 'NotADirectoryError', 'NotImplemented', - 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', - 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', - 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', - 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', - 'TabError', 'TimeoutError', 'TypeError', 'UnboundLocalError', - 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', - 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', + 'ArithmeticError', + 'AssertionError', + 'AttributeError', + 'BaseException', + 'BlockingIOError', + 'BrokenPipeError', + 'BufferError', + 'BytesWarning', + 'ChildProcessError', + 'ConnectionAbortedError', + 'ConnectionError', + 'ConnectionRefusedError', + 'ConnectionResetError', + 'DeprecationWarning', + 'EOFError', + 'Ellipsis', + 'EnvironmentError', + 'Exception', + 'FileExistsError', + 'FileNotFoundError', + 'FloatingPointError', + 'FutureWarning', + 'GeneratorExit', + 'IOError', + 'ImportError', + 'ImportWarning', + 'IndentationError', + 'IndexError', + 'InterruptedError', + 'IsADirectoryError', + 'KeyError', + 'KeyboardInterrupt', + 'LookupError', + 'ModuleNotFoundError', + 'MemoryError', + 'NameError', + 'NotADirectoryError', + 'NotImplemented', + 'NotImplementedError', + 'OSError', + 'OverflowError', + 'PendingDeprecationWarning', + 'PermissionError', + 'ProcessLookupError', + 'RecursionError', + 'ReferenceError', + 'ResourceWarning', + 'RuntimeError', + 'RuntimeWarning', + 'StopAsyncIteration', + 'StopIteration', + 'SyntaxError', + 'SyntaxWarning', + 'SystemError', + 'SystemExit', + 'TabError', + 'TimeoutError', + 'TypeError', + 'UnboundLocalError', + 'UnicodeDecodeError', + 'UnicodeEncodeError', + 'UnicodeError', + 'UnicodeTranslateError', + 'UnicodeWarning', + 'UserWarning', + 'ValueError', + 'Warning', + 'WindowsError', 'ZeroDivisionError', - '__import__', '__loader__', '__name__', - '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', - 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', - 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', - 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', - 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', - 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', - 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', - 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', - 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', - 'vars', 'zip', + '__import__', + '__loader__', + '__name__', + '__package__', + '__spec__', + 'abs', + 'all', + 'any', + 'ascii', + 'bin', + 'bool', + 'breakpoint', + 'bytearray', + 'bytes', + 'callable', + 'chr', + 'classmethod', + 'compile', + 'complex', + 'copyright', + 'credits', + 'delattr', + 'dict', + 'dir', + 'divmod', + 'enumerate', + 'eval', + 'exec', + 'exit', + 'filter', + 'float', + 'format', + 'frozenset', + 'getattr', + 'globals', + 'hasattr', + 'hash', + 'help', + 'hex', + 'id', + 'input', + 'int', + 'isinstance', + 'issubclass', + 'iter', + 'len', + 'license', + 'list', + 'locals', + 'map', + 'max', + 'memoryview', + 'min', + 'next', + 'object', + 'oct', + 'open', + 'ord', + 'pow', + 'print', + 'property', + 'quit', + 'range', + 'repr', + 'reversed', + 'round', + 'set', + 'setattr', + 'slice', + 'sorted', + 'staticmethod', + 'str', + 'sum', + 'super', + 'tuple', + 'type', + 'vars', + 'zip', // These really shouldn't be exposed but are defined by builtins.pyi currently. - 'function', 'ellipsis']; + 'function', + 'ellipsis' + ]; const moduleScope = AnalyzerNodeInfo.getScope(analysisResults[0].parseResults!.parseTree)!; assert.notEqual(moduleScope, undefined); @@ -73,7 +193,7 @@ test('Builtins1', () => { for (const symbolName of expectedBuiltinsSymbols) { const symbol = moduleScope.lookUpSymbolRecursive(symbolName); if (symbol === undefined) { - assert.fail(`${ symbolName } is missing from builtins scope`); + assert.fail(`${symbolName} is missing from builtins scope`); } } @@ -88,15 +208,13 @@ test('Builtins1', () => { const symbolInfo = moduleScope.lookUpSymbolRecursive(builtinName); if (symbolInfo && symbolInfo.isBeyondExecutionScope) { if (symbolMap.get(builtinName) === undefined) { - assert.fail(`${ builtinName } should not be in builtins scope`); + assert.fail(`${builtinName} should not be in builtins scope`); } } } }); -function validateResults(results: TestUtils.FileAnalysisResult[], - errorCount: number, warningCount = 0) { - +function validateResults(results: TestUtils.FileAnalysisResult[], errorCount: number, warningCount = 0) { assert.equal(results.length, 1); assert.equal(results[0].errors.length, errorCount); assert.equal(results[0].warnings.length, warningCount); diff --git a/server/src/tests/collectionUtils.test.ts b/server/src/tests/collectionUtils.test.ts index 52004367a..b7d4d476e 100644 --- a/server/src/tests/collectionUtils.test.ts +++ b/server/src/tests/collectionUtils.test.ts @@ -36,29 +36,44 @@ test('UtilsAppendUndefinedValue', () => { test('UtilsFindEmpty', () => { const data: number[] = []; - assert.equal(utils.find(data, e => true), undefined); + assert.equal( + utils.find(data, e => true), + undefined + ); }); test('UtilsFindNoMatch', () => { const data = [1]; - assert.equal(utils.find(data, e => false), undefined); + assert.equal( + utils.find(data, e => false), + undefined + ); }); test('UtilsFindMatchSimple', () => { const data = [1]; - assert.equal(utils.find(data, e => e === 1), 1); + assert.equal( + utils.find(data, e => e === 1), + 1 + ); }); test('UtilsFindMatch', () => { const data = [new D(1, 'Hello')]; - assert.equal(utils.find(data, e => e.value === 1), data[0]); + assert.equal( + utils.find(data, e => e.value === 1), + data[0] + ); }); test('UtilsFindMatchCovariant', () => { const item1 = new D(1, 'Hello'); const item2 = new D(2, 'Hello2'); const data: B[] = [new B(0), item1, item2, new B(3)]; - assert.equal(utils.find(data, (e: D) => e.value === 2), item2); + assert.equal( + utils.find(data, (e: D) => e.value === 2), + item2 + ); }); test('UtilsStableSort', () => { @@ -126,7 +141,11 @@ test('cloneAndSort', () => { }); test('flatten', () => { - const data: number[][] = [[1, 2], [3, 4], [5, 6]]; + const data: number[][] = [ + [1, 2], + [3, 4], + [5, 6] + ]; assert.deepEqual(utils.flatten(data), [1, 2, 3, 4, 5, 6]); }); diff --git a/server/src/tests/config.test.ts b/server/src/tests/config.test.ts index 542634100..422c5772a 100644 --- a/server/src/tests/config.test.ts +++ b/server/src/tests/config.test.ts @@ -13,11 +13,11 @@ import { AnalyzerService } from '../analyzer/service'; import { CommandLineOptions } from '../common/commandLineOptions'; import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions'; import { NullConsole } from '../common/console'; -import { combinePaths, normalizePath,normalizeSlashes } from '../common/pathUtils'; +import { combinePaths, normalizePath, normalizeSlashes } from '../common/pathUtils'; import { createFromRealFileSystem } from '../common/vfs'; test('FindFilesWithConfigFile', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const service = new AnalyzerService('', createFromRealFileSystem(), new NullConsole()); const commandLineOptions = new CommandLineOptions(cwd, true); commandLineOptions.configFilePath = 'src/tests/samples/project1'; @@ -26,9 +26,11 @@ test('FindFilesWithConfigFile', () => { service.setOptions(commandLineOptions); // The config file specifies a single file spec (a directory). - assert.equal(configOptions.include.length, 1, `failed creating options from ${ cwd }`); - assert.equal(normalizeSlashes(configOptions.projectRoot), - normalizeSlashes(combinePaths(cwd, commandLineOptions.configFilePath))); + assert.equal(configOptions.include.length, 1, `failed creating options from ${cwd}`); + assert.equal( + normalizeSlashes(configOptions.projectRoot), + normalizeSlashes(combinePaths(cwd, commandLineOptions.configFilePath)) + ); const fileList = service.test_getFileNamesFromFileSpecs(); @@ -39,7 +41,7 @@ test('FindFilesWithConfigFile', () => { }); test('FileSpecNotAnArray', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const nullConsole = new NullConsole(); const service = new AnalyzerService('', createFromRealFileSystem(nullConsole), nullConsole); const commandLineOptions = new CommandLineOptions(cwd, false); @@ -53,7 +55,7 @@ test('FileSpecNotAnArray', () => { }); test('FileSpecNotAString', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const nullConsole = new NullConsole(); const service = new AnalyzerService('', createFromRealFileSystem(nullConsole), nullConsole); const commandLineOptions = new CommandLineOptions(cwd, false); @@ -67,7 +69,7 @@ test('FileSpecNotAString', () => { }); test('SomeFileSpecsAreInvalid', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const nullConsole = new NullConsole(); const service = new AnalyzerService('', createFromRealFileSystem(nullConsole), nullConsole); const commandLineOptions = new CommandLineOptions(cwd, false); @@ -78,10 +80,12 @@ test('SomeFileSpecsAreInvalid', () => { // The config file specifies four file specs in the include array // and one in the exclude array. - assert.equal(configOptions.include.length, 4, `failed creating options from ${ cwd }`); + assert.equal(configOptions.include.length, 4, `failed creating options from ${cwd}`); assert.equal(configOptions.exclude.length, 1); - assert.equal(normalizeSlashes(configOptions.projectRoot), - normalizeSlashes(combinePaths(cwd, commandLineOptions.configFilePath))); + assert.equal( + normalizeSlashes(configOptions.projectRoot), + normalizeSlashes(combinePaths(cwd, commandLineOptions.configFilePath)) + ); const fileList = service.test_getFileNamesFromFileSpecs(); @@ -90,7 +94,7 @@ test('SomeFileSpecsAreInvalid', () => { }); test('ConfigBadJson', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const nullConsole = new NullConsole(); const service = new AnalyzerService('', createFromRealFileSystem(nullConsole), nullConsole); const commandLineOptions = new CommandLineOptions(cwd, false); @@ -104,7 +108,7 @@ test('ConfigBadJson', () => { }); test('FindExecEnv1', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const configOptions = new ConfigOptions(cwd); // Build a config option with three execution environments. @@ -122,12 +126,11 @@ test('FindExecEnv1', () => { // a default environment with the root equal to that of the config. const file4 = '/nothing/bar.py'; const defaultExecEnv = configOptions.findExecEnvironment(file4); - assert.equal(normalizeSlashes(defaultExecEnv.root), - normalizeSlashes(configOptions.projectRoot)); + assert.equal(normalizeSlashes(defaultExecEnv.root), normalizeSlashes(configOptions.projectRoot)); }); test('PythonPlatform', () => { - const cwd = normalizePath(combinePaths(process.cwd(), "../server")) + const cwd = normalizePath(combinePaths(process.cwd(), '../server')); const nullConsole = new NullConsole(); const configOptions = new ConfigOptions(cwd); diff --git a/server/src/tests/debug.test.ts b/server/src/tests/debug.test.ts index 9ebe418a5..7977dc953 100644 --- a/server/src/tests/debug.test.ts +++ b/server/src/tests/debug.test.ts @@ -9,14 +9,19 @@ import * as assert from 'assert'; import * as debug from '../common/debug'; test('DebugAssertTrue', () => { - assert.doesNotThrow(() => { debug.assert(true, 'doesn\'t throw'); }); + assert.doesNotThrow(() => { + debug.assert(true, "doesn't throw"); + }); }); test('DebugAssertFalse', () => { assert.throws( - () => { debug.assert(false, 'should throw'); }, + () => { + debug.assert(false, 'should throw'); + }, (err: any) => err instanceof Error, - 'unexpected'); + 'unexpected' + ); }); test('DebugAssertDetailInfo', () => { @@ -24,17 +29,23 @@ test('DebugAssertDetailInfo', () => { // assert raised const detailInfo = 'Detail Info'; assert.throws( - () => { debug.assert(false, 'should throw', () => detailInfo); }, + () => { + debug.assert(false, 'should throw', () => detailInfo); + }, (err: any) => err instanceof Error && err.message.includes(detailInfo), - 'unexpected'); + 'unexpected' + ); }); test('DebugAssertStackTrace', () => { // let assert to control what callstack to put in exception stack assert.throws( - () => { debug.assert(false, 'should throw', undefined, assert.throws); }, + () => { + debug.assert(false, 'should throw', undefined, assert.throws); + }, (err: any) => err instanceof Error && !err.message.includes('assert.throws'), - 'unexpected'); + 'unexpected' + ); }); test('DebugAssertUndefined', () => { @@ -42,7 +53,8 @@ test('DebugAssertUndefined', () => { assert.throws( () => debug.assertDefined(unused), (err: any) => err instanceof Error, - 'unexpected'); + 'unexpected' + ); }); test('DebugAssertDefined', () => { @@ -56,7 +68,8 @@ test('DebugAssertEachUndefined', () => { assert.throws( () => debug.assertEachDefined(unused), (err: any) => err instanceof Error, - 'unexpected'); + 'unexpected' + ); }); test('DebugAssertEachDefined', () => { @@ -65,7 +78,11 @@ test('DebugAssertEachDefined', () => { }); test('DebugAssertNever', () => { - const enum MyEnum { A, B, C } + const enum MyEnum { + A, + B, + C + } const unused = 5 as MyEnum; // prevent one from adding new values and forget to add @@ -82,7 +99,8 @@ test('DebugAssertNever', () => { } }, (err: any) => err instanceof Error, - 'unexpected'); + 'unexpected' + ); }); test('DebugGetFunctionName', () => { @@ -93,6 +111,10 @@ test('DebugGetFunctionName', () => { test('DebugFormatEnum', () => { // helper method to add better message in exception around enum // const enum require --preserveConstEnums flag to work properly - enum MyEnum { A, B, C } + enum MyEnum { + A, + B, + C + } assert(debug.formatEnum(MyEnum.A, MyEnum, false) === 'A'); }); diff --git a/server/src/tests/filesystem.test.ts b/server/src/tests/filesystem.test.ts index 706dd16dd..f26355faa 100644 --- a/server/src/tests/filesystem.test.ts +++ b/server/src/tests/filesystem.test.ts @@ -24,7 +24,9 @@ test('Folders', () => { const fs = new vfs.FileSystem(/*ignoreCase*/ true, { cwd }); // no such dir exist - assert.throws(() => { fs.chdir('a'); }); + assert.throws(() => { + fs.chdir('a'); + }); fs.mkdirSync('a'); fs.chdir('a'); @@ -34,7 +36,9 @@ test('Folders', () => { fs.rmdirSync('a'); // no such dir exist - assert.throws(() => { fs.chdir('a'); }); + assert.throws(() => { + fs.chdir('a'); + }); }); test('Files', () => { @@ -145,8 +149,10 @@ test('createFromFileSystem1', () => { const content = '# test'; // file system will map physical file system to virtual one - const fs = factory.createFromFileSystem(host.HOST, false, - { documents: [new factory.TextDocument(filepath, content)], cwd: factory.srcFolder }); + const fs = factory.createFromFileSystem(host.HOST, false, { + documents: [new factory.TextDocument(filepath, content)], + cwd: factory.srcFolder + }); // check existing typeshed folder on virtual path inherited from base snapshot from physical file system const entries = fs.readdirSync(factory.typeshedFolder); @@ -165,7 +171,8 @@ test('createFromFileSystem2', () => { test('createFromFileSystemWithCustomTypeshedPath', () => { const invalidpath = normalizeSlashes(combinePaths(host.HOST.getWorkspaceRoot(), '../docs')); const fs = factory.createFromFileSystem(host.HOST, /* ignoreCase */ false, { - cwd: factory.srcFolder, meta: { [factory.typeshedFolder]: invalidpath } + cwd: factory.srcFolder, + meta: { [factory.typeshedFolder]: invalidpath } }); const entries = fs.readdirSync(factory.typeshedFolder); @@ -174,7 +181,8 @@ test('createFromFileSystemWithCustomTypeshedPath', () => { test('createFromFileSystemWithMetadata', () => { const fs = factory.createFromFileSystem(host.HOST, /* ignoreCase */ false, { - cwd: factory.srcFolder, meta: { 'unused': 'unused' } + cwd: factory.srcFolder, + meta: { unused: 'unused' } }); assert(fs.existsSync(factory.srcFolder)); diff --git a/server/src/tests/fourSlashParser.test.ts b/server/src/tests/fourSlashParser.test.ts index 9874e0ae9..399ce7fe9 100644 --- a/server/src/tests/fourSlashParser.test.ts +++ b/server/src/tests/fourSlashParser.test.ts @@ -30,7 +30,10 @@ test('GlobalOptions', () => { pass`; const data = parseTestData('.', code, 'test.py'); - assertOptions(data.globalOptions, [['libpath', '../dist/lib'], ['pythonversion', '3.7']]); + assertOptions(data.globalOptions, [ + ['libpath', '../dist/lib'], + ['pythonversion', '3.7'] + ]); assert.equal(data.files.length, 1); assert.equal(data.files[0].fileName, 'test.py'); @@ -69,7 +72,10 @@ test('Extra file options', () => { assert.equal(data.files[0].fileName, normalizeSlashes('./file1.py')); assertOptions(data.globalOptions, []); - assertOptions(data.files[0].fileOptions, [['filename', 'file1.py'], ['library', 'false']]); + assertOptions(data.files[0].fileOptions, [ + ['filename', 'file1.py'], + ['library', 'false'] + ]); }); test('Library options', () => { @@ -215,8 +221,10 @@ test('Multiple Files', () => { assert.equal(data.files.length, 3); assert.equal(data.files.filter(f => f.fileName === normalizeSlashes('./src/A.py'))[0].content, getContent('A')); - assert.equal(data.files.filter(f => f.fileName === - normalizeSlashes(combinePaths(factory.libFolder, 'src/B.py')))[0].content, getContent('B')); + assert.equal( + data.files.filter(f => f.fileName === normalizeSlashes(combinePaths(factory.libFolder, 'src/B.py')))[0].content, + getContent('B') + ); assert.equal(data.files.filter(f => f.fileName === normalizeSlashes('./src/C.py'))[0].content, getContent('C')); }); @@ -290,10 +298,14 @@ test('fourSlashWithFileSystem', () => { `; const data = parseTestData('.', code, 'unused'); - const documents = data.files.map(f => new factory.TextDocument(f.fileName, f.content, - new Map(Object.entries(f.fileOptions)))); + const documents = data.files.map( + f => new factory.TextDocument(f.fileName, f.content, new Map(Object.entries(f.fileOptions))) + ); - const fs = factory.createFromFileSystem(host.HOST, /* ignoreCase */ false, { documents, cwd: normalizeSlashes('/') }); + const fs = factory.createFromFileSystem(host.HOST, /* ignoreCase */ false, { + documents, + cwd: normalizeSlashes('/') + }); for (const file of data.files) { assert.equal(fs.readFileSync(file.fileName, 'utf8'), getContent(getBaseFileName(file.fileName, '.py', false))); @@ -301,7 +313,7 @@ test('fourSlashWithFileSystem', () => { }); function getContent(className: string) { - return `class ${ className }: + return `class ${className}: pass`; } @@ -309,5 +321,6 @@ function assertOptions(actual: CompilerSettings, expected: [string, string][], m assert.deepEqual( Object.entries(actual).sort((x, y) => compareStringsCaseSensitive(x[0], y[0])), expected, - message); + message + ); } diff --git a/server/src/tests/fourslash/dataclass1.fourslash.ts b/server/src/tests/fourslash/dataclass1.fourslash.ts index 4deb6cef6..262de7a69 100644 --- a/server/src/tests/fourslash/dataclass1.fourslash.ts +++ b/server/src/tests/fourslash/dataclass1.fourslash.ts @@ -2,12 +2,12 @@ // @filename: dataclass1.py //// # This sample validates the Python 3.7 data class feature. -//// +//// //// from typing import NamedTuple, Optional -//// +//// //// class Other: //// pass -//// +//// //// class DataTuple(NamedTuple): //// def _m(self): //// pass @@ -15,19 +15,19 @@ //// aid: Other //// valll: str = '' //// name: Optional[str] = None -//// +//// //// d1 = DataTuple(id=1, aid=Other()) //// d2 = DataTuple(id=1, aid=Other(), valll='v') //// d3 = DataTuple(id=1, aid=Other(), name='hello') //// d4 = DataTuple(id=1, aid=Other(), name=None) //// id = d1.id -//// +//// //// # This should generate an error because the name argument //// # is the incorrect type. //// d5 = DataTuple(id=1, aid=Other(), name=[|{|"category": "error"|}3|]) -//// +//// //// # This should generate an error because aid is a required //// # parameter and is missing an argument here. //// d6 = [|{|"category": "error"|}DataTuple(id=1, name=None|]) -helper.verifyDiagnostics(); \ No newline at end of file +helper.verifyDiagnostics(); diff --git a/server/src/tests/fourslash/dataclass3.fourslash.ts b/server/src/tests/fourslash/dataclass3.fourslash.ts index 44866065d..430ce296c 100644 --- a/server/src/tests/fourslash/dataclass3.fourslash.ts +++ b/server/src/tests/fourslash/dataclass3.fourslash.ts @@ -3,18 +3,18 @@ // @filename: dataclass3.py //// # This sample validates the Python 3.7 data class feature, ensuring that //// # NamedTuple must be a direct base class. -//// +//// //// from typing import NamedTuple -//// +//// //// class Parent(NamedTuple): //// pass -//// +//// //// class DataTuple2(Parent): //// id: int -//// +//// //// # This should generate an error because DataTuple2 isn't considered //// # a data class and won't have the associated __new__ or __init__ //// # method defined. //// data = DataTuple2([|{|"category": "error"|}id|]=1) -helper.verifyDiagnostics(); \ No newline at end of file +helper.verifyDiagnostics(); diff --git a/server/src/tests/fourslash/dataclass4.fourslash.ts b/server/src/tests/fourslash/dataclass4.fourslash.ts index a036bcef6..0bf5b6baf 100644 --- a/server/src/tests/fourslash/dataclass4.fourslash.ts +++ b/server/src/tests/fourslash/dataclass4.fourslash.ts @@ -1,58 +1,65 @@ /// //// # This sample tests the handling of the @dataclass decorator. -//// +//// //// from dataclasses import dataclass, InitVar -//// +//// //// @dataclass //// class Bar(): //// bbb: int //// ccc: str //// aaa = 'string' -//// +//// //// bar1 = Bar(bbb=5, ccc='hello') //// bar2 = Bar(5, 'hello') //// bar3 = Bar(5, 'hello', 'hello2') //// print(bar3.bbb) //// print(bar3.ccc) //// print(bar3.aaa) -//// +//// //// # This should generate an error because ddd //// # isn't a declared value. //// bar = Bar(bbb=5, [|/*marker1*/ddd|]=5, ccc='hello') -//// +//// //// # This should generate an error because the //// # parameter types don't match. //// bar = Bar([|/*marker2*/'hello'|], 'goodbye') -//// +//// //// # This should generate an error because a parameter //// # is missing. //// bar = [|/*marker3*/Bar(2)|] -//// +//// //// # This should generate an error because there are //// # too many parameters. //// bar = Bar(2, 'hello', 'hello', [|/*marker4*/4|]) -//// -//// +//// +//// //// @dataclass //// class Baz1(): //// bbb: int //// aaa = 'string' -//// +//// //// # This should generate an error because variables //// # with no default cannot come after those with //// # defaults. //// [|/*marker5*/ccc|]: str -//// +//// //// @dataclass //// class Baz2(): //// aaa: str //// ddd: InitVar[int] = 3 helper.verifyDiagnostics({ - "marker1": { category: "error", message: "No parameter named 'ddd'" }, - "marker2": { category: "error", message: "Argument of type 'Literal['hello']' cannot be assigned to parameter 'bbb' of type 'int'\n 'str' is incompatible with 'int'" }, - "marker3": { category: "error", message: "Argument missing for parameter 'ccc'" }, - "marker4": { category: "error", message: "Expected 3 positional arguments" }, - "marker5": { category: "error", message: "Data fields without default value cannot appear after data fields with default values" }, -}); \ No newline at end of file + marker1: { category: 'error', message: "No parameter named 'ddd'" }, + marker2: { + category: 'error', + message: + "Argument of type 'Literal['hello']' cannot be assigned to parameter 'bbb' of type 'int'\n 'str' is incompatible with 'int'" + }, + marker3: { category: 'error', message: "Argument missing for parameter 'ccc'" }, + marker4: { category: 'error', message: 'Expected 3 positional arguments' }, + marker5: { + category: 'error', + message: 'Data fields without default value cannot appear after data fields with default values' + } +}); diff --git a/server/src/tests/fourslash/dataclass5.fourslash.ts b/server/src/tests/fourslash/dataclass5.fourslash.ts index 22b364a01..6f276a725 100644 --- a/server/src/tests/fourslash/dataclass5.fourslash.ts +++ b/server/src/tests/fourslash/dataclass5.fourslash.ts @@ -2,52 +2,52 @@ //// # This sample tests the handling of the @dataclass decorator //// # with a custom __init__. -//// +//// //// from dataclasses import dataclass -//// +//// //// @dataclass(init=False) //// class A: //// x: int //// x_squared: int -//// +//// //// def __init__(self, x: int): //// self.x = x //// self.x_squared = x ** 2 -//// +//// //// a = A(3) -//// +//// //// @dataclass(init=True) //// class B: //// x: int //// x_squared: int -//// +//// //// def __init__(self, x: int): //// self.x = x //// self.x_squared = x ** 2 -//// +//// //// b = B(3) -//// +//// //// @dataclass() //// class C: //// x: int //// x_squared: int -//// +//// //// def __init__(self, x: int): //// self.x = x //// self.x_squared = x ** 2 -//// +//// //// c = C(3) -//// +//// //// @dataclass(init=False) //// class D: //// x: int //// x_squared: int -//// +//// //// # This should generate an error because there is no //// # override __init__ method and no synthesized __init__. //// d = [|/*marker1*/[|/*marker2*/D(3|]|]) helper.verifyDiagnostics({ - "marker1": { category: "error", message: "Expected no arguments to 'D' constructor" }, - "marker2": { category: "error", message: "'D(3)' has type 'Type[D]' and is not callable" }, -}); \ No newline at end of file + marker1: { category: 'error', message: "Expected no arguments to 'D' constructor" }, + marker2: { category: 'error', message: "'D(3)' has type 'Type[D]' and is not callable" } +}); diff --git a/server/src/tests/fourslash/dataclass6.fourslash.ts b/server/src/tests/fourslash/dataclass6.fourslash.ts index 714e5f6bf..e9f790cff 100644 --- a/server/src/tests/fourslash/dataclass6.fourslash.ts +++ b/server/src/tests/fourslash/dataclass6.fourslash.ts @@ -3,32 +3,32 @@ //// # This sample tests the type checker's handling of //// # synthesized __init__ and __new__ methods for //// # dataclass classes and their subclasses. -//// +//// //// from dataclasses import dataclass -//// +//// //// @dataclass //// class A: //// x: int -//// +//// //// @dataclass(init) //// class B(A): //// y: int -//// +//// //// def __init__(self, a: A, y: int): //// self.__dict__ = a.__dict__ -//// +//// //// a = A(3) //// b = B(a, 5) -//// -//// +//// +//// //// # This should generate an error because there is an extra parameter //// a = A(3, [|/*marker1*/4|]) -//// +//// //// # This should generate an error because there is one too few parameters //// b = [|/*marker2*/B(a)|] -//// +//// helper.verifyDiagnostics({ - "marker1": { category: "error", message: "Expected 1 positional argument" }, - "marker2": { category: "error", message: "Argument missing for parameter 'y'" }, -}); \ No newline at end of file + marker1: { category: 'error', message: 'Expected 1 positional argument' }, + marker2: { category: 'error', message: "Argument missing for parameter 'y'" } +}); diff --git a/server/src/tests/fourslash/dataclass7.fourslash.ts b/server/src/tests/fourslash/dataclass7.fourslash.ts index 07d3b543d..935869182 100644 --- a/server/src/tests/fourslash/dataclass7.fourslash.ts +++ b/server/src/tests/fourslash/dataclass7.fourslash.ts @@ -2,58 +2,65 @@ //// # This sample tests the analyzer's ability to handline inherited //// # data classes. -//// +//// //// from dataclasses import dataclass -//// +//// //// class C1: ... //// class C2: ... //// class C3: ... -//// +//// //// @dataclass //// class DC1: //// aa: C1 //// bb: C2 //// cc: C3 -//// +//// //// class NonDC2: //// ff: int -//// +//// //// @dataclass //// class DC2(NonDC2, DC1): //// ee: C2 //// aa: C2 //// dd: C2 -//// +//// //// dc2_1 = DC2(C2(), C2(), C3(), C2(), C2()) -//// +//// //// # This should generate an error because the type //// # of parameter aa has been replaced with type C1. //// dc2_2 = DC2([|/*marker1*/C1()|], C2(), C3(), C2(), C2()) -//// +//// //// dc2_3 = DC2(ee=C2(), dd=C2(), aa=C2(), bb=C2(), cc=C3()) -//// -//// +//// +//// //// @dataclass //// class DC3: //// aa: C1 //// bb: C2 = C2() //// cc: C3 = C3() -//// +//// //// @dataclass //// class DC4(DC3): //// # This should generate an error because //// # previous parameters have default values. //// [|/*marker2*/dd|]: C1 -//// +//// //// @dataclass //// class DC5(DC3): //// # This should not generate an error because //// # aa replaces aa in DC3, and it's ordered //// # before the params with default values. //// aa: C2 -//// +//// helper.verifyDiagnostics({ - "marker1": { category: "error", message: "Argument of type 'C1' cannot be assigned to parameter 'aa' of type 'C2'\n 'C1' is incompatible with 'C2'" }, - "marker2": { category: "error", message: "Data fields without default value cannot appear after data fields with default values" }, -}); \ No newline at end of file + marker1: { + category: 'error', + message: + "Argument of type 'C1' cannot be assigned to parameter 'aa' of type 'C2'\n 'C1' is incompatible with 'C2'" + }, + marker2: { + category: 'error', + message: 'Data fields without default value cannot appear after data fields with default values' + } +}); diff --git a/server/src/tests/fourslash/fourslash.ts b/server/src/tests/fourslash/fourslash.ts index 088d20feb..a9ae68e9a 100644 --- a/server/src/tests/fourslash/fourslash.ts +++ b/server/src/tests/fourslash/fourslash.ts @@ -1,26 +1,26 @@ /* -* fourslash.ts -* Copyright (c) Microsoft Corporation. -* Licensed under the MIT license. -* -* this file only exists for the richer editing experiences on *.fourslash.ts files. -* when fourslash tests are actually running this file is not used. -* -* this basically provides type information through // while editing but -* get ignored when test run due to how test code is injected when running. -* see - server\pyright\server\src\tests\harness\fourslash\runner.ts@runCode - for more detail -* -* when run, helper variable will be bount to TestState (server\pyright\server\src\tests\harness\fourslash\testState.ts) -* so make sure Foruslash type is in sync with TestState -* -* for how markup language and helper is used in fourslash tests, see these 2 tests -* server\pyright\server\src\tests\fourSlashParser.test.ts -* server\pyright\server\src\tests\testState.test.ts -* -* for debugging, open *.fourslash.ts test file you want to debug, and select "fourslash current file" as debug configuration -* and set break point in one of TestState methods you are using in the test or set break point on "runCode" above -* and hit F5. -*/ + * fourslash.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * + * this file only exists for the richer editing experiences on *.fourslash.ts files. + * when fourslash tests are actually running this file is not used. + * + * this basically provides type information through // while editing but + * get ignored when test run due to how test code is injected when running. + * see - server\pyright\server\src\tests\harness\fourslash\runner.ts@runCode - for more detail + * + * when run, helper variable will be bount to TestState (server\pyright\server\src\tests\harness\fourslash\testState.ts) + * so make sure Foruslash type is in sync with TestState + * + * for how markup language and helper is used in fourslash tests, see these 2 tests + * server\pyright\server\src\tests\fourSlashParser.test.ts + * server\pyright\server\src\tests\testState.test.ts + * + * for debugging, open *.fourslash.ts test file you want to debug, and select "fourslash current file" as debug configuration + * and set break point in one of TestState methods you are using in the test or set break point on "runCode" above + * and hit F5. + */ declare namespace _ { interface TextRange { @@ -84,9 +84,13 @@ declare namespace _ { moveCaretRight(count: number): void; verifyDiagnostics(map?: { [marker: string]: { category: string; message: string } }): void; - verifyCodeActions(map: { [marker: string]: { codeActions: { title: string; kind: string; command: Command }[] } }): void; + verifyCodeActions(map: { + [marker: string]: { codeActions: { title: string; kind: string; command: Command }[] }; + }): void; verifyCommand(command: Command, files: { [filePath: string]: string }): void; - verifyInvokeCodeAction(map: { [marker: string]: { title: string; files: { [filePath: string]: string } } }): void; + verifyInvokeCodeAction(map: { + [marker: string]: { title: string; files: { [filePath: string]: string } }; + }): void; /* not tested yet openFile(indexOrName: number | string, content?: string): void; diff --git a/server/src/tests/fourslash/importnotresolved.fourslash.ts b/server/src/tests/fourslash/importnotresolved.fourslash.ts index 3dd1e0c28..767757f09 100644 --- a/server/src/tests/fourslash/importnotresolved.fourslash.ts +++ b/server/src/tests/fourslash/importnotresolved.fourslash.ts @@ -2,13 +2,12 @@ // @filename: importnotresolved.py //// # these will not be resolve, no typestubs for django in typeshed -//// +//// //// import [|/*marker1*/notexistant|] //// import [|/*marker2*/django|] -//// - +//// helper.verifyDiagnostics({ - "marker1": { category: "error", message: "Import 'notexistant' could not be resolved" }, - "marker2": { category: "error", message: "Import 'django' could not be resolved" }, -}); \ No newline at end of file + marker1: { category: 'error', message: "Import 'notexistant' could not be resolved" }, + marker2: { category: 'error', message: "Import 'django' could not be resolved" } +}); diff --git a/server/src/tests/fourslash/missingTypeStub.codeAction.fourslash.ts b/server/src/tests/fourslash/missingTypeStub.codeAction.fourslash.ts index a77044405..9f34b6dcf 100644 --- a/server/src/tests/fourslash/missingTypeStub.codeAction.fourslash.ts +++ b/server/src/tests/fourslash/missingTypeStub.codeAction.fourslash.ts @@ -16,11 +16,17 @@ //// import [|/*marker*/testLi|]b helper.verifyCodeActions({ - 'marker': { - codeActions: [{ - title: 'Create Type Stub For ‘testLib’', kind: Consts.CodeActionKind.QuickFix, command: { - title: 'Create Type Stub', command: Consts.Commands.createTypeStub, arguments: ['\\', 'testLib', '\\.src\\test.py'] + marker: { + codeActions: [ + { + title: "Create Type Stub For 'testLib'", + kind: Consts.CodeActionKind.QuickFix, + command: { + title: 'Create Type Stub', + command: Consts.Commands.createTypeStub, + arguments: ['\\', 'testLib', '\\.src\\test.py'] + } } - }] + ] } }); diff --git a/server/src/tests/fourslash/missingTypeStub.command.fourslash.ts b/server/src/tests/fourslash/missingTypeStub.command.fourslash.ts index 37fbcd904..4a4a9faf2 100644 --- a/server/src/tests/fourslash/missingTypeStub.command.fourslash.ts +++ b/server/src/tests/fourslash/missingTypeStub.command.fourslash.ts @@ -17,7 +17,11 @@ //// import [|/*marker*/testLi|]b const filename = helper.getMarkerByName('marker').fileName; -const command = { title: 'Create Type Stub', command: Consts.Commands.createTypeStub, arguments: ['/', 'testLib', filename] }; +const command = { + title: 'Create Type Stub', + command: Consts.Commands.createTypeStub, + arguments: ['/', 'testLib', filename] +}; helper.verifyCommand(command, { ['/typings/testLib/__init__.pyi']: `""" diff --git a/server/src/tests/fourslash/missingTypeStub.fourslash.ts b/server/src/tests/fourslash/missingTypeStub.fourslash.ts index 9a75038d8..9433b0741 100644 --- a/server/src/tests/fourslash/missingTypeStub.fourslash.ts +++ b/server/src/tests/fourslash/missingTypeStub.fourslash.ts @@ -16,5 +16,5 @@ //// import [|/*marker*/testLi|]b helper.verifyDiagnostics({ - 'marker': { category: 'warning', message: 'Stub file not found for \'testLib\'' } + marker: { category: 'warning', message: "Stub file not found for 'testLib'" } }); diff --git a/server/src/tests/fourslash/missingTypeStub.invokeCodeAction.fourslash.ts b/server/src/tests/fourslash/missingTypeStub.invokeCodeAction.fourslash.ts index cc0342fb4..6724f3af3 100644 --- a/server/src/tests/fourslash/missingTypeStub.invokeCodeAction.fourslash.ts +++ b/server/src/tests/fourslash/missingTypeStub.invokeCodeAction.fourslash.ts @@ -17,8 +17,8 @@ //// import [|/*marker*/testLi|]b helper.verifyInvokeCodeAction({ - 'marker': { - title: 'Create Type Stub For ‘testLib’', + marker: { + title: "Create Type Stub For 'testLib'", files: { ['/typings/testLib/__init__.pyi']: `""" This type stub file was generated by pyright. diff --git a/server/src/tests/harness/fourslash/fourSlashParser.ts b/server/src/tests/harness/fourslash/fourSlashParser.ts index c70558467..8b36d417d 100644 --- a/server/src/tests/harness/fourslash/fourSlashParser.ts +++ b/server/src/tests/harness/fourslash/fourSlashParser.ts @@ -8,7 +8,13 @@ import { contains } from '../../../common/collectionUtils'; import { toBoolean } from '../../../common/core'; -import { combinePaths, getRelativePath, isRootedDiskPath, normalizePath, normalizeSlashes } from '../../../common/pathUtils'; +import { + combinePaths, + getRelativePath, + isRootedDiskPath, + normalizePath, + normalizeSlashes +} from '../../../common/pathUtils'; import { libFolder } from '../vfs/factory'; import { fileMetadataNames, FourSlashData, FourSlashFile, Marker, MetadataOptionNames, Range } from './fourSlashTypes'; @@ -48,10 +54,14 @@ export function parseTestData(basePath: string, contents: string, fileName: stri let currentFileOptions: { [s: string]: string } = {}; function nextFile() { - if (currentFileContent === undefined) { return; } + if (currentFileContent === undefined) { + return; + } if (toBoolean(currentFileOptions[MetadataOptionNames.library])) { - currentFileName = normalizePath(combinePaths(libFolder, getRelativePath(currentFileName, normalizedBasePath))); + currentFileName = normalizePath( + combinePaths(libFolder, getRelativePath(currentFileName, normalizedBasePath)) + ); } const file = parseFileContent(currentFileContent, currentFileName, markerPositions, markers, ranges); @@ -75,7 +85,7 @@ export function parseTestData(basePath: string, contents: string, fileName: stri const text = line.substr(4); currentFileContent = currentFileContent === undefined ? text : currentFileContent + '\n' + text; } else if (line.substr(0, 3) === '///' && currentFileContent !== undefined) { - throw new Error(`Three-slash line in the middle of four-slash region at line ${ i }`); + throw new Error(`Three-slash line in the middle of four-slash region at line ${i}`); } else if (line.substr(0, 2) === '//') { // Comment line, check for global/file @options and record them const match = optionRegex.exec(line.substr(2)); @@ -85,7 +95,7 @@ export function parseTestData(basePath: string, contents: string, fileName: stri if (!contains(fileMetadataNames, key)) { // Check if the match is already existed in the global options if (globalOptions[key] !== undefined) { - throw new Error(`Global option '${ key }' already exists`); + throw new Error(`Global option '${key}' already exists`); } globalOptions[key] = value; } else { @@ -94,8 +104,9 @@ export function parseTestData(basePath: string, contents: string, fileName: stri // Found an @FileName directive, if this is not the first then create a new subfile nextFile(); const normalizedPath = normalizeSlashes(value); - currentFileName = isRootedDiskPath(normalizedPath) ? normalizedPath : - combinePaths(normalizedBasePath, normalizedPath); + currentFileName = isRootedDiskPath(normalizedPath) + ? normalizedPath + : combinePaths(normalizedBasePath, normalizedPath); currentFileOptions[key] = value; break; } @@ -141,19 +152,23 @@ const enum State { } function reportError(fileName: string, line: number, col: number, message: string) { - const errorMessage = `${ fileName }(${ line },${ col }): ${ message }`; + const errorMessage = `${fileName}(${line},${col}): ${message}`; throw new Error(errorMessage); } -function recordObjectMarker(fileName: string, location: LocationInformation, - text: string, markerMap: Map, markers: Marker[]): Marker | undefined { - +function recordObjectMarker( + fileName: string, + location: LocationInformation, + text: string, + markerMap: Map, + markers: Marker[] +): Marker | undefined { let markerValue: any; try { // Attempt to parse the marker value as JSON markerValue = JSON.parse('{ ' + text + ' }'); } catch (e) { - reportError(fileName, location.sourceLine, location.sourceColumn, `Unable to parse marker text ${ e.message }`); + reportError(fileName, location.sourceLine, location.sourceColumn, `Unable to parse marker text ${e.message}`); } if (markerValue === undefined) { @@ -177,9 +192,13 @@ function recordObjectMarker(fileName: string, location: LocationInformation, return marker; } -function recordMarker(fileName: string, location: LocationInformation, - name: string, markerMap: Map, markers: Marker[]): Marker | undefined { - +function recordMarker( + fileName: string, + location: LocationInformation, + name: string, + markerMap: Map, + markers: Marker[] +): Marker | undefined { const marker: Marker = { fileName, position: location.position @@ -187,7 +206,7 @@ function recordMarker(fileName: string, location: LocationInformation, // Verify markers for uniqueness if (markerMap.has(name)) { - const message = 'Marker \'' + name + '\' is duplicated in the source file contents.'; + const message = "Marker '" + name + "' is duplicated in the source file contents."; reportError(marker.fileName, location.sourceLine, location.sourceColumn, message); return undefined; } else { @@ -197,9 +216,13 @@ function recordMarker(fileName: string, location: LocationInformation, } } -function parseFileContent(content: string, fileName: string, - markerMap: Map, markers: Marker[], ranges: Range[]): FourSlashFile { - +function parseFileContent( + content: string, + fileName: string, + markerMap: Map, + markers: Marker[], + ranges: Range[] +): FourSlashFile { content = chompLeadingSpace(content); // Any slash-star comment with a character not in this string is not a marker. @@ -231,8 +254,12 @@ function parseFileContent(content: string, fileName: string, let column = 1; const flush = (lastSafeCharIndex: number | undefined) => { - output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex === undefined ? undefined - : lastSafeCharIndex - lastNormalCharPosition); + output = + output + + content.substr( + lastNormalCharPosition, + lastSafeCharIndex === undefined ? undefined : lastSafeCharIndex - lastNormalCharPosition + ); }; if (content.length > 0) { @@ -244,7 +271,7 @@ function parseFileContent(content: string, fileName: string, if (previousChar === '[' && currentChar === '|') { // found a range start openRanges.push({ - position: (i - 1) - difference, + position: i - 1 - difference, sourcePosition: i - 1, sourceLine: line, sourceColumn: column @@ -263,7 +290,7 @@ function parseFileContent(content: string, fileName: string, const range: Range = { fileName, pos: rangeStart!.position, - end: (i - 1) - difference, + end: i - 1 - difference, marker: rangeStart!.marker }; localRanges.push(range); @@ -276,7 +303,7 @@ function parseFileContent(content: string, fileName: string, // found a possible marker start state = State.inSlashStarMarker; openMarker = { - position: (i - 1) - difference, + position: i - 1 - difference, sourcePosition: i - 1, sourceLine: line, sourceColumn: column @@ -285,7 +312,7 @@ function parseFileContent(content: string, fileName: string, // found an object marker start state = State.inObjectMarker; openMarker = { - position: (i - 1) - difference, + position: i - 1 - difference, sourcePosition: i - 1, sourceLine: line, sourceColumn: column @@ -299,7 +326,13 @@ function parseFileContent(content: string, fileName: string, if (previousChar === '|' && currentChar === '}') { // Record the marker const objectMarkerNameText = content.substring(openMarker!.sourcePosition + 2, i - 1).trim(); - const marker = recordObjectMarker(fileName, openMarker!, objectMarkerNameText, markerMap, markers); + const marker = recordObjectMarker( + fileName, + openMarker!, + objectMarkerNameText, + markerMap, + markers + ); if (openRanges.length > 0) { openRanges[openRanges.length - 1].marker = marker; @@ -377,8 +410,10 @@ function parseFileContent(content: string, fileName: string, } // put ranges in the correct order - localRanges = localRanges.sort((a, b) => a.pos < b.pos ? -1 : a.pos === b.pos && a.end > b.end ? -1 : 1); - localRanges.forEach(r => { ranges.push(r); }); + localRanges = localRanges.sort((a, b) => (a.pos < b.pos ? -1 : a.pos === b.pos && a.end > b.end ? -1 : 1)); + localRanges.forEach(r => { + ranges.push(r); + }); return { content: output, @@ -391,7 +426,7 @@ function parseFileContent(content: string, fileName: string, function chompLeadingSpace(content: string) { const lines = content.split('\n'); for (const line of lines) { - if ((line.length !== 0) && (line.charAt(0) !== ' ')) { + if (line.length !== 0 && line.charAt(0) !== ' ') { return content; } } diff --git a/server/src/tests/harness/fourslash/runner.ts b/server/src/tests/harness/fourslash/runner.ts index fe9ab5c7b..18a52e684 100644 --- a/server/src/tests/harness/fourslash/runner.ts +++ b/server/src/tests/harness/fourslash/runner.ts @@ -21,10 +21,14 @@ import { Consts } from './testState.Consts'; * @param basePath this is used as a base path of the virtual file system the test will run upon * @param fileName this is the file path where fourslash test file will be read from */ -export function runFourSlashTest(basePath: string, fileName: string, cb?: jest.DoneCallback, - mountPaths?: Map, importResolverFactory?: ImportResolverFactory) { - - const content = (host.HOST.readFile(fileName)!); +export function runFourSlashTest( + basePath: string, + fileName: string, + cb?: jest.DoneCallback, + mountPaths?: Map, + importResolverFactory?: ImportResolverFactory +) { + const content = host.HOST.readFile(fileName)!; runFourSlashTestContent(basePath, fileName, content, cb, mountPaths, importResolverFactory); } @@ -36,9 +40,14 @@ export function runFourSlashTest(basePath: string, fileName: string, cb?: jest.D * if fourslash markup `content` doesn't have explicit `@filename` option * @param content this is fourslash markup string */ -export function runFourSlashTestContent(basePath: string, fileName: string, content: string, cb?: jest.DoneCallback, - mountPaths?: Map, importResolverFactory?: ImportResolverFactory) { - +export function runFourSlashTestContent( + basePath: string, + fileName: string, + content: string, + cb?: jest.DoneCallback, + mountPaths?: Map, + importResolverFactory?: ImportResolverFactory +) { // give file paths an absolute path for the virtual file system const absoluteBasePath = combinePaths('/', basePath); const absoluteFileName = combinePaths('/', fileName); @@ -46,9 +55,12 @@ export function runFourSlashTestContent(basePath: string, fileName: string, cont // parse out the files and their metadata const testData = parseTestData(absoluteBasePath, content, absoluteFileName); const state = new TestState(absoluteBasePath, testData, cb, mountPaths, importResolverFactory); - const output = ts.transpileModule(content, { reportDiagnostics: true, compilerOptions: { target: ts.ScriptTarget.ES2015 } }); + const output = ts.transpileModule(content, { + reportDiagnostics: true, + compilerOptions: { target: ts.ScriptTarget.ES2015 } + }); if (output.diagnostics!.length > 0) { - throw new Error(`Syntax error in ${ absoluteBasePath }: ${ output.diagnostics![0].messageText }`); + throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`); } runCode(output.outputText, state); @@ -56,9 +68,8 @@ export function runFourSlashTestContent(basePath: string, fileName: string, cont function runCode(code: string, state: TestState): void { // Compile and execute the test - const wrappedCode = - `(function(helper, Consts) { -${ code } + const wrappedCode = `(function(helper, Consts) { +${code} })`; // TODO: figure out how to use this with async diff --git a/server/src/tests/harness/fourslash/testLanguageService.ts b/server/src/tests/harness/fourslash/testLanguageService.ts index 096592bb1..c3c14220b 100644 --- a/server/src/tests/harness/fourslash/testLanguageService.ts +++ b/server/src/tests/harness/fourslash/testLanguageService.ts @@ -11,12 +11,21 @@ import * as path from 'path'; import { ConsoleInterface } from '../../../common/console'; import * as debug from '../../../common/debug'; import { VirtualFileSystem } from '../../../common/vfs'; -import { LanguageServerInterface, ServerSettings, WindowInterface, WorkspaceServiceInstance } from '../../../languageServerBase'; +import { + LanguageServerInterface, + ServerSettings, + WindowInterface, + WorkspaceServiceInstance +} from '../../../languageServerBase'; export class TestLanguageService implements LanguageServerInterface { private readonly _workspace: WorkspaceServiceInstance; - constructor(workspace: WorkspaceServiceInstance, readonly console: ConsoleInterface, readonly fs: VirtualFileSystem) { + constructor( + workspace: WorkspaceServiceInstance, + readonly console: ConsoleInterface, + readonly fs: VirtualFileSystem + ) { this._workspace = workspace; } @@ -38,7 +47,9 @@ export class TestLanguageService implements LanguageServerInterface { return settings; } - reanalyze(): void { /* don't do anything */ } + reanalyze(): void { + /* don't do anything */ + } readonly rootPath = path.sep; readonly window = new TestWindow(); @@ -46,11 +57,11 @@ export class TestLanguageService implements LanguageServerInterface { class TestWindow implements WindowInterface { showErrorMessage(message: string): void { - debug.fail('shouldn\'t be called'); + debug.fail("shouldn't be called"); } showWarningMessage(message: string): void { - debug.fail('shouldn\'t be called'); + debug.fail("shouldn't be called"); } showInformationMessage(message: string): void { diff --git a/server/src/tests/harness/fourslash/testState.ts b/server/src/tests/harness/fourslash/testState.ts index 697815de2..5cf24e820 100644 --- a/server/src/tests/harness/fourslash/testState.ts +++ b/server/src/tests/harness/fourslash/testState.ts @@ -20,7 +20,14 @@ import { ConsoleInterface, NullConsole } from '../../../common/console'; import { Comparison, isNumber, isString, toBoolean } from '../../../common/core'; import * as debug from '../../../common/debug'; import { DiagnosticCategory } from '../../../common/diagnostic'; -import { combinePaths, comparePaths, convertPathToUri, getBaseFileName, normalizePath, normalizeSlashes } from '../../../common/pathUtils'; +import { + combinePaths, + comparePaths, + convertPathToUri, + getBaseFileName, + normalizePath, + normalizeSlashes +} from '../../../common/pathUtils'; import { convertOffsetToPosition, convertPositionToOffset } from '../../../common/positionUtils'; import { getStringComparer } from '../../../common/stringUtils'; import { Position, TextRange } from '../../../common/textRange'; @@ -31,8 +38,16 @@ import { stringify } from '../utils'; import { createFromFileSystem } from '../vfs/factory'; import * as vfs from '../vfs/filesystem'; import { - CompilerSettings, FourSlashData, FourSlashFile, GlobalMetadataOptionNames, Marker, - MetadataOptionNames, MultiMap, pythonSettingFilename, Range, TestCancellationToken + CompilerSettings, + FourSlashData, + FourSlashFile, + GlobalMetadataOptionNames, + Marker, + MetadataOptionNames, + MultiMap, + pythonSettingFilename, + Range, + TestCancellationToken } from './fourSlashTypes'; import { TestLanguageService } from './testLanguageService'; @@ -64,9 +79,13 @@ export class TestState { // The file that's currently 'opened' activeFile!: FourSlashFile; - constructor(basePath: string, public testData: FourSlashData, cb?: jest.DoneCallback, - mountPaths?: Map, importResolverFactory?: ImportResolverFactory) { - + constructor( + basePath: string, + public testData: FourSlashData, + cb?: jest.DoneCallback, + mountPaths?: Map, + importResolverFactory?: ImportResolverFactory + ) { const nullConsole = new NullConsole(); const ignoreCase = toBoolean(testData.globalOptions[GlobalMetadataOptionNames.ignoreCase]); @@ -82,7 +101,7 @@ export class TestState { try { configJson = JSON.parse(file.content); } catch (e) { - throw new Error(`Failed to parse test ${ file.fileName }: ${ e.message }`); + throw new Error(`Failed to parse test ${file.fileName}: ${e.message}`); } configOptions.initializeFromJson(configJson, nullConsole); @@ -97,11 +116,19 @@ export class TestState { } this.console = nullConsole; - this.fs = createFromFileSystem(host.HOST, ignoreCase, { cwd: basePath, files, meta: testData.globalOptions }, mountPaths); + this.fs = createFromFileSystem( + host.HOST, + ignoreCase, + { cwd: basePath, files, meta: testData.globalOptions }, + mountPaths + ); this._files = sourceFiles; - const service = this._createAnalysisService(nullConsole, - importResolverFactory ?? AnalyzerService.createImportResolver, configOptions); + const service = this._createAnalysisService( + nullConsole, + importResolverFactory ?? AnalyzerService.createImportResolver, + configOptions + ); this.workspace = { workspaceName: 'test workspace', @@ -155,7 +182,7 @@ export class TestState { const content = this._getFileContent(marker.fileName); if (marker.position === -1 || marker.position > content.length) { - throw new Error(`Marker "${ nameOrMarker }" has been invalidated by unrecoverable edits to the file.`); + throw new Error(`Marker "${nameOrMarker}" has been invalidated by unrecoverable edits to the file.`); } const mName = isString(nameOrMarker) ? nameOrMarker : this.getMarkerName(marker); @@ -186,7 +213,11 @@ export class TestState { getMarkerByName(markerName: string) { const markerPos = this.testData.markerPositions.get(markerName); if (markerPos === undefined) { - throw new Error(`Unknown marker "${ markerName }" Available markers: ${ this.getMarkerNames().map(m => '"' + m + '"').join(', ') }`); + throw new Error( + `Unknown marker "${markerName}" Available markers: ${this.getMarkerNames() + .map(m => '"' + m + '"') + .join(', ')}` + ); } else { return markerPos; } @@ -261,7 +292,9 @@ export class TestState { } getRangesByText(): Map { - if (this.testData.rangesByText) { return this.testData.rangesByText; } + if (this.testData.rangesByText) { + return this.testData.rangesByText; + } const result = this._createMultiMap(this.getRanges(), r => this._rangeText(r)); this.testData.rangesByText = result; @@ -279,7 +312,10 @@ export class TestState { moveCaretRight(count = 1) { this.currentCaretPosition += count; - this.currentCaretPosition = Math.min(this.currentCaretPosition, this._getFileContent(this.activeFile.fileName).length); + this.currentCaretPosition = Math.min( + this.currentCaretPosition, + this._getFileContent(this.activeFile.fileName).length + ); this.selectionEnd = -1; } @@ -295,12 +331,14 @@ export class TestState { printCurrentFileState(showWhitespace: boolean, makeCaretVisible: boolean) { for (const file of this.testData.files) { - const active = (this.activeFile === file); - host.HOST.log(`=== Script (${ file.fileName }) ${ (active ? '(active, cursor at |)' : '') } ===`); + const active = this.activeFile === file; + host.HOST.log(`=== Script (${file.fileName}) ${active ? '(active, cursor at |)' : ''} ===`); let content = this._getFileContent(file.fileName); if (active) { - content = content.substr(0, this.currentCaretPosition) - + (makeCaretVisible ? '|' : '') + content.substr(this.currentCaretPosition); + content = + content.substr(0, this.currentCaretPosition) + + (makeCaretVisible ? '|' : '') + + content.substr(this.currentCaretPosition); } if (showWhitespace) { content = this._makeWhitespaceVisible(content); @@ -333,7 +371,10 @@ export class TestState { deleteLineRange(startIndex: number, endIndexInclusive: number) { const startPos = this._convertPositionToOffset(this.activeFile.fileName, { line: startIndex, character: 0 }); - const endPos = this._convertPositionToOffset(this.activeFile.fileName, { line: endIndexInclusive + 1, character: 0 }); + const endPos = this._convertPositionToOffset(this.activeFile.fileName, { + line: endIndexInclusive + 1, + character: 0 + }); this.replace(startPos, endPos - startPos, ''); } @@ -376,7 +417,12 @@ export class TestState { // Enters text as if the user had pasted it paste(text: string) { - this._editScriptAndUpdateMarkers(this.activeFile.fileName, this.currentCaretPosition, this.currentCaretPosition, text); + this._editScriptAndUpdateMarkers( + this.activeFile.fileName, + this.currentCaretPosition, + this.currentCaretPosition, + text + ); this._checkPostEditInvariants(); } @@ -389,7 +435,11 @@ export class TestState { // expected number of files if (resultPerFile.size !== rangePerFile.size) { - this._raiseError(`actual and expected doesn't match - expected: ${ stringify(resultPerFile) }, actual: ${ stringify(rangePerFile) }`); + this._raiseError( + `actual and expected doesn't match - expected: ${stringify(resultPerFile)}, actual: ${stringify( + rangePerFile + )}` + ); } for (const [file, ranges] of rangePerFile.entries()) { @@ -405,22 +455,31 @@ export class TestState { const result = resultPerFile.get(file)!; for (const [category, expected] of rangesPerCategory.entries()) { const lines = result.parseResults!.tokenizerOutput.lines; - const actual = category === 'error' ? result.errors : category === 'warning' ? result.warnings : this._raiseError(`unexpected category ${ category }`); + const actual = + category === 'error' + ? result.errors + : category === 'warning' + ? result.warnings + : this._raiseError(`unexpected category ${category}`); if (expected.length !== actual.length) { - this._raiseError(`contains unexpected result - expected: ${ stringify(expected) }, actual: ${ actual }`); + this._raiseError( + `contains unexpected result - expected: ${stringify(expected)}, actual: ${actual}` + ); } for (const range of ranges) { const rangeSpan = TextRange.fromBounds(range.pos, range.end); const matches = actual.filter(d => { - const diagnosticSpan = TextRange.fromBounds(convertPositionToOffset(d.range.start, lines)!, - convertPositionToOffset(d.range.end, lines)!); + const diagnosticSpan = TextRange.fromBounds( + convertPositionToOffset(d.range.start, lines)!, + convertPositionToOffset(d.range.end, lines)! + ); return this._deepEqual(diagnosticSpan, rangeSpan); }); if (matches.length === 0) { - this._raiseError(`doesn't contain expected range: ${ stringify(range) }`); + this._raiseError(`doesn't contain expected range: ${stringify(range)}`); } // if map is provided, check message as well @@ -429,7 +488,11 @@ export class TestState { const message = map[name].message; if (matches.filter(d => message === d.message).length !== 1) { - this._raiseError(`message doesn't match: ${ message } of ${ name } - ${ stringify(range) }, actual: ${ stringify(matches) }`); + this._raiseError( + `message doesn't match: ${message} of ${name} - ${stringify( + range + )}, actual: ${stringify(matches)}` + ); } } } @@ -437,7 +500,9 @@ export class TestState { } } - verifyCodeActions(map: { [marker: string]: { codeActions: { title: string; kind: string; command: Command }[] } }): void { + verifyCodeActions(map: { + [marker: string]: { codeActions: { title: string; kind: string; command: Command }[] }; + }): void { this._analyze(); for (const range of this.getRanges()) { @@ -451,11 +516,14 @@ export class TestState { arguments: expected.command.arguments?.map(a => normalizeSlashes(a)) }; - const matches = actual.filter(a => a.title === expected.title - && a.kind! === expected.kind && this._deepEqual(a.command, command)); + const matches = actual.filter( + a => a.title === expected.title && a.kind! === expected.kind && this._deepEqual(a.command, command) + ); if (matches.length !== 1) { - this._raiseError(`doesn't contain expected result: ${ stringify(expected) }, actual: ${ stringify(actual) }`); + this._raiseError( + `doesn't contain expected result: ${stringify(expected)}, actual: ${stringify(actual)}` + ); } } } @@ -471,7 +539,9 @@ export class TestState { this.markTestDone(); } - async verifyInvokeCodeAction(map: { [marker: string]: { title: string; files: { [filePath: string]: string } } }): Promise { + async verifyInvokeCodeAction(map: { + [marker: string]: { title: string; files: { [filePath: string]: string } }; + }): Promise { this._analyze(); for (const range of this.getRanges()) { @@ -479,7 +549,10 @@ export class TestState { const controller = new CommandController(new TestLanguageService(this.workspace, this.console, this.fs)); for (const codeAction of this._getCodeActions(range).filter(c => c.title === map[name].title)) { - await controller.execute({ command: codeAction.command!.command, arguments: codeAction.command?.arguments }); + await controller.execute({ + command: codeAction.command!.command, + arguments: codeAction.command?.arguments + }); } await this._verifyFiles(map[name].files); @@ -491,17 +564,25 @@ export class TestState { verifyCaretAtMarker(markerName = '') { const pos = this.getMarkerByName(markerName); if (pos.fileName !== this.activeFile.fileName) { - throw new Error(`verifyCaretAtMarker failed - expected to be in file "${ pos.fileName }", but was in file "${ this.activeFile.fileName }"`); + throw new Error( + `verifyCaretAtMarker failed - expected to be in file "${pos.fileName}", but was in file "${this.activeFile.fileName}"` + ); } if (pos.position !== this.currentCaretPosition) { - throw new Error(`verifyCaretAtMarker failed - expected to be at marker "/*${ markerName }*/, but was at position ${ this.currentCaretPosition }(${ this._getLineColStringAtPosition(this.currentCaretPosition) })`); + throw new Error( + `verifyCaretAtMarker failed - expected to be at marker "/*${markerName}*/, but was at position ${ + this.currentCaretPosition + }(${this._getLineColStringAtPosition(this.currentCaretPosition)})` + ); } } verifyCurrentLineContent(text: string) { const actual = this._getCurrentLineContent(); if (actual !== text) { - throw new Error('verifyCurrentLineContent\n' + this._displayExpectedAndActualString(text, actual, /* quoted */ true)); + throw new Error( + 'verifyCurrentLineContent\n' + this._displayExpectedAndActualString(text, actual, /* quoted */ true) + ); } } @@ -510,10 +591,14 @@ export class TestState { } verifyTextAtCaretIs(text: string) { - const actual = this._getFileContent(this.activeFile.fileName) - .substring(this.currentCaretPosition, this.currentCaretPosition + text.length); + const actual = this._getFileContent(this.activeFile.fileName).substring( + this.currentCaretPosition, + this.currentCaretPosition + text.length + ); if (actual !== text) { - throw new Error('verifyTextAtCaretIs\n' + this._displayExpectedAndActualString(text, actual, /* quoted */ true)); + throw new Error( + 'verifyTextAtCaretIs\n' + this._displayExpectedAndActualString(text, actual, /* quoted */ true) + ); } } @@ -565,7 +650,9 @@ export class TestState { } private _getFileContent(fileName: string): string { - const files = this.testData.files.filter(f => comparePaths(f.fileName, fileName, this.fs.ignoreCase) === Comparison.EqualTo); + const files = this.testData.files.filter( + f => comparePaths(f.fileName, fileName, this.fs.ignoreCase) === Comparison.EqualTo + ); return files[0].content; } @@ -591,9 +678,10 @@ export class TestState { } private _messageAtLastKnownMarker(message: string) { - const locationDescription = this.lastKnownMarker ? this.lastKnownMarker + const locationDescription = this.lastKnownMarker + ? this.lastKnownMarker : this._getLineColStringAtPosition(this.currentCaretPosition); - return `At ${ locationDescription }: ${ message }`; + return `At ${locationDescription}: ${message}`; } private _checkPostEditInvariants() { @@ -639,7 +727,7 @@ export class TestState { if (values) { values.push(value); } else { - this.set(key, values = [value]); + this.set(key, (values = [value])); } return values; } @@ -647,7 +735,11 @@ export class TestState { function multiMapRemove(this: MultiMap, key: string, value: T) { const values = this.get(key); if (values) { - values.forEach((v, i, arr) => { if (v === value) { arr.splice(i, 1); } }); + values.forEach((v, i, arr) => { + if (v === value) { + arr.splice(i, 1); + } + }); if (!values.length) { this.delete(key); } @@ -671,19 +763,24 @@ export class TestState { private _verifyFileContent(fileName: string, text: string) { const actual = this._getFileContent(fileName); if (actual !== text) { - throw new Error(`verifyFileContent failed:\n${ this._showTextDiff(text, actual) }`); + throw new Error(`verifyFileContent failed:\n${this._showTextDiff(text, actual)}`); } } private _verifyTextMatches(actualText: string, includeWhitespace: boolean, expectedText: string) { - const removeWhitespace = (s: string): string => includeWhitespace ? s : this._removeWhitespace(s); + const removeWhitespace = (s: string): string => (includeWhitespace ? s : this._removeWhitespace(s)); if (removeWhitespace(actualText) !== removeWhitespace(expectedText)) { - this._raiseError(`Actual range text doesn't match expected text.\n${ this._showTextDiff(expectedText, actualText) }`); + this._raiseError( + `Actual range text doesn't match expected text.\n${this._showTextDiff(expectedText, actualText)}` + ); } } private _getSelection(): TextRange { - return TextRange.fromBounds(this.currentCaretPosition, this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd); + return TextRange.fromBounds( + this.currentCaretPosition, + this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd + ); } private _getLineContent(index: number) { @@ -716,24 +813,27 @@ export class TestState { // Get the text of the entire line the caret is currently at private _getCurrentLineContent() { - return this._getLineContent(this._convertOffsetToPosition( - this.activeFile.fileName, - this.currentCaretPosition - ).line); + return this._getLineContent( + this._convertOffsetToPosition(this.activeFile.fileName, this.currentCaretPosition).line + ); } private _findFile(indexOrName: string | number): FourSlashFile { if (typeof indexOrName === 'number') { const index = indexOrName; if (index >= this.testData.files.length) { - throw new Error(`File index (${ index }) in openFile was out of range. There are only ${ this.testData.files.length } files in this test.`); + throw new Error( + `File index (${index}) in openFile was out of range. There are only ${this.testData.files.length} files in this test.` + ); } else { return this.testData.files[index]; } } else if (isString(indexOrName)) { const { file, availableNames } = this._tryFindFileWorker(indexOrName); if (!file) { - throw new Error(`No test file named "${ indexOrName }" exists. Available file names are: ${ availableNames.join(', ') }`); + throw new Error( + `No test file named "${indexOrName}" exists. Available file names are: ${availableNames.join(', ')}` + ); } return file; } else { @@ -741,7 +841,9 @@ export class TestState { } } - private _tryFindFileWorker(name: string): { readonly file: FourSlashFile | undefined; readonly availableNames: readonly string[] } { + private _tryFindFileWorker( + name: string + ): { readonly file: FourSlashFile | undefined; readonly availableNames: readonly string[] } { name = normalizePath(name); let file: FourSlashFile | undefined; @@ -763,7 +865,7 @@ export class TestState { private _getLineColStringAtPosition(position: number, file: FourSlashFile = this.activeFile) { const pos = this._convertOffsetToPosition(file.fileName, position); - return `line ${ (pos.line + 1) }, col ${ pos.character }`; + return `line ${pos.line + 1}, col ${pos.character}`; } private _showTextDiff(expected: string, actual: string): string { @@ -784,16 +886,20 @@ export class TestState { const actualMsg = '\x1b[1mActual\x1b[0m\x1b[31m'; const expectedString = quoted ? '"' + expected + '"' : expected; const actualString = quoted ? '"' + actual + '"' : actual; - return `\n${ expectMsg }:\n${ expectedString }\n\n${ actualMsg }:\n${ actualString }`; + return `\n${expectMsg}:\n${expectedString}\n\n${actualMsg}:\n${actualString}`; } private _makeWhitespaceVisible(text: string) { - return text.replace(/ /g, '\u00B7').replace(/\r/g, '\u00B6').replace(/\n/g, '\u2193\n').replace(/\t/g, '\u2192 '); + return text + .replace(/ /g, '\u00B7') + .replace(/\r/g, '\u00B6') + .replace(/\n/g, '\u2193\n') + .replace(/\t/g, '\u2192 '); } private _updatePosition(position: number, editStart: number, editEnd: number, { length }: string): number { // If inside the edit, return -1 to mark as invalid - return position <= editStart ? position : position < editEnd ? -1 : position + length - + (editEnd - editStart); + return position <= editStart ? position : position < editEnd ? -1 : position + length - +(editEnd - editStart); } private _analyze() { @@ -817,16 +923,18 @@ export class TestState { }; return [filePath, value] as [string, typeof value]; } else { - this._raiseError(`Source file not found for ${ this._files[index] }`); + this._raiseError(`Source file not found for ${this._files[index]}`); } }); return new Map(results); } - private _createAnalysisService(nullConsole: ConsoleInterface, - importResolverFactory: ImportResolverFactory, configOptions: ConfigOptions) { - + private _createAnalysisService( + nullConsole: ConsoleInterface, + importResolverFactory: ImportResolverFactory, + configOptions: ConfigOptions + ) { // we do not initiate automatic analysis or file watcher in test. const service = new AnalyzerService('test service', this.fs, nullConsole, importResolverFactory, configOptions); @@ -850,7 +958,11 @@ export class TestState { private async _waitForFile(filePath: string) { while (!this.fs.existsSync(filePath)) { - await (new Promise(res => setTimeout(() => { res(); }, 200))); + await new Promise(res => + setTimeout(() => { + res(); + }, 200) + ); } } @@ -874,7 +986,9 @@ export class TestState { const actual = this.fs.readFileSync(normalizedFilePath, 'utf8'); if (actual !== expected) { - this._raiseError(`doesn't contain expected result: ${ stringify(expected) }, actual: ${ stringify(actual) }`); + this._raiseError( + `doesn't contain expected result: ${stringify(expected)}, actual: ${stringify(actual)}` + ); } } } diff --git a/server/src/tests/harness/host.ts b/server/src/tests/harness/host.ts index 17ea25772..21fa78469 100644 --- a/server/src/tests/harness/host.ts +++ b/server/src/tests/harness/host.ts @@ -8,7 +8,14 @@ import * as os from 'os'; import * as pathModule from 'path'; import { NullConsole } from '../../common/console'; -import { combinePaths, directoryExists, fileExists, FileSystemEntries, getFileSize, resolvePaths } from '../../common/pathUtils'; +import { + combinePaths, + directoryExists, + fileExists, + FileSystemEntries, + getFileSize, + resolvePaths +} from '../../common/pathUtils'; import { compareStringsCaseInsensitive, compareStringsCaseSensitive } from '../../common/stringUtils'; import { createFromRealFileSystem } from '../../common/vfs'; @@ -25,9 +32,13 @@ export interface TestHost { getWorkspaceRoot(): string; writeFile(path: string, contents: string): void; - listFiles(path: string, filter?: RegExp, options?: { - recursive?: boolean; - }): string[]; + listFiles( + path: string, + filter?: RegExp, + options?: { + recursive?: boolean; + } + ): string[]; log(text: string): void; } @@ -80,22 +91,29 @@ function createHost(): TestHost { function getAccessibleFileSystemEntries(dirname: string): FileSystemEntries { try { - const entries: string[] = vfs.readdirSync(dirname || '.').sort( - useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive); + const entries: string[] = vfs + .readdirSync(dirname || '.') + .sort(useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive); const files: string[] = []; const directories: string[] = []; for (const entry of entries) { - if (entry === '.' || entry === '..') { continue; } + if (entry === '.' || entry === '..') { + continue; + } const name = combinePaths(dirname, entry); try { const stat = vfs.statSync(name); - if (!stat) { continue; } + if (!stat) { + continue; + } if (stat.isFile()) { files.push(entry); } else if (stat.isDirectory()) { directories.push(entry); } - } catch { /*ignore*/ } + } catch { + /*ignore*/ + } } return { files, directories }; } catch (e) { @@ -109,7 +127,7 @@ function createHost(): TestHost { } const buffer = vfs.readFileSync(fileName); let len = buffer.length; - if (len >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF) { + if (len >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff) { // Big endian UTF-16 byte order mark detected. Since big endian is not supported by node.js, // flip all byte pairs and treat as little endian. len &= ~1; // Round down to a multiple of 2 @@ -120,11 +138,11 @@ function createHost(): TestHost { } return buffer.toString('utf16le', 2); } - if (len >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) { + if (len >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) { // Little endian UTF-16 byte order mark detected return buffer.toString('utf16le', 2); } - if (len >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) { + if (len >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) { // UTF-8 byte order mark detected return buffer.toString('utf8', 3); } @@ -145,11 +163,15 @@ function createHost(): TestHost { useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, getFileSize: (path: string) => getFileSize(vfs, path), readFile: path => readFile(path), - writeFile: (path, content) => { writeFile(path, content); }, + writeFile: (path, content) => { + writeFile(path, content); + }, fileExists: path => fileExists(vfs, path), directoryExists: path => directoryExists(vfs, path), listFiles, - log: s => { console.log(s); }, + log: s => { + console.log(s); + }, getWorkspaceRoot: () => resolvePaths(__dirname, '../../..'), getAccessibleFileSystemEntries }; diff --git a/server/src/tests/harness/utils.ts b/server/src/tests/harness/utils.ts index 2e1d65108..3690c1f1b 100644 --- a/server/src/tests/harness/utils.ts +++ b/server/src/tests/harness/utils.ts @@ -65,7 +65,9 @@ export class SortedMap { this._writePreamble(); insertAt(this._keys, ~index, key); insertAt(this._values, ~index, value); - if (this._order) { insertAt(this._order, ~index, this._version); } + if (this._order) { + insertAt(this._order, ~index, this._version); + } this._writePostScript(); } return this; @@ -77,7 +79,9 @@ export class SortedMap { this._writePreamble(); this._orderedRemoveItemAt(this._keys, index); this._orderedRemoveItemAt(this._values, index); - if (this._order) { this._orderedRemoveItemAt(this._order, index); } + if (this._order) { + this._orderedRemoveItemAt(this._order, index); + } this._writePostScript(); return true; } @@ -89,7 +93,9 @@ export class SortedMap { this._writePreamble(); this._keys.length = 0; this._values.length = 0; - if (this._order) { this._order.length = 0; } + if (this._order) { + this._order.length = 0; + } this._writePostScript(); } } @@ -117,7 +123,7 @@ export class SortedMap { } } - * keys() { + *keys() { const keys = this._keys; const indices = this._getIterationOrder(); const version = this._version; @@ -137,7 +143,7 @@ export class SortedMap { } } - * values() { + *values() { const values = this._values; const indices = this._getIterationOrder(); const version = this._version; @@ -157,7 +163,7 @@ export class SortedMap { } } - * entries() { + *entries() { const keys = this._keys; const values = this._values; const indices = this._getIterationOrder(); @@ -188,7 +194,9 @@ export class SortedMap { if (this._copyOnWrite) { this._keys = this._keys.slice(); this._values = this._values.slice(); - if (this._order) { this._order = this._order.slice(); } + if (this._order) { + this._order = this._order.slice(); + } this._copyOnWrite = false; } } @@ -200,9 +208,7 @@ export class SortedMap { private _getIterationOrder() { if (this._order) { const order = this._order; - return this._order - .map((_, i) => i) - .sort((x, y) => order[x] - order[y]); + return this._order.map((_, i) => i).sort((x, y) => order[x] - order[y]); } return undefined; } @@ -228,7 +234,9 @@ export function nextResult(iterator: Iterator): IteratorResult | undefi export function closeIterator(iterator: Iterator) { const fn = iterator.return; - if (typeof fn === 'function') { fn.call(iterator); } + if (typeof fn === 'function') { + fn.call(iterator); + } } /** @@ -250,7 +258,9 @@ export class Metadata { get size(): number { if (this._size === -1 || (this._parent && this._parent._version !== this._parentVersion)) { let size = 0; - for (const _ of Object.keys(this._map)) { size++; } + for (const _ of Object.keys(this._map)) { + size++; + } this._size = size; if (this._parent) { this._parentVersion = this._parent._version; @@ -303,18 +313,21 @@ export class Metadata { } private static _escapeKey(text: string) { - return (text.length >= 2 && text.charAt(0) === '_' && text.charAt(1) === '_' ? '_' + text : text); + return text.length >= 2 && text.charAt(0) === '_' && text.charAt(1) === '_' ? '_' + text : text; } private static _unescapeKey(text: string) { - return (text.length >= 3 && text.charAt(0) === '_' && text.charAt(1) === '_' && text.charAt(2) === '_' ? text.slice(1) : text); + return text.length >= 3 && text.charAt(0) === '_' && text.charAt(1) === '_' && text.charAt(2) === '_' + ? text.slice(1) + : text; } } export function bufferFrom(input: string, encoding?: BufferEncoding): Buffer { // See https://github.com/Microsoft/TypeScript/issues/25652 return Buffer.from && (Buffer.from as Function) !== Int8Array.from - ? Buffer.from(input, encoding) : new Buffer(input, encoding); + ? Buffer.from(input, encoding) + : new Buffer(input, encoding); } export const IO_ERROR_MESSAGE = Object.freeze({ @@ -333,9 +346,11 @@ export const IO_ERROR_MESSAGE = Object.freeze({ }); export function createIOError(code: keyof typeof IO_ERROR_MESSAGE, details = '') { - const err: NodeJS.ErrnoException = new Error(`${ code }: ${ IO_ERROR_MESSAGE[code] } ${ details }`); + const err: NodeJS.ErrnoException = new Error(`${code}: ${IO_ERROR_MESSAGE[code]} ${details}`); err.code = code; - if (Error.captureStackTrace) { Error.captureStackTrace(err, createIOError); } + if (Error.captureStackTrace) { + Error.captureStackTrace(err, createIOError); + } return err; } diff --git a/server/src/tests/harness/vfs/factory.ts b/server/src/tests/harness/vfs/factory.ts index 936f49cfa..d74e6dad4 100644 --- a/server/src/tests/harness/vfs/factory.ts +++ b/server/src/tests/harness/vfs/factory.ts @@ -11,7 +11,16 @@ import { combinePaths, getDirectoryPath, normalizeSlashes, resolvePaths } from ' import { GlobalMetadataOptionNames } from '../fourslash/fourSlashTypes'; import { TestHost } from '../host'; import { bufferFrom } from '../utils'; -import { FileSet, FileSystem, FileSystemOptions, FileSystemResolver, MODULE_PATH, Mount, S_IFDIR, S_IFREG } from './filesystem'; +import { + FileSet, + FileSystem, + FileSystemOptions, + FileSystemResolver, + MODULE_PATH, + Mount, + S_IFDIR, + S_IFREG +} from './filesystem'; export class TextDocument { readonly meta: Map; @@ -30,8 +39,10 @@ export interface FileSystemCreateOptions extends FileSystemOptions { documents?: readonly TextDocument[]; } -export const libFolder = combinePaths(MODULE_PATH, normalizeSlashes( - combinePaths(pathConsts.lib, pathConsts.sitePackages))); +export const libFolder = combinePaths( + MODULE_PATH, + normalizeSlashes(combinePaths(pathConsts.lib, pathConsts.sitePackages)) +); export const typeshedFolder = combinePaths(MODULE_PATH, normalizeSlashes(pathConsts.typeshedFallback)); export const srcFolder = normalizeSlashes('/.src'); @@ -51,10 +62,12 @@ export const srcFolder = normalizeSlashes('/.src'); * * all `FileSystemCreateOptions` are optional */ -export function createFromFileSystem(host: TestHost, ignoreCase: boolean, +export function createFromFileSystem( + host: TestHost, + ignoreCase: boolean, { documents, files, cwd, time, meta }: FileSystemCreateOptions = {}, - mountPaths: Map = new Map()) { - + mountPaths: Map = new Map() +) { const typeshedPath = meta ? meta[GlobalMetadataOptionNames.typeshed] : undefined; if (typeshedPath) { mountPaths.set(typeshedFolder, typeshedPath); @@ -101,8 +114,10 @@ let localCSFSCache: FileSystem | undefined; function getBuiltLocal(host: TestHost, ignoreCase: boolean, mountPaths: Map): FileSystem { // Ensure typeshed folder if (!mountPaths.has(typeshedFolder)) { - mountPaths.set(typeshedFolder, resolvePaths(host.getWorkspaceRoot(), '../client/' + - pathConsts.typeshedFallback)); + mountPaths.set( + typeshedFolder, + resolvePaths(host.getWorkspaceRoot(), '../client/' + pathConsts.typeshedFallback) + ); } if (!canReuseCache(host, mountPaths)) { @@ -114,7 +129,7 @@ function getBuiltLocal(host: TestHost, ignoreCase: boolean, mountPaths: Map files[k] = new Mount(v, resolver)); + mountPaths.forEach((v, k) => (files[k] = new Mount(v, resolver))); localCIFSCache = new FileSystem(/*ignoreCase*/ true, { files, @@ -124,7 +139,9 @@ function getBuiltLocal(host: TestHost, ignoreCase: boolean, mountPaths: Map): boolean { - if (cacheKey === undefined) { return false; } - if (cacheKey.host !== host) { return false; } - if (cacheKey.mountPaths.size !== mountPaths.size) { return false; } + if (cacheKey === undefined) { + return false; + } + if (cacheKey.host !== host) { + return false; + } + if (cacheKey.mountPaths.size !== mountPaths.size) { + return false; + } for (const key of cacheKey.mountPaths.keys()) { if (cacheKey.mountPaths.get(key) !== mountPaths.get(key)) { diff --git a/server/src/tests/harness/vfs/filesystem.ts b/server/src/tests/harness/vfs/filesystem.ts index ce9ac0113..bf667bdf7 100644 --- a/server/src/tests/harness/vfs/filesystem.ts +++ b/server/src/tests/harness/vfs/filesystem.ts @@ -47,7 +47,9 @@ export class FileSystem { constructor(ignoreCase: boolean, options: FileSystemOptions = {}) { const { time = -1, files, meta } = options; this.ignoreCase = ignoreCase; - this.stringComparer = this.ignoreCase ? pathUtil.comparePathsCaseInsensitive : pathUtil.comparePathsCaseSensitive; + this.stringComparer = this.ignoreCase + ? pathUtil.comparePathsCaseInsensitive + : pathUtil.comparePathsCaseSensitive; this._time = time; if (meta) { @@ -120,7 +122,9 @@ export class FileSystem { * no action if this file system is read-only. */ snapshot() { - if (this.isReadonly) { return; } + if (this.isReadonly) { + return; + } const fs = new FileSystem(this.ignoreCase, { time: this._time }); fs._lazy = this._lazy; fs._cwd = this._cwd; @@ -138,8 +142,12 @@ export class FileSystem { * of the same data. */ shadow(ignoreCase = this.ignoreCase) { - if (!this.isReadonly) { throw new Error('Cannot shadow a mutable file system.'); } - if (ignoreCase && !this.ignoreCase) { throw new Error('Cannot create a case-insensitive file system from a case-sensitive one.'); } + if (!this.isReadonly) { + throw new Error('Cannot shadow a mutable file system.'); + } + if (ignoreCase && !this.ignoreCase) { + throw new Error('Cannot create a case-insensitive file system from a case-sensitive one.'); + } const fs = new FileSystem(ignoreCase, { time: this._time }); fs._shadowRoot = this; fs._cwd = this._cwd; @@ -152,11 +160,19 @@ export class FileSystem { * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html */ time(value?: number | Date | (() => number | Date)): number { - if (value !== undefined && this.isReadonly) { throw createIOError('EPERM'); } + if (value !== undefined && this.isReadonly) { + throw createIOError('EPERM'); + } let result = this._time; - if (typeof result === 'function') { result = result(); } - if (typeof result === 'object') { result = result.getTime(); } - if (result === -1) { result = Date.now(); } + if (typeof result === 'function') { + result = result(); + } + if (typeof result === 'object') { + result = result.getTime(); + } + if (result === -1) { + result = Date.now(); + } if (value !== undefined) { this._time = value; } @@ -169,7 +185,9 @@ export class FileSystem { */ filemeta(path: string): Metadata { const { node } = this._walk(this._resolve(path)); - if (!node) { throw createIOError('ENOENT'); } + if (!node) { + throw createIOError('ENOENT'); + } return this._filemeta(node); } @@ -187,10 +205,16 @@ export class FileSystem { * @link - http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html */ cwd() { - if (!this._cwd) { throw new Error('The current working directory has not been set.'); } + if (!this._cwd) { + throw new Error('The current working directory has not been set.'); + } const { node } = this._walk(this._cwd); - if (!node) { throw createIOError('ENOENT'); } - if (!isDirectory(node)) { throw createIOError('ENOTDIR'); } + if (!node) { + throw createIOError('ENOENT'); + } + if (!isDirectory(node)) { + throw createIOError('ENOTDIR'); + } return this._cwd; } @@ -200,11 +224,17 @@ export class FileSystem { * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html */ chdir(path: string) { - if (this.isReadonly) { throw createIOError('EPERM'); } + if (this.isReadonly) { + throw createIOError('EPERM'); + } path = this._resolve(path); const { node } = this._walk(path); - if (!node) { throw createIOError('ENOENT'); } - if (!isDirectory(node)) { throw createIOError('ENOTDIR'); } + if (!node) { + throw createIOError('ENOENT'); + } + if (!isDirectory(node)) { + throw createIOError('ENOTDIR'); + } this._cwd = path; } @@ -212,10 +242,16 @@ export class FileSystem { * Pushes the current directory onto the directory stack and changes the current working directory to the supplied path. */ pushd(path?: string) { - if (this.isReadonly) { throw createIOError('EPERM'); } - if (path) { path = this._resolve(path); } + if (this.isReadonly) { + throw createIOError('EPERM'); + } + if (path) { + path = this._resolve(path); + } if (this._cwd) { - if (!this._dirStack) { this._dirStack = []; } + if (!this._dirStack) { + this._dirStack = []; + } this._dirStack.push(this._cwd); } if (path && path !== this._cwd) { @@ -227,7 +263,9 @@ export class FileSystem { * Pops the previous directory from the location stack and changes the current directory to that directory. */ popd() { - if (this.isReadonly) { throw createIOError('EPERM'); } + if (this.isReadonly) { + throw createIOError('EPERM'); + } const path = this._dirStack && this._dirStack.pop(); if (path) { this.chdir(path); @@ -268,7 +306,11 @@ export class FileSystem { } createFileSystemWatcher(paths: string[], event: 'all', listener: Listener): FileWatcher { - return { close: () => { /* left empty */ } }; + return { + close: () => { + /* left empty */ + } + }; } getModulePath(): string { @@ -289,7 +331,9 @@ export class FileSystem { if (!traversal.traverse || traversal.traverse(dirname, stats)) { this._scan(dirname, stats, 'ancestors-or-self', traversal, noFollow, results); } - } catch { /*ignored*/ } + } catch { + /*ignored*/ + } } } if (axis === 'descendants-or-self' || axis === 'descendants') { @@ -299,7 +343,9 @@ export class FileSystem { const childpath = pathUtil.combinePaths(path, file); const stats = this._stat(this._walk(childpath, noFollow)); this._scan(childpath, stats, 'descendants-or-self', traversal, noFollow, results); - } catch { /*ignored*/ } + } catch { + /*ignored*/ + } } } } @@ -313,12 +359,16 @@ export class FileSystem { * @param resolver An object used to resolve files in `source`. */ mountSync(source: string, target: string, resolver: FileSystemResolver) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } source = validate(source, ValidationFlags.Absolute); const { parent, links, node: existingNode, basename } = this._walk(this._resolve(target), /*noFollow*/ true); - if (existingNode) { throw createIOError('EEXIST'); } + if (existingNode) { + throw createIOError('EEXIST'); + } const time = this.time(); const node = this._mknod(parent ? parent.dev : ++devCount, S_IFDIR, /*mode*/ 0o777, time); @@ -342,7 +392,9 @@ export class FileSystem { this.rmdirSync(path); } } catch (e) { - if (e.code === 'ENOENT') { return; } + if (e.code === 'ENOENT') { + return; + } throw e; } } @@ -360,7 +412,9 @@ export class FileSystem { return 'throw'; }); - if (!result.node) { this._mkdir(result); } + if (!result.node) { + this._mkdir(result); + } } getFileListing(): string { @@ -372,7 +426,9 @@ export class FileSystem { const [name, node] = i.value; const path = dirname ? pathUtil.combinePaths(dirname, name) : name; const marker = pathUtil.comparePaths(this._cwd, path, this.ignoreCase) === 0 ? '*' : ' '; - if (result) { result += '\n'; } + if (result) { + result += '\n'; + } result += marker; if (isDirectory(node)) { result += pathUtil.ensureTrailingDirectorySeparator(path); @@ -380,7 +436,7 @@ export class FileSystem { } else if (isFile(node)) { result += path; } else if (isSymlink(node)) { - result += `${ path } -> ${ node.symlink }`; + result += `${path} -> ${node.symlink}`; } } } finally { @@ -425,8 +481,12 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ utimesSync(path: string, atime: Date, mtime: Date) { - if (this.isReadonly) { throw createIOError('EROFS'); } - if (!isFinite(+atime) || !isFinite(+mtime)) { throw createIOError('EINVAL'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } + if (!isFinite(+atime) || !isFinite(+mtime)) { + throw createIOError('EINVAL'); + } const entry = this._walk(this._resolve(path)); if (!entry || !entry.node) { @@ -450,16 +510,18 @@ export class FileSystem { private _stat(entry: WalkResult) { const node = entry.node; - if (!node) { throw createIOError(`ENOENT`, entry.realpath); } + if (!node) { + throw createIOError(`ENOENT`, entry.realpath); + } return new Stats( node.dev, node.ino, node.mode, node.nlink, - /*rdev*/ 0, - /*size*/ isFile(node) ? this._getSize(node) : isSymlink(node) ? node.symlink.length : 0, - /*blksize*/ 4096, - /*blocks*/ 0, + /*rdev*/ 0, + /*size*/ isFile(node) ? this._getSize(node) : isSymlink(node) ? node.symlink.length : 0, + /*blksize*/ 4096, + /*blocks*/ 0, node.atimeMs, node.mtimeMs, node.ctimeMs, @@ -476,8 +538,12 @@ export class FileSystem { */ readdirSync(path: string) { const { node } = this._walk(this._resolve(path)); - if (!node) { throw createIOError('ENOENT'); } - if (!isDirectory(node)) { throw createIOError('ENOTDIR'); } + if (!node) { + throw createIOError('ENOENT'); + } + if (!isDirectory(node)) { + throw createIOError('ENOTDIR'); + } return Array.from(this._getLinks(node).keys()); } @@ -489,13 +555,17 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ mkdirSync(path: string) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } this._mkdir(this._walk(this._resolve(path), /*noFollow*/ true)); } private _mkdir({ parent, links, node: existingNode, basename }: WalkResult) { - if (existingNode) { throw createIOError('EEXIST'); } + if (existingNode) { + throw createIOError('EEXIST'); + } const time = this.time(); const node = this._mknod(parent ? parent.dev : ++devCount, S_IFDIR, /*mode*/ 0o777, time); this._addLink(parent, links, basename, node, time); @@ -509,13 +579,21 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ rmdirSync(path: string) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } path = this._resolve(path); const { parent, links, node, basename } = this._walk(path, /*noFollow*/ true); - if (!parent) { throw createIOError('EPERM'); } - if (!isDirectory(node)) { throw createIOError('ENOTDIR'); } - if (this._getLinks(node).size !== 0) { throw createIOError('ENOTEMPTY'); } + if (!parent) { + throw createIOError('EPERM'); + } + if (!isDirectory(node)) { + throw createIOError('ENOTDIR'); + } + if (this._getLinks(node).size !== 0) { + throw createIOError('ENOTEMPTY'); + } this._removeLink(parent, links, basename, node); } @@ -528,15 +606,25 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ linkSync(oldpath: string, newpath: string) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } const { node } = this._walk(this._resolve(oldpath)); - if (!node) { throw createIOError('ENOENT'); } - if (isDirectory(node)) { throw createIOError('EPERM'); } + if (!node) { + throw createIOError('ENOENT'); + } + if (isDirectory(node)) { + throw createIOError('EPERM'); + } const { parent, links, basename, node: existingNode } = this._walk(this._resolve(newpath), /*noFollow*/ true); - if (!parent) { throw createIOError('EPERM'); } - if (existingNode) { throw createIOError('EEXIST'); } + if (!parent) { + throw createIOError('EPERM'); + } + if (existingNode) { + throw createIOError('EEXIST'); + } this._addLink(parent, links, basename, node); } @@ -549,12 +637,20 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ unlinkSync(path: string) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } const { parent, links, node, basename } = this._walk(this._resolve(path), /*noFollow*/ true); - if (!parent) { throw createIOError('EPERM'); } - if (!node) { throw createIOError('ENOENT'); } - if (isDirectory(node)) { throw createIOError('EISDIR'); } + if (!parent) { + throw createIOError('EPERM'); + } + if (!node) { + throw createIOError('ENOENT'); + } + if (isDirectory(node)) { + throw createIOError('EISDIR'); + } this._removeLink(parent, links, basename, node); } @@ -567,26 +663,44 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ renameSync(oldpath: string, newpath: string) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } - const { parent: oldParent, links: oldParentLinks, node, basename: oldBasename } - = this._walk(this._resolve(oldpath), /*noFollow*/ true); + const { parent: oldParent, links: oldParentLinks, node, basename: oldBasename } = this._walk( + this._resolve(oldpath), + /*noFollow*/ true + ); - if (!oldParent) { throw createIOError('EPERM'); } - if (!node) { throw createIOError('ENOENT'); } + if (!oldParent) { + throw createIOError('EPERM'); + } + if (!node) { + throw createIOError('ENOENT'); + } - const { parent: newParent, links: newParentLinks, node: existingNode, basename: newBasename } - = this._walk(this._resolve(newpath), /*noFollow*/ true); + const { parent: newParent, links: newParentLinks, node: existingNode, basename: newBasename } = this._walk( + this._resolve(newpath), + /*noFollow*/ true + ); - if (!newParent) { throw createIOError('EPERM'); } + if (!newParent) { + throw createIOError('EPERM'); + } const time = this.time(); if (existingNode) { if (isDirectory(node)) { - if (!isDirectory(existingNode)) { throw createIOError('ENOTDIR'); } - if (this._getLinks(existingNode).size > 0) { throw createIOError('ENOTEMPTY'); } + if (!isDirectory(existingNode)) { + throw createIOError('ENOTDIR'); + } + if (this._getLinks(existingNode).size > 0) { + throw createIOError('ENOTEMPTY'); + } } else { - if (isDirectory(existingNode)) { throw createIOError('EISDIR'); } + if (isDirectory(existingNode)) { + throw createIOError('EISDIR'); + } } this._removeLink(newParent, newParentLinks, newBasename, existingNode, time); } @@ -602,11 +716,17 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ symlinkSync(target: string, linkpath: string) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } const { parent, links, node: existingNode, basename } = this._walk(this._resolve(linkpath), /*noFollow*/ true); - if (!parent) { throw createIOError('EPERM'); } - if (existingNode) { throw createIOError('EEXIST'); } + if (!parent) { + throw createIOError('EPERM'); + } + if (existingNode) { + throw createIOError('EEXIST'); + } const time = this.time(); const node = this._mknod(parent.dev, S_IFLNK, /*mode*/ 0o666, time); @@ -646,9 +766,15 @@ export class FileSystem { readFileSync(path: string, encoding?: string | null): string | Buffer; readFileSync(path: string, encoding: string | null = null) { const { node } = this._walk(this._resolve(path)); - if (!node) { throw createIOError('ENOENT'); } - if (isDirectory(node)) { throw createIOError('EISDIR'); } - if (!isFile(node)) { throw createIOError('EBADF'); } + if (!node) { + throw createIOError('ENOENT'); + } + if (isDirectory(node)) { + throw createIOError('EISDIR'); + } + if (!isFile(node)) { + throw createIOError('EBADF'); + } const buffer = this._getBuffer(node).slice(); return encoding ? buffer.toString(encoding) : buffer; @@ -660,10 +786,14 @@ export class FileSystem { * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module. */ writeFileSync(path: string, data: string | Buffer, encoding: string | null = null) { - if (this.isReadonly) { throw createIOError('EROFS'); } + if (this.isReadonly) { + throw createIOError('EROFS'); + } const { parent, links, node: existingNode, basename } = this._walk(this._resolve(path), /*noFollow*/ false); - if (!parent) { throw createIOError('EPERM'); } + if (!parent) { + throw createIOError('EPERM'); + } const time = this.time(); let node = existingNode; @@ -672,9 +802,15 @@ export class FileSystem { this._addLink(parent, links, basename, node, time); } - if (isDirectory(node)) { throw createIOError('EISDIR'); } - if (!isFile(node)) { throw createIOError('EBADF'); } - node.buffer = Buffer.isBuffer(data) ? data.slice() : bufferFrom('' + data, (encoding as BufferEncoding) || 'utf8'); + if (isDirectory(node)) { + throw createIOError('EISDIR'); + } + if (!isFile(node)) { + throw createIOError('EBADF'); + } + node.buffer = Buffer.isBuffer(data) + ? data.slice() + : bufferFrom('' + data, (encoding as BufferEncoding) || 'utf8'); node.size = node.buffer.byteLength; node.mtimeMs = time; node.ctimeMs = time; @@ -686,9 +822,9 @@ export class FileSystem { */ diff(base = this.shadowRoot, options: DiffOptions = {}) { const differences: FileSet = {}; - const hasDifferences = base ? - FileSystem._rootDiff(differences, this, base, options) : - FileSystem._trackCreatedInodes(differences, this, this._getRootLinks()); + const hasDifferences = base + ? FileSystem._rootDiff(differences, this, base, options) + : FileSystem._trackCreatedInodes(differences, this, this._getRootLinks()); return hasDifferences ? differences : undefined; } @@ -697,16 +833,23 @@ export class FileSystem { */ static diff(changed: FileSystem, base: FileSystem, options: DiffOptions = {}) { const differences: FileSet = {}; - return FileSystem._rootDiff(differences, changed, base, options) ? - differences : - undefined; + return FileSystem._rootDiff(differences, changed, base, options) ? differences : undefined; } - private static _diffWorker(container: FileSet, changed: FileSystem, changedLinks: ReadonlyMap | undefined, - base: FileSystem, baseLinks: ReadonlyMap | undefined, options: DiffOptions) { - - if (changedLinks && !baseLinks) { return FileSystem._trackCreatedInodes(container, changed, changedLinks); } - if (baseLinks && !changedLinks) { return FileSystem._trackDeletedInodes(container, baseLinks); } + private static _diffWorker( + container: FileSet, + changed: FileSystem, + changedLinks: ReadonlyMap | undefined, + base: FileSystem, + baseLinks: ReadonlyMap | undefined, + options: DiffOptions + ) { + if (changedLinks && !baseLinks) { + return FileSystem._trackCreatedInodes(container, changed, changedLinks); + } + if (baseLinks && !changedLinks) { + return FileSystem._trackDeletedInodes(container, baseLinks); + } if (changedLinks && baseLinks) { let hasChanges = false; // track base items missing in changed @@ -721,18 +864,29 @@ export class FileSystem { const baseNode = baseLinks.get(basename); if (baseNode) { if (isDirectory(changedNode) && isDirectory(baseNode)) { - return hasChanges = FileSystem._directoryDiff(container, basename, - changed, changedNode, base, baseNode, options) || hasChanges; + return (hasChanges = + FileSystem._directoryDiff( + container, + basename, + changed, + changedNode, + base, + baseNode, + options + ) || hasChanges); } if (isFile(changedNode) && isFile(baseNode)) { - return hasChanges = FileSystem._fileDiff(container, basename, - changed, changedNode, base, baseNode, options) || hasChanges; + return (hasChanges = + FileSystem._fileDiff(container, basename, changed, changedNode, base, baseNode, options) || + hasChanges); } if (isSymlink(changedNode) && isSymlink(baseNode)) { - return hasChanges = FileSystem._symlinkDiff(container, basename, changedNode, baseNode) || hasChanges; + return (hasChanges = + FileSystem._symlinkDiff(container, basename, changedNode, baseNode) || hasChanges); } } - return hasChanges = FileSystem._trackCreatedInode(container, basename, changed, changedNode) || hasChanges; + return (hasChanges = + FileSystem._trackCreatedInode(container, basename, changed, changedNode) || hasChanges); }); return hasChanges; } @@ -740,38 +894,76 @@ export class FileSystem { } private static _rootDiff(container: FileSet, changed: FileSystem, base: FileSystem, options: DiffOptions) { - while (!changed._lazy.links && changed._shadowRoot) { changed = changed._shadowRoot; } - while (!base._lazy.links && base._shadowRoot) { base = base._shadowRoot; } + while (!changed._lazy.links && changed._shadowRoot) { + changed = changed._shadowRoot; + } + while (!base._lazy.links && base._shadowRoot) { + base = base._shadowRoot; + } // no difference if the file systems are the same reference - if (changed === base) { return false; } + if (changed === base) { + return false; + } // no difference if the root links are empty and unshadowed - if (!changed._lazy.links && !changed._shadowRoot && !base._lazy.links && !base._shadowRoot) { return false; } + if (!changed._lazy.links && !changed._shadowRoot && !base._lazy.links && !base._shadowRoot) { + return false; + } return FileSystem._diffWorker(container, changed, changed._getRootLinks(), base, base._getRootLinks(), options); } - private static _directoryDiff(container: FileSet, basename: string, changed: FileSystem, - changedNode: DirectoryInode, base: FileSystem, baseNode: DirectoryInode, options: DiffOptions) { - - while (!changedNode.links && changedNode.shadowRoot) { changedNode = changedNode.shadowRoot; } - while (!baseNode.links && baseNode.shadowRoot) { baseNode = baseNode.shadowRoot; } + private static _directoryDiff( + container: FileSet, + basename: string, + changed: FileSystem, + changedNode: DirectoryInode, + base: FileSystem, + baseNode: DirectoryInode, + options: DiffOptions + ) { + while (!changedNode.links && changedNode.shadowRoot) { + changedNode = changedNode.shadowRoot; + } + while (!baseNode.links && baseNode.shadowRoot) { + baseNode = baseNode.shadowRoot; + } // no difference if the nodes are the same reference - if (changedNode === baseNode) { return false; } + if (changedNode === baseNode) { + return false; + } // no difference if both nodes are non shadowed and have no entries - if (isEmptyNonShadowedDirectory(changedNode) && isEmptyNonShadowedDirectory(baseNode)) { return false; } + if (isEmptyNonShadowedDirectory(changedNode) && isEmptyNonShadowedDirectory(baseNode)) { + return false; + } // no difference if both nodes are unpopulated and point to the same mounted file system - if (!changedNode.links && !baseNode.links && - changedNode.resolver && changedNode.source !== undefined && - baseNode.resolver === changedNode.resolver && baseNode.source === changedNode.source) { return false; } + if ( + !changedNode.links && + !baseNode.links && + changedNode.resolver && + changedNode.source !== undefined && + baseNode.resolver === changedNode.resolver && + baseNode.source === changedNode.source + ) { + return false; + } // no difference if both nodes have identical children const children: FileSet = {}; - if (!FileSystem._diffWorker(children, changed, changed._getLinks(changedNode), base, base._getLinks(baseNode), options)) { + if ( + !FileSystem._diffWorker( + children, + changed, + changed._getLinks(changedNode), + base, + base._getLinks(baseNode), + options + ) + ) { return false; } @@ -779,32 +971,57 @@ export class FileSystem { return true; } - private static _fileDiff(container: FileSet, basename: string, changed: FileSystem, - changedNode: FileInode, base: FileSystem, baseNode: FileInode, options: DiffOptions) { - - while (!changedNode.buffer && changedNode.shadowRoot) { changedNode = changedNode.shadowRoot; } - while (!baseNode.buffer && baseNode.shadowRoot) { baseNode = baseNode.shadowRoot; } + private static _fileDiff( + container: FileSet, + basename: string, + changed: FileSystem, + changedNode: FileInode, + base: FileSystem, + baseNode: FileInode, + options: DiffOptions + ) { + while (!changedNode.buffer && changedNode.shadowRoot) { + changedNode = changedNode.shadowRoot; + } + while (!baseNode.buffer && baseNode.shadowRoot) { + baseNode = baseNode.shadowRoot; + } // no difference if the nodes are the same reference - if (changedNode === baseNode) { return false; } + if (changedNode === baseNode) { + return false; + } // no difference if both nodes are non shadowed and have no entries - if (isEmptyNonShadowedFile(changedNode) && isEmptyNonShadowedFile(baseNode)) { return false; } + if (isEmptyNonShadowedFile(changedNode) && isEmptyNonShadowedFile(baseNode)) { + return false; + } // no difference if both nodes are unpopulated and point to the same mounted file system - if (!changedNode.buffer && !baseNode.buffer && - changedNode.resolver && changedNode.source !== undefined && - baseNode.resolver === changedNode.resolver && baseNode.source === changedNode.source) { return false; } + if ( + !changedNode.buffer && + !baseNode.buffer && + changedNode.resolver && + changedNode.source !== undefined && + baseNode.resolver === changedNode.resolver && + baseNode.source === changedNode.source + ) { + return false; + } const changedBuffer = changed._getBuffer(changedNode); const baseBuffer = base._getBuffer(baseNode); // no difference if both buffers are the same reference - if (changedBuffer === baseBuffer) { return false; } + if (changedBuffer === baseBuffer) { + return false; + } // no difference if both buffers are identical if (Buffer.compare(changedBuffer, baseBuffer) === 0) { - if (!options.includeChangedFileWithSameContent) { return false; } + if (!options.includeChangedFileWithSameContent) { + return false; + } container[basename] = new SameFileContentFile(changedBuffer); return true; } @@ -813,9 +1030,16 @@ export class FileSystem { return true; } - private static _symlinkDiff(container: FileSet, basename: string, changedNode: SymlinkInode, baseNode: SymlinkInode) { + private static _symlinkDiff( + container: FileSet, + basename: string, + changedNode: SymlinkInode, + baseNode: SymlinkInode + ) { // no difference if the nodes are the same reference - if (changedNode.symlink === baseNode.symlink) { return false; } + if (changedNode.symlink === baseNode.symlink) { + return false; + } container[basename] = new Symlink(changedNode.symlink); return true; } @@ -833,18 +1057,30 @@ export class FileSystem { return true; } - private static _trackCreatedInodes(container: FileSet, changed: FileSystem, changedLinks: ReadonlyMap) { + private static _trackCreatedInodes( + container: FileSet, + changed: FileSystem, + changedLinks: ReadonlyMap + ) { // no difference if links are empty - if (!changedLinks.size) { return false; } + if (!changedLinks.size) { + return false; + } - changedLinks.forEach((node, basename) => { FileSystem._trackCreatedInode(container, basename, changed, node); }); + changedLinks.forEach((node, basename) => { + FileSystem._trackCreatedInode(container, basename, changed, node); + }); return true; } private static _trackDeletedInodes(container: FileSet, baseLinks: ReadonlyMap) { // no difference if links are empty - if (!baseLinks.size) { return false; } - baseLinks.forEach((node, basename) => { container[basename] = isDirectory(node) ? new Rmdir() : new Unlink(); }); + if (!baseLinks.size) { + return false; + } + baseLinks.forEach((node, basename) => { + container[basename] = isDirectory(node) ? new Rmdir() : new Unlink(); + }); return true; } @@ -864,26 +1100,49 @@ export class FileSystem { }; } - private _addLink(parent: DirectoryInode | undefined, links: SortedMap, name: string, node: Inode, time = this.time()) { + private _addLink( + parent: DirectoryInode | undefined, + links: SortedMap, + name: string, + node: Inode, + time = this.time() + ) { links.set(name, node); node.nlink++; node.ctimeMs = time; - if (parent) { parent.mtimeMs = time; } - if (!parent && !this._cwd) { this._cwd = name; } + if (parent) { + parent.mtimeMs = time; + } + if (!parent && !this._cwd) { + this._cwd = name; + } } - private _removeLink(parent: DirectoryInode | undefined, links: SortedMap, - name: string, node: Inode, time = this.time()) { - + private _removeLink( + parent: DirectoryInode | undefined, + links: SortedMap, + name: string, + node: Inode, + time = this.time() + ) { links.delete(name); node.nlink--; node.ctimeMs = time; - if (parent) { parent.mtimeMs = time; } + if (parent) { + parent.mtimeMs = time; + } } - private _replaceLink(oldParent: DirectoryInode, oldLinks: SortedMap, - oldName: string, newParent: DirectoryInode, newLinks: SortedMap, newName: string, node: Inode, time: number) { - + private _replaceLink( + oldParent: DirectoryInode, + oldLinks: SortedMap, + oldName: string, + newParent: DirectoryInode, + newLinks: SortedMap, + newName: string, + node: Inode, + time: number + ) { if (oldParent !== newParent) { this._removeLink(oldParent, oldLinks, oldName, node, time); this._addLink(newParent, newLinks, newName, node, time); @@ -918,20 +1177,20 @@ export class FileSystem { const stats = resolver.statSync(path); switch (stats.mode & S_IFMT) { case S_IFDIR: { - const dir = this._mknod(node.dev, S_IFDIR, 0o777); - dir.source = pathUtil.combinePaths(source, name); - dir.resolver = resolver; - this._addLink(node, links, name, dir); - break; - } + const dir = this._mknod(node.dev, S_IFDIR, 0o777); + dir.source = pathUtil.combinePaths(source, name); + dir.resolver = resolver; + this._addLink(node, links, name, dir); + break; + } case S_IFREG: { - const file = this._mknod(node.dev, S_IFREG, 0o666); - file.source = pathUtil.combinePaths(source, name); - file.resolver = resolver; - file.size = stats.size; - this._addLink(node, links, name, file); - break; - } + const file = this._mknod(node.dev, S_IFREG, 0o666); + file.source = pathUtil.combinePaths(source, name); + file.resolver = resolver; + file.size = stats.size; + this._addLink(node, links, name, file); + break; + } } } } else if (this._shadowRoot && node.shadowRoot) { @@ -961,7 +1220,9 @@ export class FileSystem { shadowRoot: root }; - if (isSymlink(root)) { (shadow as SymlinkInode).symlink = root.symlink; } + if (isSymlink(root)) { + (shadow as SymlinkInode).symlink = root.symlink; + } shadows.set(shadow.ino, shadow); } @@ -981,10 +1242,18 @@ export class FileSystem { } private _getSize(node: FileInode): number { - if (node.buffer) { return node.buffer.byteLength; } - if (node.size !== undefined) { return node.size; } - if (node.source && node.resolver) { return node.size = node.resolver.statSync(node.source).size; } - if (this._shadowRoot && node.shadowRoot) { return node.size = this._shadowRoot._getSize(node.shadowRoot); } + if (node.buffer) { + return node.buffer.byteLength; + } + if (node.size !== undefined) { + return node.size; + } + if (node.source && node.resolver) { + return (node.size = node.resolver.statSync(node.source).size); + } + if (this._shadowRoot && node.shadowRoot) { + return (node.size = this._shadowRoot._getSize(node.shadowRoot)); + } return 0; } @@ -1014,10 +1283,21 @@ export class FileSystem { * * @link http://man7.org/linux/man-pages/man7/path_resolution.7.html */ - private _walk(path: string, noFollow?: boolean, - onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => 'retry' | 'throw'): WalkResult; - private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => 'stop' | 'retry' | 'throw'): WalkResult | undefined; - private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => 'stop' | 'retry' | 'throw'): WalkResult | undefined { + private _walk( + path: string, + noFollow?: boolean, + onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => 'retry' | 'throw' + ): WalkResult; + private _walk( + path: string, + noFollow?: boolean, + onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => 'stop' | 'retry' | 'throw' + ): WalkResult | undefined; + private _walk( + path: string, + noFollow?: boolean, + onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => 'stop' | 'retry' | 'throw' + ): WalkResult | undefined { let links = this._getRootLinks(); let parent: DirectoryInode | undefined; let components = pathUtil.getPathComponents(path); @@ -1025,7 +1305,9 @@ export class FileSystem { let depth = 0; let retry = false; while (true) { - if (depth >= 40) { throw createIOError('ELOOP'); } + if (depth >= 40) { + throw createIOError('ELOOP'); + } const lastStep = step === components.length - 1; const basename = components[step]; const node = links.get(basename); @@ -1033,7 +1315,9 @@ export class FileSystem { return { realpath: pathUtil.combinePathComponents(components), basename, parent, links, node }; } if (node === undefined) { - if (trapError(createIOError('ENOENT'), node)) { continue; } + if (trapError(createIOError('ENOENT'), node)) { + continue; + } return undefined; } if (isSymlink(node)) { @@ -1054,7 +1338,9 @@ export class FileSystem { retry = false; continue; } - if (trapError(createIOError('ENOTDIR'), node)) { continue; } + if (trapError(createIOError('ENOTDIR'), node)) { + continue; + } return undefined; } @@ -1062,7 +1348,9 @@ export class FileSystem { const realpath = pathUtil.combinePathComponents(components.slice(0, step + 1)); const basename = components[step]; const result = !retry && onError ? onError(error, { realpath, basename, parent, links, node }) : 'throw'; - if (result === 'stop') { return false; } + if (result === 'stop') { + return false; + } if (result === 'retry') { retry = true; return true; @@ -1076,7 +1364,10 @@ export class FileSystem { */ private _resolve(path: string) { return this._cwd - ? pathUtil.resolvePaths(this._cwd, validate(path, ValidationFlags.RelativeOrAbsolute | ValidationFlags.AllowWildcard)) + ? pathUtil.resolvePaths( + this._cwd, + validate(path, ValidationFlags.RelativeOrAbsolute | ValidationFlags.AllowWildcard) + ) : validate(path, ValidationFlags.Absolute | ValidationFlags.AllowWildcard); } @@ -1343,7 +1634,8 @@ interface WalkResult { } function normalizeFileSetEntry(value: FileSet[string]) { - if (value === undefined || + if ( + value === undefined || value === null || value instanceof Directory || value instanceof File || @@ -1351,7 +1643,8 @@ function normalizeFileSetEntry(value: FileSet[string]) { value instanceof Symlink || value instanceof Mount || value instanceof Rmdir || - value instanceof Unlink) { + value instanceof Unlink + ) { return value; } return typeof value === 'string' || Buffer.isBuffer(value) ? new File(value) : new Directory(value); @@ -1369,22 +1662,22 @@ function formatPatchWorker(dirname: string, container: FileSet): string { const entry = normalizeFileSetEntry(container[name]); const file = dirname ? pathUtil.combinePaths(dirname, name) : name; if (entry === null || entry === undefined || entry instanceof Unlink || entry instanceof Rmdir) { - text += `//// [${ file }] unlink\r\n`; + text += `//// [${file}] unlink\r\n`; } else if (entry instanceof Rmdir) { - text += `//// [${ pathUtil.ensureTrailingDirectorySeparator(file) }] rmdir\r\n`; + text += `//// [${pathUtil.ensureTrailingDirectorySeparator(file)}] rmdir\r\n`; } else if (entry instanceof Directory) { text += formatPatchWorker(file, entry.files); } else if (entry instanceof SameFileContentFile) { - text += `//// [${ file }] file written with same contents\r\n`; + text += `//// [${file}] file written with same contents\r\n`; } else if (entry instanceof File) { const content = typeof entry.data === 'string' ? entry.data : entry.data.toString('utf8'); - text += `//// [${ file }]\r\n${ content }\r\n\r\n`; + text += `//// [${file}]\r\n${content}\r\n\r\n`; } else if (entry instanceof Link) { - text += `//// [${ file }] link(${ entry.path })\r\n`; + text += `//// [${file}] link(${entry.path})\r\n`; } else if (entry instanceof Symlink) { - text += `//// [${ file }] symlink(${ entry.symlink })\r\n`; + text += `//// [${file}] symlink(${entry.symlink})\r\n`; } else if (entry instanceof Mount) { - text += `//// [${ file }] mount(${ entry.source })\r\n`; + text += `//// [${file}] mount(${entry.source})\r\n`; } } return text; @@ -1411,11 +1704,34 @@ class Stats { birthtime: Date; constructor(); - constructor(dev: number, ino: number, mode: number, nlink: number, rdev: number, - size: number, blksize: number, blocks: number, atimeMs: number, mtimeMs: number, ctimeMs: number, birthtimeMs: number); - constructor(dev = 0, ino = 0, mode = 0, nlink = 0, rdev = 0, size = 0, - blksize = 0, blocks = 0, atimeMs = 0, mtimeMs = 0, ctimeMs = 0, birthtimeMs = 0) { - + constructor( + dev: number, + ino: number, + mode: number, + nlink: number, + rdev: number, + size: number, + blksize: number, + blocks: number, + atimeMs: number, + mtimeMs: number, + ctimeMs: number, + birthtimeMs: number + ); + constructor( + dev = 0, + ino = 0, + mode = 0, + nlink = 0, + rdev = 0, + size = 0, + blksize = 0, + blocks = 0, + atimeMs = 0, + mtimeMs = 0, + ctimeMs = 0, + birthtimeMs = 0 + ) { this.dev = dev; this.ino = ino; this.mode = mode; @@ -1436,11 +1752,25 @@ class Stats { this.birthtime = new Date(this.birthtimeMs); } - isFile() { return (this.mode & S_IFMT) === S_IFREG; } - isDirectory() { return (this.mode & S_IFMT) === S_IFDIR; } - isSymbolicLink() { return (this.mode & S_IFMT) === S_IFLNK; } - isBlockDevice() { return (this.mode & S_IFMT) === S_IFBLK; } - isCharacterDevice() { return (this.mode & S_IFMT) === S_IFCHR; } - isFIFO() { return (this.mode & S_IFMT) === S_IFIFO; } - isSocket() { return (this.mode & S_IFMT) === S_IFSOCK; } + isFile() { + return (this.mode & S_IFMT) === S_IFREG; + } + isDirectory() { + return (this.mode & S_IFMT) === S_IFDIR; + } + isSymbolicLink() { + return (this.mode & S_IFMT) === S_IFLNK; + } + isBlockDevice() { + return (this.mode & S_IFMT) === S_IFBLK; + } + isCharacterDevice() { + return (this.mode & S_IFMT) === S_IFCHR; + } + isFIFO() { + return (this.mode & S_IFMT) === S_IFIFO; + } + isSocket() { + return (this.mode & S_IFMT) === S_IFSOCK; + } } diff --git a/server/src/tests/harness/vfs/pathValidation.ts b/server/src/tests/harness/vfs/pathValidation.ts index fac869e54..cce6af901 100644 --- a/server/src/tests/harness/vfs/pathValidation.ts +++ b/server/src/tests/harness/vfs/pathValidation.ts @@ -37,10 +37,21 @@ export const enum ValidationFlags { Root = RequireRoot | AllowRoot | AllowTrailingSeparator, /** Path must be a absolute */ - Absolute = RequireRoot | AllowRoot | AllowDirname | AllowBasename | AllowExtname | AllowTrailingSeparator | AllowNavigation, + Absolute = RequireRoot | + AllowRoot | + AllowDirname | + AllowBasename | + AllowExtname | + AllowTrailingSeparator | + AllowNavigation, /** Path may be relative or absolute */ - RelativeOrAbsolute = AllowRoot | AllowDirname | AllowBasename | AllowExtname | AllowTrailingSeparator | AllowNavigation, + RelativeOrAbsolute = AllowRoot | + AllowDirname | + AllowBasename | + AllowExtname | + AllowTrailingSeparator | + AllowNavigation, /** Path may only be a filename */ Basename = RequireBasename | AllowExtname @@ -51,35 +62,74 @@ function validateComponents(components: string[], flags: ValidationFlags, hasTra const hasDirname = components.length > 2; const hasBasename = components.length > 1; const hasExtname = hasBasename && extRegExp.test(components[components.length - 1]); - const invalidComponentRegExp = flags & ValidationFlags.AllowNavigation - ? flags & ValidationFlags.AllowWildcard ? invalidNavigableComponentWithWildcardsRegExp : invalidNavigableComponentRegExp - : flags & ValidationFlags.AllowWildcard ? invalidNonNavigableComponentWithWildcardsRegExp : invalidNonNavigableComponentRegExp; + const invalidComponentRegExp = + flags & ValidationFlags.AllowNavigation + ? flags & ValidationFlags.AllowWildcard + ? invalidNavigableComponentWithWildcardsRegExp + : invalidNavigableComponentRegExp + : flags & ValidationFlags.AllowWildcard + ? invalidNonNavigableComponentWithWildcardsRegExp + : invalidNonNavigableComponentRegExp; // Validate required components - if (flags & ValidationFlags.RequireRoot && !hasRoot) { return false; } - if (flags & ValidationFlags.RequireDirname && !hasDirname) { return false; } - if (flags & ValidationFlags.RequireBasename && !hasBasename) { return false; } - if (flags & ValidationFlags.RequireExtname && !hasExtname) { return false; } - if (flags & ValidationFlags.RequireTrailingSeparator && !hasTrailingSeparator) { return false; } + if (flags & ValidationFlags.RequireRoot && !hasRoot) { + return false; + } + if (flags & ValidationFlags.RequireDirname && !hasDirname) { + return false; + } + if (flags & ValidationFlags.RequireBasename && !hasBasename) { + return false; + } + if (flags & ValidationFlags.RequireExtname && !hasExtname) { + return false; + } + if (flags & ValidationFlags.RequireTrailingSeparator && !hasTrailingSeparator) { + return false; + } // Required components indicate allowed components - if (flags & ValidationFlags.RequireRoot) { flags |= ValidationFlags.AllowRoot; } - if (flags & ValidationFlags.RequireDirname) { flags |= ValidationFlags.AllowDirname; } - if (flags & ValidationFlags.RequireBasename) { flags |= ValidationFlags.AllowBasename; } - if (flags & ValidationFlags.RequireExtname) { flags |= ValidationFlags.AllowExtname; } - if (flags & ValidationFlags.RequireTrailingSeparator) { flags |= ValidationFlags.AllowTrailingSeparator; } + if (flags & ValidationFlags.RequireRoot) { + flags |= ValidationFlags.AllowRoot; + } + if (flags & ValidationFlags.RequireDirname) { + flags |= ValidationFlags.AllowDirname; + } + if (flags & ValidationFlags.RequireBasename) { + flags |= ValidationFlags.AllowBasename; + } + if (flags & ValidationFlags.RequireExtname) { + flags |= ValidationFlags.AllowExtname; + } + if (flags & ValidationFlags.RequireTrailingSeparator) { + flags |= ValidationFlags.AllowTrailingSeparator; + } // Validate disallowed components - if (~flags & ValidationFlags.AllowRoot && hasRoot) { return false; } - if (~flags & ValidationFlags.AllowDirname && hasDirname) { return false; } - if (~flags & ValidationFlags.AllowBasename && hasBasename) { return false; } - if (~flags & ValidationFlags.AllowExtname && hasExtname) { return false; } - if (~flags & ValidationFlags.AllowTrailingSeparator && hasTrailingSeparator) { return false; } + if (~flags & ValidationFlags.AllowRoot && hasRoot) { + return false; + } + if (~flags & ValidationFlags.AllowDirname && hasDirname) { + return false; + } + if (~flags & ValidationFlags.AllowBasename && hasBasename) { + return false; + } + if (~flags & ValidationFlags.AllowExtname && hasExtname) { + return false; + } + if (~flags & ValidationFlags.AllowTrailingSeparator && hasTrailingSeparator) { + return false; + } // Validate component strings - if (invalidRootComponentRegExp.test(components[0])) { return false; } + if (invalidRootComponentRegExp.test(components[0])) { + return false; + } for (let i = 1; i < components.length; i++) { - if (invalidComponentRegExp.test(components[i])) { return false; } + if (invalidComponentRegExp.test(components[i])) { + return false; + } } return true; @@ -88,12 +138,17 @@ function validateComponents(components: string[], flags: ValidationFlags, hasTra export function validate(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) { const components = pu.getPathComponents(path); const trailing = pu.hasTrailingDirectorySeparator(path); - if (!validateComponents(components, flags, trailing)) { throw createIOError('ENOENT'); } - return components.length > 1 && trailing ? pu.combinePathComponents(pu.reducePathComponents(components)) + sep + if (!validateComponents(components, flags, trailing)) { + throw createIOError('ENOENT'); + } + return components.length > 1 && trailing + ? pu.combinePathComponents(pu.reducePathComponents(components)) + sep : pu.combinePathComponents(pu.reducePathComponents(components)); } function getInvalidRootComponentRegExp(): RegExp { const escapedSeparator = pu.getRegexEscapedSeparator(); - return new RegExp(`^(?!(${ escapedSeparator }|${ escapedSeparator }${ escapedSeparator }w+${ escapedSeparator }|[a-zA-Z]:${ escapedSeparator }?|)$)`); + return new RegExp( + `^(?!(${escapedSeparator}|${escapedSeparator}${escapedSeparator}w+${escapedSeparator}|[a-zA-Z]:${escapedSeparator}?|)$)` + ); } diff --git a/server/src/tests/pathUtils.test.ts b/server/src/tests/pathUtils.test.ts index b3992c974..fa65b18a5 100644 --- a/server/src/tests/pathUtils.test.ts +++ b/server/src/tests/pathUtils.test.ts @@ -12,13 +12,31 @@ import * as path from 'path'; import { Comparison } from '../common/core'; import { - changeAnyExtension, combinePathComponents, combinePaths, - comparePaths, comparePathsCaseInsensitive, - comparePathsCaseSensitive, containsPath, ensureTrailingDirectorySeparator, getAnyExtensionFromPath, - getBaseFileName, getFileExtension, getFileName, getPathComponents, getRegexEscapedSeparator, - getRelativePath, getRelativePathFromDirectory, getWildcardRegexPattern, getWildcardRoot, - hasTrailingDirectorySeparator, isRootedDiskPath, normalizeSlashes, reducePathComponents, - resolvePaths, stripFileExtension, stripTrailingDirectorySeparator + changeAnyExtension, + combinePathComponents, + combinePaths, + comparePaths, + comparePathsCaseInsensitive, + comparePathsCaseSensitive, + containsPath, + ensureTrailingDirectorySeparator, + getAnyExtensionFromPath, + getBaseFileName, + getFileExtension, + getFileName, + getPathComponents, + getRegexEscapedSeparator, + getRelativePath, + getRelativePathFromDirectory, + getWildcardRegexPattern, + getWildcardRoot, + hasTrailingDirectorySeparator, + isRootedDiskPath, + normalizeSlashes, + reducePathComponents, + resolvePaths, + stripFileExtension, + stripTrailingDirectorySeparator } from '../common/pathUtils'; test('getPathComponents1', () => { @@ -106,14 +124,14 @@ test('stripFileExtension', () => { test('getWildcardRegexPattern1', () => { const pattern = getWildcardRegexPattern('/users/me', './blah/'); const sep = getRegexEscapedSeparator(); - assert.equal(pattern, `${ sep }users${ sep }me${ sep }blah`); + assert.equal(pattern, `${sep}users${sep}me${sep}blah`); }); test('getWildcardRegexPattern2', () => { const pattern = getWildcardRegexPattern('/users/me', './**/*.py?/'); const sep = getRegexEscapedSeparator(); - assert.equal(pattern, `${ sep }users${ sep }me(${ sep }[^${ sep }.][^${ sep }]*)*?${ sep }[^${ sep }]*\\.py[^${ sep }]`); + assert.equal(pattern, `${sep}users${sep}me(${sep}[^${sep}.][^${sep}]*)*?${sep}[^${sep}]*\\.py[^${sep}]`); }); test('getWildcardRoot1', () => { @@ -269,5 +287,8 @@ test('isDiskPathRoot3', () => { }); test('getRelativePath', () => { - assert.equal(getRelativePath(normalizeSlashes('/a/b/c/d/e/f'), normalizeSlashes('/a/b/c')), normalizeSlashes('./d/e/f')); + assert.equal( + getRelativePath(normalizeSlashes('/a/b/c/d/e/f'), normalizeSlashes('/a/b/c')), + normalizeSlashes('./d/e/f') + ); }); diff --git a/server/src/tests/stringUtils.test.ts b/server/src/tests/stringUtils.test.ts index 0b0651f65..8ee9a3ad6 100644 --- a/server/src/tests/stringUtils.test.ts +++ b/server/src/tests/stringUtils.test.ts @@ -10,15 +10,15 @@ import * as core from '../common/core'; import * as utils from '../common/stringUtils'; test('CoreCompareStringsCaseInsensitive1', () => { - assert.equal(utils.compareStringsCaseInsensitive("Hello", "hello"), core.Comparison.EqualTo); + assert.equal(utils.compareStringsCaseInsensitive('Hello', 'hello'), core.Comparison.EqualTo); }); test('CoreCompareStringsCaseInsensitive2', () => { - assert.equal(utils.compareStringsCaseInsensitive("Hello", undefined), core.Comparison.GreaterThan); + assert.equal(utils.compareStringsCaseInsensitive('Hello', undefined), core.Comparison.GreaterThan); }); test('CoreCompareStringsCaseInsensitive3', () => { - assert.equal(utils.compareStringsCaseInsensitive(undefined, "hello"), core.Comparison.LessThan); + assert.equal(utils.compareStringsCaseInsensitive(undefined, 'hello'), core.Comparison.LessThan); }); test('CoreCompareStringsCaseInsensitive4', () => { @@ -26,5 +26,5 @@ test('CoreCompareStringsCaseInsensitive4', () => { }); test('CoreCompareStringsCaseSensitive', () => { - assert.equal(utils.compareStringsCaseSensitive("Hello", "hello"), core.Comparison.LessThan); + assert.equal(utils.compareStringsCaseSensitive('Hello', 'hello'), core.Comparison.LessThan); }); diff --git a/server/src/tests/testState.test.ts b/server/src/tests/testState.test.ts index d5e384c64..f51329c30 100644 --- a/server/src/tests/testState.test.ts +++ b/server/src/tests/testState.test.ts @@ -147,7 +147,7 @@ test('CustomTypeshedFolder', () => { // those virtual folder will mount to. const code = ` // global options -// @typeshed: ${ __dirname } +// @typeshed: ${__dirname} `; // mount the folder this file is in as typeshed folder and check whether @@ -232,8 +232,13 @@ test('Markers', () => { const marker1 = data.markerPositions.get('marker1'); assert.deepEqual(state.getMarkerName(marker1!), 'marker1'); - assert.deepEqual(state.getMarkers().map(m => state.getMarkerName(m)).sort( - compareStringsCaseSensitive), state.getMarkerNames().sort(comparePathsCaseSensitive)); + assert.deepEqual( + state + .getMarkers() + .map(m => state.getMarkerName(m)) + .sort(compareStringsCaseSensitive), + state.getMarkerNames().sort(comparePathsCaseSensitive) + ); }); test('GoToPosition', () => { @@ -371,7 +376,10 @@ test('getRangesInFile', () => { const { data, state } = parseAndGetTestState(code); - assert.deepEqual(state.getRangesInFile(data.files[0].fileName), data.ranges.filter(r => r.fileName === data.files[0].fileName)); + assert.deepEqual( + state.getRangesInFile(data.files[0].fileName), + data.ranges.filter(r => r.fileName === data.files[0].fileName) + ); }); test('rangesByText', () => { diff --git a/server/src/tests/testUtils.ts b/server/src/tests/testUtils.ts index d767868bd..4f2ddbb16 100644 --- a/server/src/tests/testUtils.ts +++ b/server/src/tests/testUtils.ts @@ -41,7 +41,7 @@ export interface FileParseResult { } export function resolveSampleFilePath(fileName: string): string { - return path.resolve(path.dirname(module.filename), `./samples/${ fileName }`); + return path.resolve(path.dirname(module.filename), `./samples/${fileName}`); } export function readSampleFile(fileName: string): string { @@ -50,21 +50,25 @@ export function readSampleFile(fileName: string): string { try { return fs.readFileSync(filePath, { encoding: 'utf8' }); } catch { - console.error(`Could not read file "${ fileName }"`); + console.error(`Could not read file "${fileName}"`); return ''; } } -export function parseText(textToParse: string, diagSink: DiagnosticSink, - parseOptions: ParseOptions = new ParseOptions()): ParseResults { - +export function parseText( + textToParse: string, + diagSink: DiagnosticSink, + parseOptions: ParseOptions = new ParseOptions() +): ParseResults { const parser = new Parser(); return parser.parseSourceFile(textToParse, parseOptions, diagSink); } -export function parseSampleFile(fileName: string, diagSink: DiagnosticSink, - execEnvironment = new ExecutionEnvironment('.')): FileParseResult { - +export function parseSampleFile( + fileName: string, + diagSink: DiagnosticSink, + execEnvironment = new ExecutionEnvironment('.') +): FileParseResult { const text = readSampleFile(fileName); const parseOptions = new ParseOptions(); if (fileName.endsWith('pyi')) { @@ -78,9 +82,12 @@ export function parseSampleFile(fileName: string, diagSink: DiagnosticSink, }; } -export function buildAnalyzerFileInfo(filePath: string, fileContents: string, - parseResults: ParseResults, configOptions: ConfigOptions): AnalyzerFileInfo { - +export function buildAnalyzerFileInfo( + filePath: string, + fileContents: string, + parseResults: ParseResults, + configOptions: ConfigOptions +): AnalyzerFileInfo { const analysisDiagnostics = new TextRangeDiagnosticSink(parseResults.tokenizerOutput.lines); const fileInfo: AnalyzerFileInfo = { @@ -102,16 +109,13 @@ export function buildAnalyzerFileInfo(filePath: string, fileContents: string, return fileInfo; } -export function bindSampleFile(fileName: string, - configOptions = new ConfigOptions('.')): FileAnalysisResult { - +export function bindSampleFile(fileName: string, configOptions = new ConfigOptions('.')): FileAnalysisResult { const diagSink = new DiagnosticSink(); const filePath = resolveSampleFilePath(fileName); const execEnvironment = configOptions.findExecEnvironment(filePath); const parseInfo = parseSampleFile(fileName, diagSink, execEnvironment); - const fileInfo = buildAnalyzerFileInfo(filePath, parseInfo.fileContents, - parseInfo.parseResults, configOptions); + const fileInfo = buildAnalyzerFileInfo(filePath, parseInfo.fileContents, parseInfo.parseResults, configOptions); const binder = new Binder(fileInfo); binder.bindModule(parseInfo.parseResults.parseTree); @@ -127,9 +131,10 @@ export function bindSampleFile(fileName: string, }; } -export function typeAnalyzeSampleFiles(fileNames: string[], - configOptions = new ConfigOptions('.')): FileAnalysisResult[] { - +export function typeAnalyzeSampleFiles( + fileNames: string[], + configOptions = new ConfigOptions('.') +): FileAnalysisResult[] { // Always enable "test mode". configOptions.internalTestMode = true; const importResolver = new ImportResolver(createFromRealFileSystem(), configOptions); @@ -155,7 +160,7 @@ export function typeAnalyzeSampleFiles(fileNames: string[], }; return analysisResult; } else { - fail(`Source file not found for ${ filePaths[index] }`); + fail(`Source file not found for ${filePaths[index]}`); const analysisResult: FileAnalysisResult = { filePath: '', @@ -170,16 +175,16 @@ export function typeAnalyzeSampleFiles(fileNames: string[], export function printDiagnostics(fileResults: FileAnalysisResult) { if (fileResults.errors.length > 0) { - console.error(`Errors in ${ fileResults.filePath }:`); + console.error(`Errors in ${fileResults.filePath}:`); for (const diag of fileResults.errors) { - console.error(` ${ diag.message }`); + console.error(` ${diag.message}`); } } if (fileResults.warnings.length > 0) { - console.error(`Warnings in ${ fileResults.filePath }:`); + console.error(`Warnings in ${fileResults.filePath}:`); for (const diag of fileResults.warnings) { - console.error(` ${ diag.message }`); + console.error(` ${diag.message}`); } } } diff --git a/server/src/tests/tokenizer.test.ts b/server/src/tests/tokenizer.test.ts index ac2ba0ce9..80a3206d9 100644 --- a/server/src/tests/tokenizer.test.ts +++ b/server/src/tests/tokenizer.test.ts @@ -14,9 +14,19 @@ import * as assert from 'assert'; import * as StringTokenUtils from '../parser/stringTokenUtils'; import { Tokenizer } from '../parser/tokenizer'; -import { DedentToken, IdentifierToken, IndentToken, NewLineToken, NewLineType, - NumberToken, OperatorToken, OperatorType, StringToken, - StringTokenFlags, TokenType } from '../parser/tokenizerTypes'; +import { + DedentToken, + IdentifierToken, + IndentToken, + NewLineToken, + NewLineType, + NumberToken, + OperatorToken, + OperatorType, + StringToken, + StringTokenFlags, + TokenType +} from '../parser/tokenizerTypes'; import * as TestUtils from './testUtils'; const _implicitTokenCount = 2; @@ -162,14 +172,7 @@ test('PunctuationTokens', () => { test('IndentDedent', () => { const t = new Tokenizer(); - const results = t.tokenize( - 'test\n' + - ' i1\n' + - ' i2 # \n' + - ' # \n' + - ' \ti3\n' + - '\ti4\n' + - ' i1'); + const results = t.tokenize('test\n' + ' i1\n' + ' i2 # \n' + ' # \n' + ' \ti3\n' + '\ti4\n' + ' i1'); assert.equal(results.tokens.count, 15 + _implicitTokenCount); assert.equal(results.tokens.getItemAt(0).type, TokenType.Identifier); @@ -233,7 +236,11 @@ test('Strings: unclosed', () => { const results = t.tokenize(' "string" """line1\n#line2"""\t\'un#closed'); assert.equal(results.tokens.count, 3 + _implicitTokenCount); - const ranges = [[1, 8], [10, 18], [29, 10]]; + const ranges = [ + [1, 8], + [10, 18], + [29, 10] + ]; for (let i = 0; i < ranges.length; i += 1) { assert.equal(results.tokens.getItemAt(i).start, ranges[i][0]); assert.equal(results.tokens.getItemAt(i).length, ranges[i][1]); @@ -246,7 +253,10 @@ test('Strings: escaped across multiple lines', () => { const results = t.tokenize(' "a\\\nb" \'c\\\r\nb\''); assert.equal(results.tokens.count, 2 + _implicitTokenCount); - const ranges = [[1, 6], [8, 7]]; + const ranges = [ + [1, 6], + [8, 7] + ]; for (let i = 0; i < ranges.length; i += 1) { assert.equal(results.tokens.getItemAt(i).start, ranges[i][0]); assert.equal(results.tokens.getItemAt(i).length, ranges[i][1]); @@ -259,7 +269,10 @@ test('Strings: block next to regular, double-quoted', () => { const results = t.tokenize('"string""""s2"""'); assert.equal(results.tokens.count, 2 + _implicitTokenCount); - const ranges = [[0, 8], [8, 8]]; + const ranges = [ + [0, 8], + [8, 8] + ]; for (let i = 0; i < ranges.length; i += 1) { assert.equal(results.tokens.getItemAt(i).start, ranges[i][0]); assert.equal(results.tokens.getItemAt(i).length, ranges[i][1]); @@ -272,7 +285,10 @@ test('Strings: block next to block, double-quoted', () => { const results = t.tokenize('""""""""'); assert.equal(results.tokens.count, 2 + _implicitTokenCount); - const ranges = [[0, 6], [6, 2]]; + const ranges = [ + [0, 6], + [6, 2] + ]; for (let i = 0; i < ranges.length; i += 1) { assert.equal(results.tokens.getItemAt(i).start, ranges[i][0]); assert.equal(results.tokens.getItemAt(i).length, ranges[i][1]); @@ -303,7 +319,7 @@ test('Strings: single quote escape', () => { assert.equal(stringToken.flags, StringTokenFlags.SingleQuote); assert.equal(stringToken.length, 12); assert.equal(stringToken.prefixLength, 0); - assert.equal(stringToken.escapedValue, '\\\'quoted\\\''); + assert.equal(stringToken.escapedValue, "\\'quoted\\'"); }); test('Strings: double quote escape', () => { @@ -325,8 +341,7 @@ test('Strings: triplicate double quote escape', () => { const stringToken = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken.type, TokenType.String); - assert.equal(stringToken.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Triplicate); + assert.equal(stringToken.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Triplicate); assert.equal(stringToken.length, 16); assert.equal(stringToken.escapedValue, '\\"quoted\\"'); }); @@ -369,8 +384,10 @@ test('Strings: single quoted multiline f-string', () => { const stringToken = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken.type, TokenType.String); - assert.equal(stringToken.flags, - StringTokenFlags.SingleQuote | StringTokenFlags.Triplicate | StringTokenFlags.Format); + assert.equal( + stringToken.flags, + StringTokenFlags.SingleQuote | StringTokenFlags.Triplicate | StringTokenFlags.Format + ); assert.equal(stringToken.length, 13); assert.equal(stringToken.escapedValue, 'quoted'); }); @@ -382,8 +399,10 @@ test('Strings: double quoted multiline f-string', () => { const stringToken = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken.type, TokenType.String); - assert.equal(stringToken.flags, - StringTokenFlags.DoubleQuote | StringTokenFlags.Triplicate | StringTokenFlags.Format); + assert.equal( + stringToken.flags, + StringTokenFlags.DoubleQuote | StringTokenFlags.Triplicate | StringTokenFlags.Format + ); assert.equal(stringToken.length, 14); assert.equal(stringToken.escapedValue, 'quoted '); }); @@ -401,8 +420,10 @@ test('Strings: f-string with single right brace', () => { assert.equal(unescapedValue.unescapeErrors.length, 1); assert.equal(unescapedValue.unescapeErrors[0].offset, 5); assert.equal(unescapedValue.unescapeErrors[0].length, 1); - assert.equal(unescapedValue.unescapeErrors[0].errorType, - StringTokenUtils.UnescapeErrorType.SingleCloseBraceWithinFormatLiteral); + assert.equal( + unescapedValue.unescapeErrors[0].errorType, + StringTokenUtils.UnescapeErrorType.SingleCloseBraceWithinFormatLiteral + ); }); test('Strings: f-string with escape in expression', () => { @@ -418,8 +439,10 @@ test('Strings: f-string with escape in expression', () => { assert.equal(unescapedValue.unescapeErrors.length, 1); assert.equal(unescapedValue.unescapeErrors[0].offset, 8); assert.equal(unescapedValue.unescapeErrors[0].length, 1); - assert.equal(unescapedValue.unescapeErrors[0].errorType, - StringTokenUtils.UnescapeErrorType.EscapeWithinFormatExpression); + assert.equal( + unescapedValue.unescapeErrors[0].errorType, + StringTokenUtils.UnescapeErrorType.EscapeWithinFormatExpression + ); }); test('Strings: f-string with unterminated expression', () => { @@ -434,8 +457,10 @@ test('Strings: f-string with unterminated expression', () => { assert.equal(unescapedValue.formatStringSegments.length, 2); assert.equal(unescapedValue.unescapeErrors.length, 1); assert.equal(unescapedValue.unescapeErrors[0].offset, 7); - assert.equal(unescapedValue.unescapeErrors[0].errorType, - StringTokenUtils.UnescapeErrorType.UnterminatedFormatExpression); + assert.equal( + unescapedValue.unescapeErrors[0].errorType, + StringTokenUtils.UnescapeErrorType.UnterminatedFormatExpression + ); }); test('Strings: escape at the end of single quoted string', () => { @@ -445,10 +470,9 @@ test('Strings: escape at the end of single quoted string', () => { const stringToken = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken.type, TokenType.String); - assert.equal(stringToken.flags, - StringTokenFlags.SingleQuote | StringTokenFlags.Unterminated); + assert.equal(stringToken.flags, StringTokenFlags.SingleQuote | StringTokenFlags.Unterminated); assert.equal(stringToken.length, 9); - assert.equal(stringToken.escapedValue, 'quoted\\\''); + assert.equal(stringToken.escapedValue, "quoted\\'"); assert.equal(results.tokens.getItemAt(1).type, TokenType.NewLine); assert.equal(results.tokens.getItemAt(2).type, TokenType.Identifier); @@ -461,8 +485,7 @@ test('Strings: escape at the end of double quoted string', () => { const stringToken = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken.type, TokenType.String); - assert.equal(stringToken.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Unterminated); + assert.equal(stringToken.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Unterminated); assert.equal(stringToken.length, 9); assert.equal(stringToken.escapedValue, 'quoted\\"'); @@ -477,32 +500,28 @@ test('Strings: b/u/r-string', () => { const stringToken0 = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken0.type, TokenType.String); - assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Bytes); + assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Bytes); assert.equal(stringToken0.length, 4); assert.equal(stringToken0.escapedValue, 'b'); assert.equal(stringToken0.prefixLength, 1); const stringToken1 = results.tokens.getItemAt(1) as StringToken; assert.equal(stringToken1.type, TokenType.String); - assert.equal(stringToken1.flags, - StringTokenFlags.SingleQuote | StringTokenFlags.Unicode); + assert.equal(stringToken1.flags, StringTokenFlags.SingleQuote | StringTokenFlags.Unicode); assert.equal(stringToken1.length, 4); assert.equal(stringToken1.escapedValue, 'u'); assert.equal(stringToken1.prefixLength, 1); const stringToken2 = results.tokens.getItemAt(2) as StringToken; assert.equal(stringToken2.type, TokenType.String); - assert.equal(stringToken2.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Bytes | StringTokenFlags.Raw); + assert.equal(stringToken2.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Bytes | StringTokenFlags.Raw); assert.equal(stringToken2.length, 6); assert.equal(stringToken2.escapedValue, 'br'); assert.equal(stringToken2.prefixLength, 2); const stringToken3 = results.tokens.getItemAt(3) as StringToken; assert.equal(stringToken3.type, TokenType.String); - assert.equal(stringToken3.flags, StringTokenFlags.SingleQuote | - StringTokenFlags.Unicode | StringTokenFlags.Raw); + assert.equal(stringToken3.flags, StringTokenFlags.SingleQuote | StringTokenFlags.Unicode | StringTokenFlags.Raw); assert.equal(stringToken3.length, 6); assert.equal(stringToken3.escapedValue, 'ur'); assert.equal(stringToken3.prefixLength, 2); @@ -510,24 +529,23 @@ test('Strings: b/u/r-string', () => { test('Strings: bytes string with non-ASCII', () => { const t = new Tokenizer(); - const results = t.tokenize('B"Teßt" b\'\'\'Teñt\'\'\''); + const results = t.tokenize("B\"Teßt\" b'''Teñt'''"); assert.equal(results.tokens.count, 2 + _implicitTokenCount); const stringToken0 = results.tokens.getItemAt(0) as StringToken; - const unescapedValue0 = StringTokenUtils.getUnescapedString( - stringToken0); + const unescapedValue0 = StringTokenUtils.getUnescapedString(stringToken0); assert.equal(stringToken0.type, TokenType.String); - assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Bytes); + assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Bytes); assert.equal(unescapedValue0.nonAsciiInBytes, true); assert.equal(stringToken0.length, 7); const stringToken1 = results.tokens.getItemAt(1) as StringToken; - const unescapedValue1 = StringTokenUtils.getUnescapedString( - stringToken1); + const unescapedValue1 = StringTokenUtils.getUnescapedString(stringToken1); assert.equal(stringToken1.type, TokenType.String); - assert.equal(stringToken1.flags, StringTokenFlags.SingleQuote | - StringTokenFlags.Bytes | StringTokenFlags.Triplicate); + assert.equal( + stringToken1.flags, + StringTokenFlags.SingleQuote | StringTokenFlags.Bytes | StringTokenFlags.Triplicate + ); assert.equal(unescapedValue1.nonAsciiInBytes, true); assert.equal(stringToken1.length, 11); }); @@ -540,8 +558,7 @@ test('Strings: raw strings with escapes', () => { const stringToken0 = results.tokens.getItemAt(0) as StringToken; const unescapedValue0 = StringTokenUtils.getUnescapedString(stringToken0); assert.equal(stringToken0.type, TokenType.String); - assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Raw); + assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Raw); assert.equal(stringToken0.length, 5); assert.equal(stringToken0.escapedValue, '\\"'); assert.equal(unescapedValue0.value, '\\"'); @@ -549,8 +566,7 @@ test('Strings: raw strings with escapes', () => { const stringToken1 = results.tokens.getItemAt(1) as StringToken; const unescapedValue1 = StringTokenUtils.getUnescapedString(stringToken1); assert.equal(stringToken1.type, TokenType.String); - assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Raw); + assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Raw); assert.equal(stringToken1.length, 10); assert.equal(stringToken1.escapedValue, '\\\r\n\\\n\\a'); assert.equal(unescapedValue1.value, '\\\r\n\\\n\\a'); @@ -563,8 +579,7 @@ test('Strings: escape at the end of double quoted string', () => { const stringToken = results.tokens.getItemAt(0) as StringToken; assert.equal(stringToken.type, TokenType.String); - assert.equal(stringToken.flags, StringTokenFlags.DoubleQuote | - StringTokenFlags.Unterminated); + assert.equal(stringToken.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.Unterminated); assert.equal(stringToken.length, 9); assert.equal(stringToken.escapedValue, 'quoted\\"'); @@ -599,12 +614,10 @@ test('Strings: invalid escape characters', () => { assert.equal(unescapedValue.unescapeErrors.length, 2); assert.equal(unescapedValue.unescapeErrors[0].offset, 0); assert.equal(unescapedValue.unescapeErrors[0].length, 2); - assert.equal(unescapedValue.unescapeErrors[0].errorType, - StringTokenUtils.UnescapeErrorType.InvalidEscapeSequence); + assert.equal(unescapedValue.unescapeErrors[0].errorType, StringTokenUtils.UnescapeErrorType.InvalidEscapeSequence); assert.equal(unescapedValue.unescapeErrors[1].offset, 4); assert.equal(unescapedValue.unescapeErrors[1].length, 2); - assert.equal(unescapedValue.unescapeErrors[1].errorType, - StringTokenUtils.UnescapeErrorType.InvalidEscapeSequence); + assert.equal(unescapedValue.unescapeErrors[1].errorType, StringTokenUtils.UnescapeErrorType.InvalidEscapeSequence); }); test('Strings: good hex escapes', () => { @@ -643,8 +656,7 @@ test('Strings: bad hex escapes', () => { assert.equal(results.tokens.count, 3 + _implicitTokenCount); const stringToken0 = results.tokens.getItemAt(0) as StringToken; - const unescapedValue0 = StringTokenUtils.getUnescapedString( - stringToken0); + const unescapedValue0 = StringTokenUtils.getUnescapedString(stringToken0); assert.equal(stringToken0.type, TokenType.String); assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote); assert.equal(unescapedValue0.unescapeErrors.length, 1); @@ -652,8 +664,7 @@ test('Strings: bad hex escapes', () => { assert.equal(unescapedValue0.value, '\\x4g'); const stringToken1 = results.tokens.getItemAt(1) as StringToken; - const unescapedValue1 = StringTokenUtils.getUnescapedString( - stringToken1); + const unescapedValue1 = StringTokenUtils.getUnescapedString(stringToken1); assert.equal(stringToken1.type, TokenType.String); assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote); assert.equal(unescapedValue1.unescapeErrors.length, 1); @@ -661,8 +672,7 @@ test('Strings: bad hex escapes', () => { assert.equal(unescapedValue1.value, '\\u006'); const stringToken2 = results.tokens.getItemAt(2) as StringToken; - const unescapedValue2 = StringTokenUtils.getUnescapedString( - stringToken2); + const unescapedValue2 = StringTokenUtils.getUnescapedString(stringToken2); assert.equal(stringToken2.type, TokenType.String); assert.equal(stringToken2.flags, StringTokenFlags.DoubleQuote); assert.equal(unescapedValue2.unescapeErrors.length, 1); @@ -676,8 +686,7 @@ test('Strings: good name escapes', () => { assert.equal(results.tokens.count, 2 + _implicitTokenCount); const stringToken0 = results.tokens.getItemAt(0) as StringToken; - const unescapedValue0 = StringTokenUtils.getUnescapedString( - stringToken0); + const unescapedValue0 = StringTokenUtils.getUnescapedString(stringToken0); assert.equal(stringToken0.type, TokenType.String); assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote); assert.equal(stringToken0.length, 11); @@ -685,8 +694,7 @@ test('Strings: good name escapes', () => { assert.equal(unescapedValue0.value, '-'); const stringToken1 = results.tokens.getItemAt(1) as StringToken; - const unescapedValue1 = StringTokenUtils.getUnescapedString( - stringToken1); + const unescapedValue1 = StringTokenUtils.getUnescapedString(stringToken1); assert.equal(stringToken1.type, TokenType.String); assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote); assert.equal(stringToken1.length, 10); @@ -770,7 +778,7 @@ test('Hex number', () => { assert.equal(results.tokens.getItemAt(2).type, TokenType.Number); assert.equal(results.tokens.getItemAt(2).length, 7); - assert.equal((results.tokens.getItemAt(2) as NumberToken).value, 0xFEAB); + assert.equal((results.tokens.getItemAt(2) as NumberToken).value, 0xfeab); assert.equal((results.tokens.getItemAt(2) as NumberToken).isInteger, true); assert.equal(results.tokens.getItemAt(3).type, TokenType.Number); @@ -1042,7 +1050,8 @@ test('Simple expression, leading minus', () => { }); test('Operators', () => { - const text = '< << <<= ' + + const text = + '< << <<= ' + '== != > >> >>= >= <=' + '+ - ~ %' + '* ** / // /= //=' + @@ -1050,14 +1059,7 @@ test('Operators', () => { '& &= | |= ^ ^= ' + ':='; const results = new Tokenizer().tokenize(text); - const lengths = [ - 1, 2, 3, - 2, 2, 1, 2, 3, 2, 2, - 1, 1, 1, 1, - 1, 2, 1, 2, 2, 3, - 2, 2, 2, 2, 3, - 1, 2, 1, 2, 1, 2, - 2]; + const lengths = [1, 2, 3, 2, 2, 1, 2, 3, 2, 2, 1, 1, 1, 1, 1, 2, 1, 2, 2, 3, 2, 2, 2, 2, 3, 1, 2, 1, 2, 1, 2, 2]; const operatorTypes = [ OperatorType.LessThan, OperatorType.LeftShift, @@ -1090,15 +1092,19 @@ test('Operators', () => { OperatorType.BitwiseOrEqual, OperatorType.BitwiseXor, OperatorType.BitwiseXorEqual, - OperatorType.Walrus]; + OperatorType.Walrus + ]; assert.equal(results.tokens.count - _implicitTokenCount, lengths.length); assert.equal(results.tokens.count - _implicitTokenCount, operatorTypes.length); for (let i = 0; i < lengths.length; i += 1) { const t = results.tokens.getItemAt(i); - assert.equal(t.type, TokenType.Operator, `${ t.type } at ${ i } is not an operator`); + assert.equal(t.type, TokenType.Operator, `${t.type} at ${i} is not an operator`); assert.equal((t as OperatorToken).operatorType, operatorTypes[i]); - assert.equal(t.length, lengths[i], - `Length ${ t.length } at ${ i } (text ${ text.substr(t.start, t.length) }), expected ${ lengths[i] }`); + assert.equal( + t.length, + lengths[i], + `Length ${t.length} at ${i} (text ${text.substr(t.start, t.length)}), expected ${lengths[i]}` + ); } }); @@ -1184,31 +1190,31 @@ test('Identifiers1', () => { assert.equal(token4.type, TokenType.Identifier); }); -test ('TypeIgnoreAll1', () => { +test('TypeIgnoreAll1', () => { const t = new Tokenizer(); const results = t.tokenize('\n#type:ignore\n"test"'); assert.equal(results.typeIgnoreAll, true); }); -test ('TypeIgnoreAll2', () => { +test('TypeIgnoreAll2', () => { const t = new Tokenizer(); const results = t.tokenize('\n# type: ignore ssss\n'); assert.equal(results.typeIgnoreAll, true); }); -test ('TypeIgnoreAll3', () => { +test('TypeIgnoreAll3', () => { const t = new Tokenizer(); const results = t.tokenize('\n# type: ignoressss\n'); assert.equal(results.typeIgnoreAll, false); }); -test ('TypeIgnoreAll3', () => { +test('TypeIgnoreAll3', () => { const t = new Tokenizer(); const results = t.tokenize('\n"hello"\n# type: ignore\n'); assert.equal(results.typeIgnoreAll, false); }); -test ('TypeIgnoreLine1', () => { +test('TypeIgnoreLine1', () => { const t = new Tokenizer(); const results = t.tokenize('\na = 3 # type: ignore\n"test" # type:ignore'); assert.equal(Object.keys(results.typeIgnoreLines).length, 2); @@ -1216,7 +1222,7 @@ test ('TypeIgnoreLine1', () => { assert.equal(results.typeIgnoreLines[2], true); }); -test ('TypeIgnoreLine2', () => { +test('TypeIgnoreLine2', () => { const t = new Tokenizer(); const results = t.tokenize('a = 3 # type: ignores\n"test" # type:ignore'); assert.equal(Object.keys(results.typeIgnoreLines).length, 1); diff --git a/server/webpack.config-cli.js b/server/webpack.config-cli.js index 0b9e7ea0d..c1c48e866 100644 --- a/server/webpack.config-cli.js +++ b/server/webpack.config-cli.js @@ -1,10 +1,12 @@ /** -* webpack.config-pyright.js -* Copyright: Microsoft 2018 -* -* Configuration for webpack to bundle the javascript into a single file -* for the pyright command-line tool. -*/ + * webpack.config-pyright.js + * Copyright: Microsoft 2018 + * + * Configuration for webpack to bundle the javascript into a single file + * for the pyright command-line tool. + */ + +/* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); @@ -17,10 +19,7 @@ module.exports = { path: path.resolve(__dirname, '../dist') }, resolve: { - modules: [ - path.resolve(__dirname, '.'), - 'node_modules' - ], + modules: [path.resolve(__dirname, '.'), 'node_modules'], extensions: ['.js', '.ts'] }, diff --git a/server/webpack.config-server.js b/server/webpack.config-server.js index c8366b827..d34a16ea5 100644 --- a/server/webpack.config-server.js +++ b/server/webpack.config-server.js @@ -1,10 +1,10 @@ /** -* webpack.config-server.js -* Copyright: Microsoft 2018 -* -* Configuration for webpack to bundle the javascript into a single file -* for the VS Code Extension language server. -*/ + * webpack.config-server.js + * Copyright: Microsoft 2018 + * + * Configuration for webpack to bundle the javascript into a single file + * for the VS Code Extension language server. + */ const path = require('path'); // eslint-disable-line @typescript-eslint/no-var-requires @@ -19,13 +19,10 @@ module.exports = { path: path.resolve(__dirname, '../client/server') }, optimization: { - usedExports: true, + usedExports: true }, resolve: { - modules: [ - path.resolve(__dirname, '.'), - 'node_modules' - ], + modules: [path.resolve(__dirname, '.'), 'node_modules'], extensions: ['.js', '.ts'] }, module: {