mirror of
https://github.com/microsoft/pyright.git
synced 2024-08-17 03:40:44 +03:00
Implemented reportUnnecessaryCast feature.
This commit is contained in:
parent
7e35e4ef1f
commit
797c790a42
@ -246,6 +246,12 @@
|
||||
"title": "Controls reporting calls to 'isinstance' where the result is statically determined to be always true or false",
|
||||
"default": "none"
|
||||
},
|
||||
"reportUnnecessaryCast": {
|
||||
"$id": "#/properties/reportUnnecessaryCast",
|
||||
"$ref": "#/definitions/diagnostic",
|
||||
"title": "Controls reporting calls to 'cast' that are unnecessary",
|
||||
"default": "none"
|
||||
},
|
||||
"pythonVersion": {
|
||||
"$id": "#/properties/pythonVersion",
|
||||
"type": "string",
|
||||
|
@ -90,6 +90,8 @@ The following settings control pyright's diagnostic output (warnings or errors).
|
||||
|
||||
**reportUnnecessaryIsInstance** [boolean or string, optional]: Generate or suppress diagnostics for 'isinstance' calls where the result is statically determined to be always true or always false. Such calls are often indicative of a programming error. The default value for this setting is 'none'.
|
||||
|
||||
**reportUnnecessaryCast** [boolean or string, optional]: Generate or suppress diagnostics for 'cast' calls that are statically determined to be unnecessary. Such calls are sometimes indicative of a programming error. 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.
|
||||
|
@ -1576,6 +1576,20 @@ export class ExpressionEvaluator {
|
||||
const functionType = this._findOverloadedFunctionType(errorNode, argList, callType);
|
||||
|
||||
if (functionType) {
|
||||
if (functionType.getBuiltInName() === 'cast' && argList.length === 2) {
|
||||
// Verify that the cast is necessary.
|
||||
const castToType = argList[0].type;
|
||||
const castFromType = argList[1].type;
|
||||
if (castToType instanceof ClassType && castFromType instanceof ObjectType) {
|
||||
if (castToType.isSame(castFromType.getClassType())) {
|
||||
this._addDiagnostic(
|
||||
this._fileInfo.diagnosticSettings.reportUnnecessaryCast,
|
||||
`Unnecessary call to cast: type is already ${ castFromType.asString() }`,
|
||||
errorNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = this._validateCallArguments(errorNode, argList, callType,
|
||||
new TypeVarMap(), specializeReturnType);
|
||||
if (!type) {
|
||||
|
@ -135,6 +135,10 @@ export interface DiagnosticSettings {
|
||||
// Report calls to isinstance that are statically determined
|
||||
// to always be true or false.
|
||||
reportUnnecessaryIsInstance: DiagnosticLevel;
|
||||
|
||||
// Report calls to cast that are statically determined
|
||||
// to always unnecessary.
|
||||
reportUnnecessaryCast: DiagnosticLevel;
|
||||
}
|
||||
|
||||
export function cloneDiagnosticSettings(
|
||||
@ -180,7 +184,8 @@ export function getDiagLevelSettings() {
|
||||
'reportUnknownVariableType',
|
||||
'reportUnknownMemberType',
|
||||
'reportCallInDefaultInitializer',
|
||||
'reportUnnecessaryIsInstance'
|
||||
'reportUnnecessaryIsInstance',
|
||||
'reportUnnecessaryCast'
|
||||
];
|
||||
}
|
||||
|
||||
@ -215,7 +220,8 @@ export function getStrictDiagnosticSettings(): DiagnosticSettings {
|
||||
reportUnknownVariableType: 'error',
|
||||
reportUnknownMemberType: 'error',
|
||||
reportCallInDefaultInitializer: 'none',
|
||||
reportUnnecessaryIsInstance: 'error'
|
||||
reportUnnecessaryIsInstance: 'error',
|
||||
reportUnnecessaryCast: 'error'
|
||||
};
|
||||
|
||||
return diagSettings;
|
||||
@ -252,7 +258,8 @@ export function getDefaultDiagnosticSettings(): DiagnosticSettings {
|
||||
reportUnknownVariableType: 'none',
|
||||
reportUnknownMemberType: 'none',
|
||||
reportCallInDefaultInitializer: 'none',
|
||||
reportUnnecessaryIsInstance: 'none'
|
||||
reportUnnecessaryIsInstance: 'none',
|
||||
reportUnnecessaryCast: 'none'
|
||||
};
|
||||
|
||||
return diagSettings;
|
||||
@ -581,7 +588,12 @@ export class ConfigOptions {
|
||||
// Read the "reportUnnecessaryIsInstance" entry.
|
||||
reportUnnecessaryIsInstance: this._convertDiagnosticLevel(
|
||||
configObj.reportUnnecessaryIsInstance, 'reportUnnecessaryIsInstance',
|
||||
defaultSettings.reportUnnecessaryIsInstance)
|
||||
defaultSettings.reportUnnecessaryIsInstance),
|
||||
|
||||
// Read the "reportUnnecessaryCast" entry.
|
||||
reportUnnecessaryCast: this._convertDiagnosticLevel(
|
||||
configObj.reportUnnecessaryCast, 'reportUnnecessaryCast',
|
||||
defaultSettings.reportUnnecessaryCast)
|
||||
};
|
||||
|
||||
// Read the "venvPath".
|
||||
|
15
server/src/tests/samples/unnecessaryCast1.py
Normal file
15
server/src/tests/samples/unnecessaryCast1.py
Normal file
@ -0,0 +1,15 @@
|
||||
# This sample tests the type checker's reoprtUnnecessaryCast feature.
|
||||
|
||||
from typing import cast, Union
|
||||
|
||||
a = 3
|
||||
# This should generate an error if
|
||||
# reportUnneessaryCast is enabled.
|
||||
b = cast(int, a)
|
||||
|
||||
c: Union[int, str] = 'hello'
|
||||
d = cast(int, c)
|
||||
|
||||
|
||||
|
||||
|
@ -547,3 +547,15 @@ test('Unbound1', () => {
|
||||
|
||||
validateResults(analysisResults, 1);
|
||||
});
|
||||
|
||||
test('UnnecessaryCast', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryCast1.py'], configOptions);
|
||||
validateResults(analysisResults, 0);
|
||||
|
||||
// Turn on errors.
|
||||
configOptions.diagnosticSettings.reportUnnecessaryCast = 'error';
|
||||
analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryCast1.py'], configOptions);
|
||||
validateResults(analysisResults, 1);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user