From 7d5ff66df2f0c2e0b49432bfa615df895dc3bb60 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Wed, 15 May 2019 22:59:32 -0700 Subject: [PATCH] Added config options strictListInference and strictDictionaryInference. --- client/schemas/pyrightconfig.schema.json | 12 +++++++++++ docs/configuration.md | 4 ++++ server/src/analyzer/expressionEvaluator.ts | 25 ++++++++++++++-------- server/src/common/configOptions.ts | 25 ++++++++++++++++++++++ 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/client/schemas/pyrightconfig.schema.json b/client/schemas/pyrightconfig.schema.json index 10a8a5cc7..372beb81e 100644 --- a/client/schemas/pyrightconfig.schema.json +++ b/client/schemas/pyrightconfig.schema.json @@ -61,6 +61,18 @@ ], "pattern": "^(.*)$" }, + "strictListInference": { + "$id": "#/properties/strictListInference", + "type": "boolean", + "title": "Infer strict types for list expressions", + "default": "false" + }, + "strictDictionaryInference": { + "$id": "#/properties/strictDictionaryInference", + "type": "boolean", + "title": "Infer strict types for dictionary expressions", + "default": "false" + }, "reportTypeshedErrors": { "$id": "#/properties/reportTypeshedErrors", "$ref": "#/definitions/diagnostic", diff --git a/docs/configuration.md b/docs/configuration.md index 00e9c148d..ac739f136 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -28,6 +28,10 @@ Pyright offers flexible configuration options specified in a JSON-formatted text ## Type Check Diagnostics Settings The following settings control pyright's diagnostic output (warnings or errors). Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, or `"error"` can be used to specify the diagnostic level. +**strictListInference** [boolean]: When inferring the type of a list, use strict type assumptions. For example, the expression `[1, 'a', 3.4]` could be inferred to be of type `List[Any]` or `List[Union[int, str, float]]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is 'false'. + +**strictDictionaryInference** [boolean]: When inferring the type of a dictionary’s keys and values, use strict type assumptions. For example, the expression `{'a': 1, 'b': 'a'}` could be inferred to be of type `Dict[str, Any]` or `Dict[str, Union[int, str]]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is 'false'. + **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. diff --git a/server/src/analyzer/expressionEvaluator.ts b/server/src/analyzer/expressionEvaluator.ts index a256457ad..cc25b8fb1 100644 --- a/server/src/analyzer/expressionEvaluator.ts +++ b/server/src/analyzer/expressionEvaluator.ts @@ -2403,12 +2403,17 @@ export class ExpressionEvaluator { keyType = keyTypes.length > 0 ? TypeUtils.combineTypes(keyTypes) : AnyType.create(); - // If the value type differs, we need to back off because we can't - // properly represent the mappings between different keys and associated - // value types. If all the values are the same type, we'll assume that - // all values in this dictionary should be the same. + // If the value type differs and we're not using "strict inference mode", + // we need to back off because we can't properly represent the mappings + // between different keys and associated value types. If all the values + // are the same type, we'll assume that all values in this dictionary should + // be the same. if (valueTypes.length > 0) { - valueType = TypeUtils.areTypesSame(valueTypes) ? valueTypes[0] : UnknownType.create(); + if (this._fileInfo.configOptions.strictDictionaryInference || this._fileInfo.useStrictMode) { + valueType = TypeUtils.combineTypes(valueTypes); + } else { + valueType = TypeUtils.areTypesSame(valueTypes) ? valueTypes[0] : UnknownType.create(); + } } else { valueType = AnyType.create(); } @@ -2428,11 +2433,13 @@ export class ExpressionEvaluator { const entryTypes = node.entries.map( entry => TypeUtils.stripLiteralValue(this.getType(entry))); - // If the list contains only one type, we'll assume the list is - // homogeneous. Otherwise, we'll avoid making assumptions about - // the list entry type. if (entryTypes.length > 0) { - listEntryType = TypeUtils.areTypesSame(entryTypes) ? entryTypes[0] : UnknownType.create(); + if (this._fileInfo.configOptions.strictListInference || this._fileInfo.useStrictMode) { + listEntryType = TypeUtils.combineTypes(entryTypes); + } else { + // Is the list homogeneous? If so, use stricter rules. Otherwise relax the rules. + listEntryType = TypeUtils.areTypesSame(entryTypes) ? entryTypes[0] : UnknownType.create(); + } } } diff --git a/server/src/common/configOptions.ts b/server/src/common/configOptions.ts index 4a609562c..02508eb1c 100644 --- a/server/src/common/configOptions.ts +++ b/server/src/common/configOptions.ts @@ -79,6 +79,12 @@ export class ConfigOptions { //--------------------------------------------------------------- // Diagnostics Settings + // Use strict inference rules for list expressions? + strictListInference = false; + + // Use strict inference rules for dictionary expressions? + strictDictionaryInference = false; + // Report diagnostics in typeshed files? reportTypeshedErrors: DiagnosticLevel = 'none'; @@ -244,6 +250,14 @@ export class ConfigOptions { } } + // Use strict inference rules for list expressions? + this.strictListInference = this._convertBoolean( + configObj.strictListInference, 'strictListInference', false); + + // Use strict inference rules for dictionary expressions? + this.strictDictionaryInference = this._convertBoolean( + configObj.strictDictionaryInference, 'strictDictionaryInference', false); + // Read the "reportTypeshedErrors" entry. this.reportTypeshedErrors = this._convertDiagnosticLevel( configObj.reportTypeshedErrors, 'reportTypeshedErrors', 'none'); @@ -419,6 +433,17 @@ export class ConfigOptions { return absolutePath; } + private _convertBoolean(value: any, fieldName: string, defaultValue: boolean): boolean { + if (value === undefined) { + return defaultValue; + } else if (typeof value === 'boolean') { + return value ? true : false; + } + + console.log(`Config "${ fieldName }" entry must be true or false.`); + return defaultValue; + } + private _convertDiagnosticLevel(value: any, fieldName: string, defaultValue: DiagnosticLevel): DiagnosticLevel {