Implemented reportUnnecessaryCast feature.

This commit is contained in:
Eric Traut 2019-09-05 17:40:25 +00:00
parent 7e35e4ef1f
commit 797c790a42
6 changed files with 65 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View 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)

View File

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