mirror of
https://github.com/microsoft/pyright.git
synced 2024-08-17 03:40:44 +03:00
Added "reportTypedDictNotRequiredAccess" diagnostic rule and split out diagnostics that pertain specifically to unguarded accesses to non-required TypedDict keys.
This commit is contained in:
parent
41a0bc4015
commit
ea8628909a
@ -86,6 +86,8 @@ The following settings control pyright’s diagnostic output (warnings or errors
|
||||
|
||||
**reportOptionalOperand** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to use an Optional type as an operand to a binary or unary operator (like '+', '==', 'or', 'not'). The default value for this setting is 'none'.
|
||||
|
||||
**reportTypedDictNotRequiredAccess** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to access a non-required field within a TypedDict without first checking whether it is present. The default value for this setting is 'error'.
|
||||
|
||||
**reportUntypedFunctionDecorator** [boolean or string, optional]: Generate or suppress diagnostics for function decorators that have no type annotations. These obscure the function type, defeating many type analysis features. The default value for this setting is 'none'.
|
||||
|
||||
**reportUntypedClassDecorator** [boolean or string, optional]: Generate or suppress diagnostics for class decorators that have no type annotations. These obscure the class type, defeating many type analysis features. The default value for this setting is 'none'.
|
||||
@ -249,6 +251,7 @@ The following table lists the default severity levels for each diagnostic rule w
|
||||
| reportOptionalIterable | "none" | "none" | "error" |
|
||||
| reportOptionalContextManager | "none" | "none" | "error" |
|
||||
| reportOptionalOperand | "none" | "none" | "error" |
|
||||
| reportTypedDictNotRequiredAccess | "none" | "error" | "error" |
|
||||
| reportUntypedFunctionDecorator | "none" | "none" | "error" |
|
||||
| reportUntypedClassDecorator | "none" | "none" | "error" |
|
||||
| reportUntypedBaseClass | "none" | "none" | "error" |
|
||||
@ -257,7 +260,7 @@ The following table lists the default severity levels for each diagnostic rule w
|
||||
| reportConstantRedefinition | "none" | "none" | "error" |
|
||||
| reportIncompatibleMethodOverride | "none" | "none" | "error" |
|
||||
| reportIncompatibleVariableOverride | "none" | "none" | "error" |
|
||||
| reportOverlappingOverload | "none" | "none" | "error" |
|
||||
| reportOverlappingOverload | "none" | "none" | "error" |
|
||||
| reportInvalidStringEscapeSequence | "none" | "warning" | "error" |
|
||||
| reportUnknownParameterType | "none" | "none" | "error" |
|
||||
| reportUnknownArgumentType | "none" | "none" | "error" |
|
||||
|
@ -5463,6 +5463,7 @@ export function createTypeEvaluator(
|
||||
const indexTypeResult = getTypeOfExpression(node.items[0].valueExpression);
|
||||
const indexType = indexTypeResult.type;
|
||||
let diag = new DiagnosticAddendum();
|
||||
let allDiagsInvolveNotRequiredKeys = true;
|
||||
|
||||
const resultingType = mapSubtypes(indexType, (subtype) => {
|
||||
if (isAnyOrUnknown(subtype)) {
|
||||
@ -5486,6 +5487,7 @@ export function createTypeEvaluator(
|
||||
type: printType(baseType),
|
||||
})
|
||||
);
|
||||
allDiagsInvolveNotRequiredKeys = false;
|
||||
return UnknownType.create();
|
||||
} else if (!entry.isRequired && usage.method === 'get') {
|
||||
diag.addMessage(
|
||||
@ -5499,19 +5501,19 @@ export function createTypeEvaluator(
|
||||
if (usage.method === 'set') {
|
||||
canAssignType(entry.valueType, usage.setType!, diag);
|
||||
} else if (usage.method === 'del' && entry.isRequired) {
|
||||
const fileInfo = getFileInfo(node);
|
||||
addDiagnostic(
|
||||
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.keyRequiredDeleted().format({ name: entryName }),
|
||||
node
|
||||
diag.addMessage(
|
||||
Localizer.DiagnosticAddendum.keyRequiredDeleted().format({
|
||||
name: entryName,
|
||||
})
|
||||
);
|
||||
allDiagsInvolveNotRequiredKeys = false;
|
||||
}
|
||||
|
||||
return entry.valueType;
|
||||
}
|
||||
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.typeNotStringLiteral().format({ type: printType(subtype) }));
|
||||
allDiagsInvolveNotRequiredKeys = false;
|
||||
return UnknownType.create();
|
||||
});
|
||||
|
||||
@ -5534,8 +5536,12 @@ export function createTypeEvaluator(
|
||||
|
||||
const fileInfo = getFileInfo(node);
|
||||
addDiagnostic(
|
||||
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
allDiagsInvolveNotRequiredKeys
|
||||
? fileInfo.diagnosticRuleSet.reportTypedDictNotRequiredAccess
|
||||
: fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
allDiagsInvolveNotRequiredKeys
|
||||
? DiagnosticRule.reportTypedDictNotRequiredAccess
|
||||
: DiagnosticRule.reportGeneralTypeIssues,
|
||||
typedDictDiag + diag.getString(),
|
||||
node
|
||||
);
|
||||
|
@ -147,9 +147,12 @@ export interface DiagnosticRuleSet {
|
||||
// Report attempts to use an Optional type in a "with" statement?
|
||||
reportOptionalContextManager: DiagnosticLevel;
|
||||
|
||||
// Report attempts to use an Optional type in a binary or unary operation.
|
||||
// Report attempts to use an Optional type in a binary or unary operation?
|
||||
reportOptionalOperand: DiagnosticLevel;
|
||||
|
||||
// Report accesses to non-required TypedDict fields?
|
||||
reportTypedDictNotRequiredAccess: DiagnosticLevel;
|
||||
|
||||
// Report untyped function decorators that obscure the function type?
|
||||
reportUntypedFunctionDecorator: DiagnosticLevel;
|
||||
|
||||
@ -288,6 +291,7 @@ export function getDiagLevelDiagnosticRules() {
|
||||
DiagnosticRule.reportOptionalIterable,
|
||||
DiagnosticRule.reportOptionalContextManager,
|
||||
DiagnosticRule.reportOptionalOperand,
|
||||
DiagnosticRule.reportTypedDictNotRequiredAccess,
|
||||
DiagnosticRule.reportUntypedFunctionDecorator,
|
||||
DiagnosticRule.reportUntypedClassDecorator,
|
||||
DiagnosticRule.reportUntypedBaseClass,
|
||||
@ -355,6 +359,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
|
||||
reportOptionalIterable: 'none',
|
||||
reportOptionalContextManager: 'none',
|
||||
reportOptionalOperand: 'none',
|
||||
reportTypedDictNotRequiredAccess: 'none',
|
||||
reportUntypedFunctionDecorator: 'none',
|
||||
reportUntypedClassDecorator: 'none',
|
||||
reportUntypedBaseClass: 'none',
|
||||
@ -418,6 +423,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
|
||||
reportOptionalIterable: 'none',
|
||||
reportOptionalContextManager: 'none',
|
||||
reportOptionalOperand: 'none',
|
||||
reportTypedDictNotRequiredAccess: 'error',
|
||||
reportUntypedFunctionDecorator: 'none',
|
||||
reportUntypedClassDecorator: 'none',
|
||||
reportUntypedBaseClass: 'none',
|
||||
@ -481,6 +487,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
|
||||
reportOptionalIterable: 'error',
|
||||
reportOptionalContextManager: 'error',
|
||||
reportOptionalOperand: 'error',
|
||||
reportTypedDictNotRequiredAccess: 'error',
|
||||
reportUntypedFunctionDecorator: 'error',
|
||||
reportUntypedClassDecorator: 'error',
|
||||
reportUntypedBaseClass: 'error',
|
||||
@ -960,6 +967,13 @@ export class ConfigOptions {
|
||||
defaultSettings.reportOptionalOperand
|
||||
),
|
||||
|
||||
// Read the "reportTypedDictNotRequiredAccess" entry.
|
||||
reportTypedDictNotRequiredAccess: this._convertDiagnosticLevel(
|
||||
configObj.reportTypedDictNotRequiredAccess,
|
||||
DiagnosticRule.reportTypedDictNotRequiredAccess,
|
||||
defaultSettings.reportTypedDictNotRequiredAccess
|
||||
),
|
||||
|
||||
// Read the "reportUntypedFunctionDecorator" entry.
|
||||
reportUntypedFunctionDecorator: this._convertDiagnosticLevel(
|
||||
configObj.reportUntypedFunctionDecorator,
|
||||
|
@ -35,6 +35,7 @@ export enum DiagnosticRule {
|
||||
reportOptionalIterable = 'reportOptionalIterable',
|
||||
reportOptionalContextManager = 'reportOptionalContextManager',
|
||||
reportOptionalOperand = 'reportOptionalOperand',
|
||||
reportTypedDictNotRequiredAccess = 'reportTypedDictNotRequiredAccess',
|
||||
reportUntypedFunctionDecorator = 'reportUntypedFunctionDecorator',
|
||||
reportUntypedClassDecorator = 'reportUntypedClassDecorator',
|
||||
reportUntypedBaseClass = 'reportUntypedBaseClass',
|
||||
|
@ -388,8 +388,6 @@ export namespace Localizer {
|
||||
new ParameterizedString<{ type: string }>(getRawString('Diagnostic.isInstanceInvalidType'));
|
||||
export const isSubclassInvalidType = () =>
|
||||
new ParameterizedString<{ type: string }>(getRawString('Diagnostic.isSubclassInvalidType'));
|
||||
export const keyRequiredDeleted = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.keyRequiredDeleted'));
|
||||
export const keyValueInSet = () => getRawString('Diagnostic.keyValueInSet');
|
||||
export const keywordArgInTypeArgument = () => getRawString('Diagnostic.keywordArgInTypeArgument');
|
||||
export const keywordSubscriptIllegal = () => getRawString('Diagnostic.keywordSubscriptIllegal');
|
||||
@ -831,6 +829,8 @@ export namespace Localizer {
|
||||
);
|
||||
export const keyNotRequired = () =>
|
||||
new ParameterizedString<{ name: string; type: string }>(getRawString('DiagnosticAddendum.keyNotRequired'));
|
||||
export const keyRequiredDeleted = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('DiagnosticAddendum.keyRequiredDeleted'));
|
||||
export const keyUndefined = () =>
|
||||
new ParameterizedString<{ name: string; type: string }>(getRawString('DiagnosticAddendum.keyUndefined'));
|
||||
export const literalAssignmentMismatch = () =>
|
||||
|
@ -172,7 +172,6 @@
|
||||
"invalidTokenChars": "Invalid character in token \"{text}\"",
|
||||
"isInstanceInvalidType": "Second argument to \"isinstance\" must be a class or tuple of classes",
|
||||
"isSubclassInvalidType": "Second argument to \"issubclass\" must be a class or tuple of classes",
|
||||
"keyRequiredDeleted": "\"{name}\" is a required key and cannot be deleted",
|
||||
"keyValueInSet": "Key/value pairs are not allowed within a set",
|
||||
"keywordArgInTypeArgument": "Keyword arguments cannot be used in type argument lists",
|
||||
"keywordSubscriptIllegal": "Keyword arguments within subscripts requires Python 3.10 or newer",
|
||||
@ -430,6 +429,7 @@
|
||||
"dataclassFrozen": "Dataclass \"{name}\" is frozen",
|
||||
"finalMethod": "Final method",
|
||||
"keyNotRequired": "\"{name}\" is not a required key in \"{type}\", so access may result in runtime exception",
|
||||
"keyRequiredDeleted": "\"{name}\" is a required key and cannot be deleted",
|
||||
"keyUndefined": "\"{name}\" is not a defined key in \"{type}\"",
|
||||
"functionParamName": "Parameter name mismatch: \"{destName}\" versus \"{srcName}\"",
|
||||
"functionReturnTypeMismatch": "Function return type \"{sourceType}\" is incompatible with type \"{destType}\"",
|
||||
|
@ -323,6 +323,17 @@
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"reportTypedDictNotRequiredAccess": {
|
||||
"type": "string",
|
||||
"description": "Diagnostics for an attempt to access a non-required key within a TypedDict without a check for its presence.",
|
||||
"default": "error",
|
||||
"enum": [
|
||||
"none",
|
||||
"information",
|
||||
"warning",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"reportUntypedFunctionDecorator": {
|
||||
"type": "string",
|
||||
"description": "Diagnostics for function decorators that have no type annotations. These obscure the function type, defeating many type analysis features.",
|
||||
|
@ -239,6 +239,12 @@
|
||||
"title": "Controls reporting of attempts to use an Optional type as an operand for a binary or unary operator",
|
||||
"default": "none"
|
||||
},
|
||||
"reportTypedDictNotRequiredAccess": {
|
||||
"$id": "#/properties/reportTypedDictNotRequiredAccess",
|
||||
"$ref": "#/definitions/diagnostic",
|
||||
"title": "Controls reporting of attempts to access a non-required key in a TypedDict without a check for its presence",
|
||||
"default": "error"
|
||||
},
|
||||
"reportUntypedFunctionDecorator": {
|
||||
"$id": "#/properties/reportUntypedFunctionDecorator",
|
||||
"$ref": "#/definitions/diagnostic",
|
||||
|
Loading…
Reference in New Issue
Block a user