Mass reformat ts/js/md with ESLint quick fixes and prettier (#113)

This commit is contained in:
Jake Bailey 2020-02-24 16:52:23 -08:00 committed by GitHub
parent e17977c928
commit 8d2cca7289
143 changed files with 11201 additions and 6061 deletions

View File

@ -1 +1,4 @@
**/dist/**
**/out/**
**/node_modules/**
**/tests/fourslash/**

View File

@ -1,6 +1,7 @@
{
"extends": [
"eslint:recommended",
"prettier",
"plugin:@typescript-eslint/recommended"
],
"env": {

6
.prettierignore Normal file
View File

@ -0,0 +1,6 @@
*.json
**/dist/**
**/out/**
**/client/server/**
**/typeshed-fallback/**
**/.github/**

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"singleQuote": true,
"tabWidth": 4,
"useTabs": false,
"printWidth": 120
}

6
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

17
.vscode/settings.json vendored Normal file
View File

@ -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
}
}

View File

@ -3,53 +3,63 @@
# 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 symbols 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).
To install pyright globally:
@ -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 Microsofts 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.

View File

@ -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 symbols 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.

View File

@ -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",

View File

@ -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": {

View File

@ -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": {

View File

@ -20,7 +20,7 @@ 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"] };
const debugOptions = { execArgv: ['--nolazy', '--inspect=6600'] };
// If the extension is launched in debug mode, then the debug server options are used.
const serverOptions: ServerOptions = {
@ -28,22 +28,27 @@ export function activate(context: ExtensionContext) {
// 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 }
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: [{
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);
@ -60,18 +65,25 @@ export function activate(context: ExtensionContext) {
// Register our custom commands.
const textEditorCommands = [Commands.orderImports, Commands.addMissingOptionalToParam];
textEditorCommands.forEach(commandName => {
context.subscriptions.push(commands.registerTextEditorCommand(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) => {
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 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);
@ -82,14 +94,18 @@ export function activate(context: ExtensionContext) {
},
() => {
// Error received. For now, do nothing.
}));
}
)
);
});
const genericCommands = [ Commands.createTypeStub, ];
const genericCommands = [Commands.createTypeStub];
genericCommands.forEach(command => {
context.subscriptions.push(commands.registerCommand(command, (...args: any[]) => {
context.subscriptions.push(
commands.registerCommand(command, (...args: any[]) => {
languageClient.sendRequest('workspace/executeCommand', { command, arguments: args });
}));
})
);
});
}
@ -99,4 +115,3 @@ export function deactivate() {
// anything to do here.
return undefined;
}

View File

@ -25,13 +25,16 @@ export class ProgressReporting implements Disposable {
this._resolveProgress = resolve;
});
window.withProgress({
window.withProgress(
{
location: ProgressLocation.Window,
title: ''
}, progress => {
},
progress => {
this._progress = progress;
return progressPromise;
});
}
);
this._primeTimeoutTimer();
});

View File

@ -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.

View File

@ -3,7 +3,7 @@
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 |
@ -17,20 +17,17 @@ Pyright can be run as either a VS Code extension or as a node-based command-line
| --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 developers 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 |

View File

@ -1,8 +1,9 @@
# VS Code Commands
Pyright offers the following commands, which can be invoked from VS Codes “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.

View File

@ -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
```

View File

@ -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 files 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 environments 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 pyrights 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 dictionarys 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 pyrights 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 pyrights 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,34 +124,25 @@ 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"
],
"include": ["src"],
"exclude": [
"**/node_modules",
"**/__pycache__",
"src/experimental",
"src/web/node_modules",
"src/typestubs"
],
"exclude": ["**/node_modules", "**/__pycache__", "src/experimental", "src/web/node_modules", "src/typestubs"],
"ignore": [
"src/oldstuff"
],
"ignore": ["src/oldstuff"],
"typingsPath": "src/typestubs",
"venvPath": "/home/foo/.venvs",
@ -168,24 +159,17 @@ The following is an example of a pyright config file:
"root": "src/web",
"pythonVersion": "3.5",
"pythonPlatform": "Windows",
"extraPaths": [
"src/service_libs"
]
"extraPaths": ["src/service_libs"]
},
{
"root": "src/sdk",
"pythonVersion": "3.0",
"extraPaths": [
"src/backend"
],
"extraPaths": ["src/backend"],
"venv": "venv_bar"
},
{
"root": "src/tests",
"extraPaths": [
"src/tests/e2e",
"src/sdk"
]
"extraPaths": ["src/tests/e2e", "src/sdk"]
},
{
"root": "src"

View File

@ -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 projects 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.

View File

@ -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 interpreters 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.

View File

@ -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 files 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 theres a logic error in the code because theres 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 functions 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.
```
def add_values(a, b):
return a + b

View File

@ -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.

View File

@ -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 modules 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 Pythons 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 isnt.
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 youre serious about static type checking for your Python source base, its 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 modules 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 dont 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]: ...
```

View File

@ -3,4 +3,4 @@
// Stash the base directory into a global variable.
global.__rootDirectory = __dirname + '/dist/';
require('./client/server/pyright')
require('./client/server/pyright');

View File

@ -3,4 +3,4 @@
// Stash the base directory into a global variable.
global.__rootDirectory = __dirname + '/dist/';
require('./dist/pyright')
require('./dist/pyright');

85
package-lock.json generated
View File

@ -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": {

View File

@ -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": {

View File

@ -1 +1,2 @@
**/client/server/**
**/tests/fourslash/**

View File

@ -1,4 +0,0 @@
{
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true,
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true
}

View File

@ -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');

View File

@ -1,55 +1,68 @@
#!/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)
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.');
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);
}
packageFile = path.resolve(packageFile);
if (!fs.existsSync(packageFile)) {
console.error('Package file ' + packageFile + ' doesn\'t exist on disk.');
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);
}
tsconfigFile = path.resolve(tsconfigFile);
if (!fs.existsSync(tsconfigFile)) {
console.error('tsconfig file ' + tsconfigFile + ' doesn\'t exist on disk.')
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;
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, '/'));
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);
}
}
@ -58,18 +71,18 @@ if (!fs.existsSync(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...');
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');

View File

@ -5,29 +5,21 @@
*/
module.exports = {
roots: [
'<rootDir>/src/tests'
],
roots: ['<rootDir>/src/tests'],
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx'
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
globals: {
'ts-jest': {
tsConfig: {
"target": "es6",
target: 'es6',
// Needed because jest calls tsc in a way that doesn't
// inline const enums.
"preserveConstEnums": true
preserveConstEnums: true
}
}
}
}
};

