diff --git a/docs/configuration.md b/docs/configuration.md index 21b8016d2..c16c472e3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -44,6 +44,8 @@ The following settings control pyright’s diagnostic output (warnings or errors **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'. +**strictSetInference** [boolean]: When inferring the type of a set, use strict type assumptions. For example, the expression `{1, 'a', 3.4}` could be inferred to be of type `Set[Any]` or `Set[Union[int, str, float]]`. 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'. diff --git a/docs/type-inference.md b/docs/type-inference.md index 89149fe44..36b205eca 100644 --- a/docs/type-inference.md +++ b/docs/type-inference.md @@ -257,6 +257,29 @@ var4: List[float] = [1, 3.4] # Infer List[float] ``` +### Set Expressions + +When inferring the type of a set expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics: + +1. If the set contains at least one element and all elements are the same type T, infer the type `Set[T]`. +2. If the set contains multiple elements that are of different types, the behavior depends on the `strictSetInference` configuration setting. By default this setting is off. + + * If `strictSetInference` is off, infer `Set[Unknown]`. + * Otherwise use the union of all element types and infer `Set[Union[(elements)]]`. + +These heuristics can be overridden through the use of bidirectional inference hints (e.g. by providing a declared type for the target of the assignment expression). + +```python +var1 = {1, 2} # Infer Set[int] + +# Type depends on strictSetInference config setting +var2 = {1, 3.4} # Infer Set[Unknown] (off) +var2 = {1, 3.4} # Infer Set[Union[int, float]] (on) + +var3: Set[float] = {1, 3.4} # Infer Set[float] +``` + + ### Dictionary Expressions When inferring the type of a dictionary expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics: diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 9bb7e4fe2..4d8c2d89a 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -10083,10 +10083,12 @@ export function createTypeEvaluator( let inferredEntryType: Type = expectedType ? AnyType.create() : UnknownType.create(); if (entryTypes.length > 0) { + const fileInfo = getFileInfo(node); // If there was an expected type or we're using strict list inference, // combine the types into a union. if ( - (builtInClassName === 'list' && getFileInfo(node).diagnosticRuleSet.strictListInference) || + (builtInClassName === 'list' && fileInfo.diagnosticRuleSet.strictListInference) || + (builtInClassName === 'set' && fileInfo.diagnosticRuleSet.strictSetInference) || !!expectedType ) { inferredEntryType = combineTypes(entryTypes, maxSubtypesForInferredType); diff --git a/packages/pyright-internal/src/common/configOptions.ts b/packages/pyright-internal/src/common/configOptions.ts index e651efd30..b00a59d71 100644 --- a/packages/pyright-internal/src/common/configOptions.ts +++ b/packages/pyright-internal/src/common/configOptions.ts @@ -84,6 +84,9 @@ export interface DiagnosticRuleSet { // Use strict inference rules for list expressions? strictListInference: boolean; + // Use strict inference rules for set expressions? + strictSetInference: boolean; + // Use strict inference rules for dictionary expressions? strictDictionaryInference: boolean; @@ -260,6 +263,7 @@ export function cloneDiagnosticRuleSet(diagSettings: DiagnosticRuleSet): Diagnos export function getBooleanDiagnosticRules() { return [ DiagnosticRule.strictListInference, + DiagnosticRule.strictSetInference, DiagnosticRule.strictDictionaryInference, DiagnosticRule.strictParameterNoneValue, @@ -337,6 +341,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet { omitUnannotatedParamType: true, pep604Printing: true, strictListInference: false, + strictSetInference: false, strictDictionaryInference: false, strictParameterNoneValue: false, enableTypeIgnoreComments: true, @@ -401,6 +406,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet { omitUnannotatedParamType: true, pep604Printing: true, strictListInference: false, + strictSetInference: false, strictDictionaryInference: false, strictParameterNoneValue: false, enableTypeIgnoreComments: true, @@ -465,6 +471,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet { omitUnannotatedParamType: false, pep604Printing: true, strictListInference: true, + strictSetInference: true, strictDictionaryInference: true, strictParameterNoneValue: true, enableTypeIgnoreComments: true, // Not overridden by strict mode @@ -813,6 +820,13 @@ export class ConfigOptions { defaultSettings.strictListInference ), + // Use strict inference rules for set expressions? + strictSetInference: this._convertBoolean( + configObj.strictSetInference, + DiagnosticRule.strictSetInference, + defaultSettings.strictSetInference + ), + // Use strict inference rules for dictionary expressions? strictDictionaryInference: this._convertBoolean( configObj.strictDictionaryInference, diff --git a/packages/pyright-internal/src/common/diagnosticRules.ts b/packages/pyright-internal/src/common/diagnosticRules.ts index 14a30dcf9..86335d7c8 100644 --- a/packages/pyright-internal/src/common/diagnosticRules.ts +++ b/packages/pyright-internal/src/common/diagnosticRules.ts @@ -12,6 +12,7 @@ // to match declaration of user-visible settings in package.json export enum DiagnosticRule { strictListInference = 'strictListInference', + strictSetInference = 'strictSetInference', strictDictionaryInference = 'strictDictionaryInference', strictParameterNoneValue = 'strictParameterNoneValue', enableTypeIgnoreComments = 'enableTypeIgnoreComments', diff --git a/packages/vscode-pyright/schemas/pyrightconfig.schema.json b/packages/vscode-pyright/schemas/pyrightconfig.schema.json index acd8ebb26..50136b406 100644 --- a/packages/vscode-pyright/schemas/pyrightconfig.schema.json +++ b/packages/vscode-pyright/schemas/pyrightconfig.schema.json @@ -107,6 +107,12 @@ "title": "Infer strict types for list expressions", "default": false }, + "strictSetInference": { + "$id": "#/properties/strictSetInference", + "type": "boolean", + "title": "Infer strict types for set expressions", + "default": false + }, "strictDictionaryInference": { "$id": "#/properties/strictDictionaryInference", "type": "boolean",