228
server/package-lock.json generated
View File

@ -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",

View File

@ -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"

View File

@ -10,8 +10,15 @@
* (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<string, string>;
}
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).

View File

@ -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,9 +203,10 @@ 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);
// Bind implicit names.
@ -169,7 +226,8 @@ export class Binder extends ParseTreeWalker {
this._walkStatementsAndReportUnreachable(node.statements);
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);
@ -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
@ -498,7 +565,8 @@ export class Binder extends ParseTreeWalker {
this._addError(
`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);
}
});
}
@ -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 &&
if (
listEntryNode.nodeType === ParseNodeType.StringList &&
listEntryNode.strings.length === 1 &&
listEntryNode.strings[0].nodeType === ParseNodeType.String) {
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,18 +1727,20 @@ export class Binder extends ParseTreeWalker {
}
case ParseNodeType.Call: {
if (expression.leftExpression.nodeType === ParseNodeType.Name &&
if (
expression.leftExpression.nodeType === ParseNodeType.Name &&
(expression.leftExpression.value === 'isinstance' ||
expression.leftExpression.value === 'issubclass') &&
expression.arguments.length === 2) {
expression.arguments.length === 2
) {
return this._isNarrowingExpression(expression.arguments[0].valueExpression, expressionList);
}
if (expression.leftExpression.nodeType === ParseNodeType.Name &&
if (
expression.leftExpression.nodeType === ParseNodeType.Name &&
expression.leftExpression.value === 'callable' &&
expression.arguments.length === 1) {
expression.arguments.length === 1
) {
return this._isNarrowingExpression(expression.arguments[0].valueExpression, expressionList);
}
}
@ -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) {

View File

@ -17,13 +17,48 @@ 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;
@ -114,7 +168,8 @@ export class Checker extends ParseTreeWalker {
this._fileInfo.diagnosticSettings.reportUnknownParameterType,
DiagnosticRule.reportUnknownParameterType,
`Type of parameter '${param.name.value}' is unknown`,
param.name);
param.name
);
} else if (containsUnknown(paramType)) {
const diagAddendum = new DiagnosticAddendum();
diagAddendum.addMessage(`Parameter type is '${this._evaluator.printType(paramType)}'`);
@ -122,7 +177,8 @@ export class Checker extends ParseTreeWalker {
this._fileInfo.diagnosticSettings.reportUnknownParameterType,
DiagnosticRule.reportUnknownParameterType,
`Type of parameter '${param.name.value}' is partially unknown` + diagAddendum.getString(),
param.name);
param.name
);
}
}
});
@ -181,14 +237,16 @@ export class Checker extends ParseTreeWalker {
this._fileInfo.diagnosticSettings.reportUnknownLambdaType,
DiagnosticRule.reportUnknownLambdaType,
`Type of '${param.name.value}' is unknown`,
param.name);
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);
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);
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();
@ -291,7 +354,8 @@ export class Checker extends ParseTreeWalker {
`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!);
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,8 +827,7 @@ 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);
@ -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);
@ -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,10 +946,11 @@ 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;
}
}
@ -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) {
@ -1069,14 +1147,16 @@ export class Checker extends ParseTreeWalker {
DiagnosticRule.reportUnnecessaryIsInstance,
`Unnecessary ${callName} call: '${this._evaluator.printType(arg0Type)}' ` +
`is never ${callType} of '${this._evaluator.printType(getTestType())}'`,
node);
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);
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 &&
if (
primaryDeclaration.node &&
primaryDeclaration.node.parent &&
primaryDeclaration.node.parent === classOrModuleNode &&
classOrModuleNode.nodeType === ParseNodeType.Class) {
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;
}
}
@ -1186,16 +1270,17 @@ export class Checker extends ParseTreeWalker {
this._fileInfo.diagnosticSettings.reportPrivateUsage,
DiagnosticRule.reportPrivateUsage,
`'${nameValue}' is protected and used outside of a derived class`,
node);
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);
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 &&
if (
substatement.nodeType !== ParseNodeType.TypeAnnotation &&
substatement.nodeType !== ParseNodeType.Ellipsis &&
substatement.nodeType !== ParseNodeType.StringList &&
substatement.nodeType !== ParseNodeType.Pass) {
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
@ -1288,8 +1378,10 @@ export class Checker extends ParseTreeWalker {
if (!ParseTreeUtils.isSuiteEmpty(node.suite)) {
this._evaluator.addError(
`Function with declared type of '${this._evaluator.printType(declaredReturnType)}'` +
` must return value` + diagAddendum.getString(),
node.returnTypeAnnotation);
` 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);
node.name
);
}
}
}
@ -1321,7 +1416,8 @@ export class Checker extends ParseTreeWalker {
this._evaluator.addError(
`'${name}' cannot be redeclared because parent class ` +
`'${parentSymbol.class.details.name}' declares it as Final`,
decl.node);
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);
`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);
}
@ -1374,10 +1471,10 @@ export class Checker extends ParseTreeWalker {
const diag = this._evaluator.addError(
`Method '${name}' cannot override final method defined ` +
`in class '${baseClassAndSymbol.class.details.name}'`,
decl.node.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);
`to yield type '${this._evaluator.printType(declaredYieldType)}'` +
diagAddendum.getString(),
node.expression || node
);
}
}
}
@ -1537,7 +1646,8 @@ export class Checker extends ParseTreeWalker {
this._fileInfo.diagnosticSettings.reportDuplicateImport,
DiagnosticRule.reportDuplicateImport,
`'${importFromAs.name.value}' is imported more than once`,
importFromAs.name);
importFromAs.name
);
} else {
symbolMap.set(importFromAs.name.value, importFromAs);
}
@ -1552,7 +1662,8 @@ export class Checker extends ParseTreeWalker {
this._fileInfo.diagnosticSettings.reportDuplicateImport,
DiagnosticRule.reportDuplicateImport,
`'${importStatement.moduleName}' is imported more than once`,
importStatement.subnode);
importStatement.subnode
);
} else {
importModuleMap.set(importStatement.moduleName, importStatement.subnode);
}

View File

@ -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));
}
}

View File

@ -14,8 +14,14 @@
*/
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

View File

@ -8,14 +8,22 @@
* 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<Token>,
defaultSettings: DiagnosticSettings, useStrict: boolean): DiagnosticSettings {
export function getFileLevelDirectives(
tokens: TextRangeCollection<Token>,
defaultSettings: DiagnosticSettings,
useStrict: boolean
): DiagnosticSettings {
let settings = cloneDiagnosticSettings(defaultSettings);
if (useStrict) {

View File

@ -10,10 +10,23 @@
*/
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<string, ModuleLoaderActions>;
}
export type Declaration = IntrinsicDeclaration | ClassDeclaration |
SpecialBuiltInClassDeclaration | FunctionDeclaration | ParameterDeclaration |
VariableDeclaration | AliasDeclaration;
export type Declaration =
| IntrinsicDeclaration
| ClassDeclaration
| SpecialBuiltInClassDeclaration
| FunctionDeclaration
| ParameterDeclaration
| VariableDeclaration
| AliasDeclaration;

View File

@ -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;
}

View File

@ -10,9 +10,16 @@
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,47 +86,53 @@ 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);
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;
}
}
@ -122,24 +141,36 @@ export class ImportResolver {
if (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.
@ -150,21 +181,33 @@ export class ImportResolver {
// use tricks to populate their package namespaces.
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<string, ImportResult>();
@ -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,23 +463,32 @@ export class ImportResolver {
return parts.join('.');
}
private _getPythonSearchPaths(execEnv: ExecutionEnvironment,
importFailureInfo: string[]) {
private _getPythonSearchPaths(execEnv: ExecutionEnvironment, importFailureInfo: string[]) {
const cacheKey = execEnv.venv ? execEnv.venv : '<default>';
// 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 {
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);
@ -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,10 +620,12 @@ 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.
@ -554,8 +639,7 @@ export class ImportResolver {
}
// 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,16 +663,20 @@ 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 {
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
@ -611,7 +701,11 @@ export class ImportResolver {
} else if (this.fileSystem.existsSync(pyFilePath) && isFile(this.fileSystem, pyFilePath)) {
importFailureInfo.push(`Resolved import with file '${pyFilePath}'`);
resolvedPaths.push(pyFilePath);
} else if (allowPydFile && this.fileSystem.existsSync(pydFilePath) && isFile(this.fileSystem, pydFilePath)) {
} else if (
allowPydFile &&
this.fileSystem.existsSync(pydFilePath) &&
isFile(this.fileSystem, pydFilePath)
) {
importFailureInfo.push(`Resolved import with file '${pydFilePath}'`);
resolvedPaths.push(pydFilePath);
isPydFile = 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;
}
@ -692,7 +787,11 @@ export class ImportResolver {
} else if (this.fileSystem.existsSync(pyFilePath) && isFile(this.fileSystem, pyFilePath)) {
importFailureInfo.push(`Resolved import with file '${pyFilePath}'`);
resolvedPaths.push(pyFilePath);
} else if (allowPydFile && this.fileSystem.existsSync(pydFilePath) && isFile(this.fileSystem, pydFilePath)) {
} else if (
allowPydFile &&
this.fileSystem.existsSync(pydFilePath) &&
isFile(this.fileSystem, pydFilePath)
) {
importFailureInfo.push(`Resolved import with file '${pydFilePath}'`);
resolvedPaths.push(pydFilePath);
if (isLastPart) {
@ -706,8 +805,9 @@ export class ImportResolver {
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);
}
}

View File

@ -12,8 +12,15 @@ 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,9 +114,13 @@ 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.
@ -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);
}
}

View File

@ -12,9 +12,23 @@ 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<TextRange>): ParseNode | undefined {
export function findNodeByPosition(
node: ParseNode,
position: Position,
lines: TextRangeCollection<TextRange>
): ParseNode | undefined {
const offset = convertPositionToOffset(position, lines);
if (offset === undefined) {
return undefined;
@ -80,13 +96,15 @@ 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 => {
return (
printExpression(node.leftExpression, flags) +
'(' +
node.arguments
.map(arg => {
let argStr = '';
if (arg.argumentCategory === ArgumentCategory.UnpackedList) {
argStr = '*';
@ -98,25 +116,33 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla
}
argStr += printExpression(arg.valueExpression, flags);
return argStr;
}).join(', ') +
')';
})
.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 node.strings
.map(str => {
return printExpression(str, flags);
}).join(' ');
})
.join(' ');
}
}
@ -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,9 +227,13 @@ 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: {
@ -251,16 +284,23 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla
listStr = `${keyStr}: ${valueStr}`;
}
return listStr + ' ' +
node.comprehensions.map(expr => {
return (
listStr +
' ' +
node.comprehensions
.map(expr => {
if (expr.nodeType === ParseNodeType.ListComprehensionFor) {
return `${ expr.isAsync ? 'async ' : '' }for ` +
return (
`${expr.isAsync ? 'async ' : ''}for ` +
printExpression(expr.targetExpression, flags) +
` in ${ printExpression(expr.iterableExpression, flags) }`;
` in ${printExpression(expr.iterableExpression, flags)}`
);
} else {
return `if ${printExpression(expr.testExpression, flags)}`;
}
}).join(' ');
})
.join(' ')
);
}
case ParseNodeType.Slice: {
@ -278,7 +318,10 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla
}
case ParseNodeType.Lambda: {
return 'lambda ' + node.parameters.map(param => {
return (
'lambda ' +
node.parameters
.map(param => {
let paramStr = '';
if (param.category === ParameterCategory.VarArgList) {
@ -295,7 +338,11 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla
paramStr += ' = ' + printExpression(param.defaultValue, flags);
}
return paramStr;
}).join(', ') + ': ' + printExpression(node.expression, flags);
})
.join(', ') +
': ' +
printExpression(node.expression, flags)
);
}
case ParseNodeType.Constant: {
@ -314,8 +361,10 @@ export function printExpression(node: ExpressionNode, flags = PrintExpressionFla
case ParseNodeType.Dictionary: {
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);
}
@ -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 ||
if (
curNode.nodeType === ParseNodeType.Lambda ||
curNode.nodeType === ParseNodeType.Function ||
curNode.nodeType === ParseNodeType.Class ||
curNode.nodeType === ParseNodeType.Module) {
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 ||
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) {
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 ||
if (
statementList.statements[0] !== callNode.parent ||
!statementList.parent ||
!(statementList.parent.nodeType === ParseNodeType.Module ||
statementList.parent.nodeType === ParseNodeType.Suite)) {
!(
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 &&
if (
defaultTarget.nodeType === ParseNodeType.MemberAccess &&
defaultTarget.memberName.value === '__new__' &&
defaultTarget.leftExpression.nodeType === ParseNodeType.Name &&
defaultTarget.leftExpression.value === namedTupleAssignedName) {
defaultTarget.leftExpression.value === namedTupleAssignedName
) {
return true;
}
}

View File

@ -8,19 +8,74 @@
*/
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;

View File

@ -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,8 +327,10 @@ 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[] = [];
@ -336,16 +345,18 @@ export class Program {
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(` 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()}`);
@ -359,8 +370,9 @@ 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()}`);
});
@ -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<string, SourceFileInfo>,
recursionCount: number) {
private _getImportsRecursive(
file: SourceFileInfo,
closureMap: Map<string, SourceFileInfo>,
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<string, boolean>()): void {
dependencyMap = new Map<string, boolean>()
): 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<string, boolean>) {
private _markFileDirtyRecursive(sourceFileInfo: SourceFileInfo, markMap: Map<string, boolean>) {
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);
}
}
}
@ -975,20 +985,20 @@ 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) => {
} 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.
@ -1001,7 +1011,8 @@ export class Program {
}
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,15 +1058,18 @@ 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}`);
@ -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);
}

View File

@ -11,8 +11,14 @@ 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<string, string[]>();
@ -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;
@ -109,10 +117,11 @@ export function findPythonSearchPaths(fs: VirtualFileSystem, configOptions: Conf
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.
@ -137,12 +146,10 @@ export function getPythonPathFromPythonInterpreter(fs: VirtualFileSystem,
if (interpreterPath) {
importFailureInfo.push(`Executing interpreter at '${interpreterPath}'`);
execOutput = child_process.execFileSync(
interpreterPath, commandLineArgs, { encoding: 'utf8' });
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.

View File

@ -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;

View File

@ -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,8 +261,10 @@ 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}.`);
configFilePath = commandLineOptions.executionRoot;
@ -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`);
`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.
@ -388,13 +402,15 @@ 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 }.`);
`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 => {
@ -406,16 +422,18 @@ 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}`);
});
@ -424,8 +442,7 @@ export class AnalyzerService {
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}`);
});
@ -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);
}
@ -587,8 +602,7 @@ export class AnalyzerService {
const fileMap = new Map<string, string>();
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__';
}
@ -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'}`);
}
}
@ -798,8 +810,7 @@ export class AnalyzerService {
this._removeConfigFileWatcher();
if (this._configFilePath) {
this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath],
'all', event => {
this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath], 'all', event => {
if (this._verboseOutput) {
this._console.log(`Received fs event '${event}' for config file`);
}
@ -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);

View File

@ -9,10 +9,7 @@
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,
diagList.push(
new Diagnostic(
DiagnosticCategory.Error,
`Import chain depth exceeded ${this._hitMaxImportDepth}`,
getEmptyRange()));
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,
{
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,
{
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,
{
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

View File

@ -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 &&
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) {
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 &&
} else if (
node.nodeType === ParseNodeType.MemberAccess &&
node.memberName.value === 'TYPE_CHECKING' &&
node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.value === 'typing') {
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 &&
if (
node.expressions[0].nodeType === ParseNodeType.Number &&
!node.expressions[0].isImaginary &&
node.expressions[1].nodeType === ParseNodeType.Number &&
!node.expressions[1].isImaginary) {
!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 &&
if (
node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.value === 'sys' &&
node.memberName.value === 'version_info') {
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 &&
if (
node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.value === 'sys' &&
node.memberName.value === 'platform') {
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 &&
if (
node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.value === 'os' &&
node.memberName.value === 'name') {
node.memberName.value === 'name'
) {
return true;
}
}

View File

@ -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() {

View File

@ -12,16 +12,12 @@ 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.

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,30 @@
*/
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';
@ -33,9 +52,7 @@ class TrackedImport {
}
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<string, boolean>,
private _treatStringsAsSymbols: boolean) {
constructor(private _accessedImportedSymbols: Map<string, boolean>, private _treatStringsAsSymbols: boolean) {
super();
}
@ -115,8 +127,7 @@ export class TypeStubWriter extends ParseTreeWalker {
private _trackedImportFrom = new Map<string, TrackedImportFrom>();
private _accessedImportedSymbols = new Map<string, boolean>();
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
@ -151,14 +162,16 @@ export class TypeStubWriter extends ParseTreeWalker {
this._emitDecorators(node.decorators);
let line = `class ${className}`;
if (node.arguments.length > 0) {
line += `(${ node.arguments.map(arg => {
line += `(${node.arguments
.map(arg => {
let argString = '';
if (arg.name) {
argString = arg.name.value + '=';
}
argString += this._printExpression(arg.valueExpression);
return argString;
}).join(', ') })`;
})
.join(', ')})`;
}
line += ':';
this._emitLine(line);
@ -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() {
@ -601,9 +621,9 @@ export class TypeStubWriter extends ParseTreeWalker {
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) {
@ -615,13 +635,15 @@ export class TypeStubWriter extends ParseTreeWalker {
if (sortedSymbols.length > 0) {
importStr += `from ${imp.importName} import `;
importStr += sortedSymbols.map(symbol => {
importStr += sortedSymbols
.map(symbol => {
let symStr = symbol.name;
if (symbol.alias) {
symStr += ' as ' + symbol.alias;
}
return symStr;
}).join(', ');
})
.join(', ');
importStr += this._lineEnd;
lineEmitted = true;

View File

@ -12,10 +12,25 @@ 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
@ -1226,8 +1246,10 @@ export function requiresSpecialization(type: Type, recursionCount = 0): boolean
}
}
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' : '';
if (typeof literalValue === 'string') {
const prefix = type.classType.details.name === 'bytes' ? 'b' : '';
literalStr = `${prefix}'${literalValue.toString()}'`;
} else if (typeof(literalValue) === 'boolean') {
} else if (typeof literalValue === 'boolean') {
literalStr = literalValue ? 'True' : 'False';
} else {
literalStr = literalValue.toString();

View File

@ -9,8 +9,8 @@
* 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<string, Type>;

View File

@ -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;
@ -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 => {
return (
classType.details.baseClasses.find(baseClass => {
if (baseClass.category === TypeCategory.Class) {
if (isBuiltIn(baseClass, 'Protocol')) {
return true;
}
}
return false;
}) !== undefined;
}) !== 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 ||
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) {
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 ||
if (
dataClassEntries1[i].name !== dataClassEntries2[i].name ||
dataClassEntries1[i].hasDefault !== dataClassEntries2[i].hasDefault ||
!isTypeSame(dataClassEntries1[i].type, dataClassEntries2[i].type, recursionCount + 1)) {
!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) {
@ -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;
}
}

View File

@ -11,7 +11,11 @@ import { compareValues, Comparison, equateValues, isArray } from './core';
export const emptyArray: never[] = [] as never[];
export type EqualityComparer<T> = (a: T, b: T) => boolean;
export function contains<T>(array: readonly T[] | undefined, value: T, equalityComparer: EqualityComparer<T> = equateValues): boolean {
export function contains<T>(
array: readonly T[] | undefined,
value: T,
equalityComparer: EqualityComparer<T> = equateValues
): boolean {
if (array) {
for (const v of array) {
if (equalityComparer(v, value)) {
@ -36,20 +40,29 @@ export interface Push<T> {
* appended.
*/
export function append<TArray extends any[] | undefined, TValue extends NonNullable<TArray>[number] | undefined>(
to: TArray, value: TValue): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable<TArray>[number][];
to: TArray,
value: TValue
): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable<TArray>[number][];
export function append<T>(to: T[], value: T | undefined): T[];
export function append<T>(to: T[] | undefined, value: T): T[];
export function append<T>(to: T[] | undefined, value: T | undefined): T[] | undefined;
export function append<T>(to: Push<T>, value: T | undefined): void;
export function append<T>(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<T, U extends T>(array: readonly T[], predicate: (element: T, index: number) => element is U): U | undefined;
export function find<T, U extends T>(
array: readonly T[],
predicate: (element: T, index: number) => element is U
): U | undefined;
export function find<T>(array: readonly T[], predicate: (element: T, index: number) => boolean): T | undefined;
export function find<T>(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<T>(to: T[], from: readonly T[] | undefined, start?: number, end?: number): T[];
export function addRange<T>(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined;
export function addRange<T>(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<T>(
to: T[] | undefined,
from: readonly T[] | undefined,
start?: number,
end?: number
): T[] | undefined;
export function addRange<T>(
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<T>(array: readonly T[], comparer: Comparer<T>): SortedReadonlyArray<T> {
const indices = indicesOf(array);
stableSortIndices(array, indices, comparer);
return indices.map(i => array[i]) as SortedArray<T> as SortedReadonlyArray<T>;
return (indices.map(i => array[i]) as SortedArray<T>) as SortedReadonlyArray<T>;
}
function stableSortIndices<T>(array: readonly T[], indices: number[], comparer: Comparer<T>) {
@ -193,9 +220,13 @@ export function every<T>(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<T, U>(array: readonly T[], value: T,
keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
export function binarySearch<T, U>(
array: readonly T[],
value: T,
keySelector: (v: T) => U,
keyComparer: Comparer<U>,
offset?: number
): number {
return binarySearchKey(array, keySelector(value), keySelector, keyComparer, offset);
}
@ -209,9 +240,13 @@ export function binarySearch<T, U>(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<T, U>(array: readonly T[], key: U,
keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
export function binarySearchKey<T, U>(
array: readonly T[],
key: U,
keySelector: (v: T) => U,
keyComparer: Comparer<U>,
offset?: number
): number {
if (!some(array)) {
return -1;
}

View File

@ -11,8 +11,7 @@ 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;
@ -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))
: '';
}
}
@ -781,9 +852,7 @@ export class ConfigOptions {
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') {
@ -798,11 +867,17 @@ export class ConfigOptions {
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') {
@ -819,8 +894,10 @@ export class ConfigOptions {
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)));
}

View File

@ -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<T>(x: T) { return x; }
export function identity<T>(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<T>(a: T, b: T) { return a === b; }
export function equateValues<T>(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;
}
/**

View File

@ -9,12 +9,17 @@
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);
}
@ -30,7 +35,9 @@ export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
}
export function assertDefined<T>(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;
}

View File

@ -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) {

View File

@ -7,7 +7,7 @@
* Represents a single edit within a file.
*/
import { Range } from "./textRange";
import { Range } from './textRange';
export interface TextEditAction {
range: Range;

View File

@ -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);
}
@ -532,8 +601,7 @@ export function getWildcardRegexPattern(rootPath: string, fileSpec: string): str
component = escapedSeparator + component;
}
regExPattern += component.replace(
reservedCharacterPattern, match => {
regExPattern += component.replace(reservedCharacterPattern, match => {
if (match === '*') {
return `[^${escapedSeparator}]*`;
} else if (match === '?') {
@ -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.
@ -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;

View File

@ -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<TextRange>): Range {
export function convertOffsetsToRange(
startOffset: number,
endOffset: number,
lines: TextRangeCollection<TextRange>
): 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<TextRange>): number | undefined {
export function convertPositionToOffset(position: Position, lines: TextRangeCollection<TextRange>): number | undefined {
if (position.line >= lines.count) {
return undefined;
}

View File

@ -31,8 +31,8 @@ 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;
const majorVersion = (version >> 8) & 0xff;
const minorVersion = version & 0xff;
return `${majorVersion}.${minorVersion}`;
}
@ -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;
}

View File

@ -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) {

View File

@ -107,9 +107,7 @@ export class TextRangeCollection<T extends TextRange> {
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;
}

View File

@ -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

View File

@ -7,11 +7,30 @@
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';
@ -98,7 +117,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
}
protected abstract async executeCommand(cmdParams: ExecuteCommandParams): Promise<any>;
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<ServerSettings>;
protected getConfiguration(workspace: WorkspaceServiceInstance, section: string) {
@ -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,7 +207,8 @@ 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._connection.onInitialize(
(params): InitializeResult => {
this.rootPath = params.rootPath || '';
// Create a service instance for each of the workspace folders.
@ -228,14 +252,12 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
triggerCharacters: ['(', ',', ')']
},
codeActionProvider: {
codeActionKinds: [
CodeActionKind.QuickFix,
CodeActionKind.SourceOrganizeImports
]
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 => {
const markupString = hoverResults.parts
.map(part => {
if (part.python) {
return '```python\n' + part.text + '\n```\n';
}
return part.text;
}).join('');
})
.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,16 +541,18 @@ 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();
@ -539,8 +560,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
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];

View File

@ -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))
);
}
}

View File

@ -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);
}
}

View File

@ -8,8 +8,7 @@
* 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';
@ -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,7 +458,8 @@ export class CompletionProvider {
}
private _printMethodSignature(node: FunctionNode): string {
const paramList = node.parameters.map(param => {
const paramList = node.parameters
.map(param => {
let paramString = '';
if (param.category === ParameterCategory.VarArgList) {
paramString += '*';
@ -466,7 +476,8 @@ export class CompletionProvider {
}
return paramString;
}).join(', ');
})
.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<string, Symbol>();
@ -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;
@ -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,15 +761,23 @@ 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}`;
@ -771,8 +811,7 @@ export class CompletionProvider {
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,8 +884,7 @@ 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}`;
@ -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);
}
@ -1124,26 +1205,51 @@ export class CompletionProvider {
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);
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 ||
} else if (
sortCategory === SortCategory.Keyword ||
sortCategory === SortCategory.NormalSymbol ||
sortCategory === SortCategory.PrivateSymbol ||
sortCategory === SortCategory.DunderSymbol) {
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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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.
@ -193,9 +188,9 @@ export class ImportSorter {
}
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}`;

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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);
});
}

View File

@ -9,8 +9,16 @@
*/
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;

View File

@ -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);
@ -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);
}
@ -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 ||
while (
nextOperator === OperatorType.Multiply ||
nextOperator === OperatorType.MatrixMultiply ||
nextOperator === OperatorType.Divide ||
nextOperator === OperatorType.Mod ||
nextOperator === OperatorType.FloorDivide) {
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 ||
if (
nextOperator === OperatorType.Add ||
nextOperator === OperatorType.Subtract ||
nextOperator === OperatorType.BitwiseInvert) {
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(), () => {
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();
});
}
);
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 ||
if (
nextTokenType === TokenType.CloseParenthesis ||
nextTokenType === TokenType.NewLine ||
nextTokenType === TokenType.EndOfStream) {
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,10 +2243,13 @@ export class Parser {
// and emit an error.
this._addError(
'Expressions surrounded by backticks are not supported in Python 3.x; use repr instead',
nextToken);
nextToken
);
const expressionNode = this._parseTestListAsExpression(ErrorExpressionCategory.MissingExpression,
'Expected expression');
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 ||
if (
keywordToken.keywordType === KeywordType.False ||
keywordToken.keywordType === KeywordType.True ||
keywordToken.keywordType === KeywordType.Debug ||
keywordToken.keywordType === KeywordType.None) {
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,7 +2417,8 @@ export class Parser {
private _parseTestListWithComprehension(): ExpressionListResult {
let sawComprehension = false;
return this._parseExpressionListGeneric(() => {
return this._parseExpressionListGeneric(
() => {
let expr = this._parseTestOrStarExpression(true);
const listComp = this._tryParseListComprehension(expr);
if (listComp) {
@ -2368,7 +2428,8 @@ export class Parser {
return expr;
},
() => this._isNextTokenNeverExpression(),
() => sawComprehension);
() => sawComprehension
);
}
// '{' [dictorsetmaker] '}'
@ -2501,11 +2562,11 @@ export class Parser {
return dictionaryAtom;
}
private _parseExpressionListGeneric(parser: () => ExpressionNode,
private _parseExpressionListGeneric(
parser: () => ExpressionNode,
terminalCheck: () => boolean = () => this._isNextTokenNeverExpression(),
finalEntryCheck: () => boolean = () => false):
ExpressionListResult {
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)
);
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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,

File diff suppressed because it is too large Load Diff

View File

@ -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);
@ -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,7 +230,9 @@ function processArgs() {
function printUsage() {
console.log(
'Usage: ' + toolName + ' [options] files...\n' +
'Usage: ' +
toolName +
' [options] files...\n' +
' Options:\n' +
' --createstub IMPORT Create type stub file(s) for import\n' +
' --dependencies Emit import dependency information\n' +
@ -256,9 +258,11 @@ function printVersion() {
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,19 +316,22 @@ 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}`);
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.category === DiagnosticCategory.Error ? chalk.red('error') : chalk.green('warning');
message += `: ${diag.message}`;
const rule = diag.getRule();
@ -345,7 +352,8 @@ function reportDiagnosticsAsText(fileDiagnostics: FileDiagnostics[]): Diagnostic
console.log(
`${errorCount.toString()} ${errorCount === 1 ? 'error' : 'errors'}, ` +
`${ warningCount.toString() } ${ warningCount === 1 ? 'warning' : 'warnings' } `);
`${warningCount.toString()} ${warningCount === 1 ? 'warning' : 'warnings'} `
);
return {
errorCount,

View File

@ -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);
@ -94,9 +214,7 @@ test('Builtins1', () => {
}
});
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);

View File

@ -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]);
});

View File

@ -17,7 +17,7 @@ import { combinePaths, normalizePath,normalizeSlashes } from '../common/pathUtil
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('<default>', createFromRealFileSystem(), new NullConsole());
const commandLineOptions = new CommandLineOptions(cwd, true);
commandLineOptions.configFilePath = 'src/tests/samples/project1';
@ -27,8 +27,10 @@ test('FindFilesWithConfigFile', () => {
// 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(
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('<default>', 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('<default>', 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('<default>', createFromRealFileSystem(nullConsole), nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, false);
@ -80,8 +82,10 @@ test('SomeFileSpecsAreInvalid', () => {
// and one in the exclude array.
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('<default>', 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);

View File

@ -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');
});

View File

@ -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));

View File

@ -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<string, string>(Object.entries(f.fileOptions))));
const documents = data.files.map(
f => new factory.TextDocument(f.fileName, f.content, new Map<string, string>(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)));
@ -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
);
}

View File

@ -50,9 +50,16 @@
//// 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" },
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'
}
});

View File

@ -48,6 +48,6 @@
//// 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" },
marker1: { category: 'error', message: "Expected no arguments to 'D' constructor" },
marker2: { category: 'error', message: "'D(3)' has type 'Type[D]' and is not callable" }
});

View File

@ -29,6 +29,6 @@
////
helper.verifyDiagnostics({
"marker1": { category: "error", message: "Expected 1 positional argument" },
"marker2": { category: "error", message: "Argument missing for parameter 'y'" },
marker1: { category: 'error', message: 'Expected 1 positional argument' },
marker2: { category: 'error', message: "Argument missing for parameter 'y'" }
});

Some files were not shown because too many files have changed in this diff Show More