Added support for new reportTypeCommentUsage diagnostic check. It flag the usage of # type: xxx comments for functions and variables. These are still supported for backward compatibility, but they are increasingly irrelevant and will likely be deprecated in the next few years.

This commit is contained in:
Eric Traut 2022-06-09 21:00:47 -07:00
parent c5153a557c
commit 484faf7ba3
13 changed files with 112 additions and 1 deletions

View File

@ -104,6 +104,8 @@ The following settings control pyrights diagnostic output (warnings or errors
**reportPrivateUsage** [boolean or string, optional]: Generate or suppress diagnostics for incorrect usage of private or protected variables or functions. Protected class members begin with a single underscore (“_”) and can be accessed only by subclasses. Private class members begin with a double underscore but do not end in a double underscore and can be accessed only within the declaring class. Variables and functions declared outside of a class are considered private if their names start with either a single or double underscore, and they cannot be accessed outside of the declaring module. The default value for this setting is 'none'.
**reportTypeCommentUsage** [boolean or string, optional]: Prior to Python 3.5, the grammar did not support type annotations, so types needed to be specified using “type comments”. Python 3.5 eliminated the need for function type comments, and Python 3.6 eliminated the need for variable type comments. Future versions of Python will likely deprecate all support for type comments. If enabled, this check will flag any type comment usage unless it is required for compatibility with the specified language version. The default value for this setting is 'none'.
**reportPrivateImportUsage** [boolean or string, optional]: Generate or suppress diagnostics for use of a symbol from a "py.typed" module that is not meant to be exported from that module. The default value for this setting is 'error'.
**reportConstantRedefinition** [boolean or string, optional]: Generate or suppress diagnostics for attempts to redefine variables whose names are all-caps with underscores and numerals. The default value for this setting is 'none'.
@ -326,6 +328,7 @@ The following table lists the default severity levels for each diagnostic rule w
| reportMissingTypeArgument | "none" | "none" | "error" |
| reportOverlappingOverload | "none" | "none" | "error" |
| reportPrivateUsage | "none" | "none" | "error" |
| reportTypeCommentUsage | "none" | "none" | "error" |
| reportUnknownArgumentType | "none" | "none" | "error" |
| reportUnknownLambdaType | "none" | "none" | "error" |
| reportUnknownMemberType | "none" | "none" | "error" |

View File

@ -562,6 +562,18 @@ export class Checker extends ParseTreeWalker {
if (node.functionAnnotationComment) {
this.walk(node.functionAnnotationComment);
if (
this._fileInfo.diagnosticRuleSet.reportTypeCommentUsage !== 'none' &&
this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V3_5
) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticRuleSet.reportTypeCommentUsage,
DiagnosticRule.reportTypeCommentUsage,
Localizer.Diagnostic.typeCommentDeprecated(),
node.functionAnnotationComment
);
}
}
this.walkMultiple(node.decorators);
@ -1055,6 +1067,18 @@ export class Checker extends ParseTreeWalker {
this._evaluator.evaluateTypesForStatement(node);
if (node.typeAnnotationComment) {
this._evaluator.getType(node.typeAnnotationComment);
if (
this._fileInfo.diagnosticRuleSet.reportTypeCommentUsage !== 'none' &&
this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V3_6
) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticRuleSet.reportTypeCommentUsage,
DiagnosticRule.reportTypeCommentUsage,
Localizer.Diagnostic.typeCommentDeprecated(),
node.typeAnnotationComment
);
}
}
return true;

View File

@ -174,6 +174,9 @@ export interface DiagnosticRuleSet {
// the owning class or module?
reportPrivateUsage: DiagnosticLevel;
// Report usage of deprecated type comments.
reportTypeCommentUsage: DiagnosticLevel;
// Report usage of an import from a py.typed module that is
// not meant to be re-exported from that module.
reportPrivateImportUsage: DiagnosticLevel;
@ -345,6 +348,7 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportUntypedBaseClass,
DiagnosticRule.reportUntypedNamedTuple,
DiagnosticRule.reportPrivateUsage,
DiagnosticRule.reportTypeCommentUsage,
DiagnosticRule.reportPrivateImportUsage,
DiagnosticRule.reportConstantRedefinition,
DiagnosticRule.reportIncompatibleMethodOverride,
@ -425,6 +429,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportUntypedBaseClass: 'none',
reportUntypedNamedTuple: 'none',
reportPrivateUsage: 'none',
reportTypeCommentUsage: 'none',
reportPrivateImportUsage: 'none',
reportConstantRedefinition: 'none',
reportIncompatibleMethodOverride: 'none',
@ -501,6 +506,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportUntypedBaseClass: 'none',
reportUntypedNamedTuple: 'none',
reportPrivateUsage: 'none',
reportTypeCommentUsage: 'none',
reportPrivateImportUsage: 'error',
reportConstantRedefinition: 'none',
reportIncompatibleMethodOverride: 'none',
@ -577,6 +583,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportUntypedBaseClass: 'error',
reportUntypedNamedTuple: 'error',
reportPrivateUsage: 'error',
reportTypeCommentUsage: 'error',
reportPrivateImportUsage: 'error',
reportConstantRedefinition: 'error',
reportIncompatibleMethodOverride: 'error',

View File

@ -42,6 +42,7 @@ export enum DiagnosticRule {
reportUntypedBaseClass = 'reportUntypedBaseClass',
reportUntypedNamedTuple = 'reportUntypedNamedTuple',
reportPrivateUsage = 'reportPrivateUsage',
reportTypeCommentUsage = 'reportTypeCommentUsage',
reportPrivateImportUsage = 'reportPrivateImportUsage',
reportConstantRedefinition = 'reportConstantRedefinition',
reportIncompatibleMethodOverride = 'reportIncompatibleMethodOverride',

View File

@ -789,6 +789,7 @@ export namespace Localizer {
getRawString('Diagnostic.typeAssignmentMismatch')
);
export const typeCallNotAllowed = () => getRawString('Diagnostic.typeCallNotAllowed');
export const typeCommentDeprecated = () => getRawString('Diagnostic.typeCommentDeprecated');
export const typedDictAccess = () => getRawString('Diagnostic.typedDictAccess');
export const typedDictBadVar = () => getRawString('Diagnostic.typedDictBadVar');
export const typedDictBaseClass = () => getRawString('Diagnostic.typedDictBaseClass');

View File

@ -390,6 +390,7 @@
"typeArgsTooMany": "Too many type arguments provided for \"{name}\"; expected {expected} but received {received}",
"typeAssignmentMismatch": "Expression of type \"{sourceType}\" cannot be assigned to declared type \"{destType}\"",
"typeCallNotAllowed": "type() call should not be used in type annotation",
"typeCommentDeprecated": "Use of type comments is deprecated; use type annotation instead",
"typedDictAccess": "Could not access item in TypedDict",
"typedDictBadVar": "TypedDict classes can contain only type annotations",
"typedDictBaseClass": "All base classes for TypedDict classes must also be TypedDict classes",

View File

@ -0,0 +1,13 @@
# This sample tests the reportTypeCommentUsage diagnostic check.
# This should generate an error if reportTypeCommentUsage is enabled.
x = 3 # type: int
class Foo:
# This should generate an error if reportTypeCommentUsage is enabled.
y = 0 # type: int
def __init__(self):
# This should generate an error if reportTypeCommentUsage is enabled.
self.x = 2 # type: int

View File

@ -1,6 +1,6 @@
# This sample tests support for comment-style function annotations.
# pyright: strict, reportMissingParameterType=false
# pyright: strict, reportMissingParameterType=false, reportTypeCommentUsage=false
from typing import Optional, Literal as _Literal, Union

View File

@ -0,0 +1,22 @@
# This sample tests the reportTypeCommentUsage diagnostic check.
from typing import Optional
# This should generate an error if reportTypeCommentUsage is enabled.
def func1a(a, b):
# type: (int, str) -> str
return ""
# This should generate an error if reportTypeCommentUsage is enabled.
def func1b(a, b): # type: (Optional[str], int) -> str
return ""
# This should generate an error if reportTypeCommentUsage is enabled.
def func1c(
a, # type: int
b, # type: str
):
# type: (...) -> str
return ""

View File

@ -800,6 +800,17 @@ test('AnnotatedVar6', () => {
TestUtils.validateResults(analysisResults, 0);
});
test('AnnotatedVar7', () => {
const configOptions = new ConfigOptions('.');
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['annotatedVar7.py'], configOptions);
TestUtils.validateResults(analysisResults1, 0);
configOptions.diagnosticRuleSet.reportTypeCommentUsage = 'error';
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['annotatedVar7.py'], configOptions);
TestUtils.validateResults(analysisResults2, 3);
});
test('CodeFlow1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['codeFlow1.py']);

View File

@ -1159,6 +1159,17 @@ test('FunctionAnnotation3', () => {
TestUtils.validateResults(analysisResults, 2);
});
test('FunctionAnnotation4', () => {
const configOptions = new ConfigOptions('.');
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['functionAnnotation4.py'], configOptions);
TestUtils.validateResults(analysisResults1, 0);
configOptions.diagnosticRuleSet.reportTypeCommentUsage = 'error';
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['functionAnnotation4.py'], configOptions);
TestUtils.validateResults(analysisResults2, 3);
});
test('Subscript1', () => {
const configOptions = new ConfigOptions('.');

View File

@ -388,6 +388,17 @@
"error"
]
},
"reportTypeCommentUsage": {
"type": "string",
"description": "Diagnostics for usage of deprecated type comments.",
"default": "none",
"enum": [
"none",
"information",
"warning",
"error"
]
},
"reportPrivateImportUsage": {
"type": "string",
"description": "Diagnostics for incorrect usage of symbol imported from a \"py.typed\" module that is not re-exported from that module.",

View File

@ -292,6 +292,12 @@
"title": "Controls reporting of private variables and functions used outside of the owning class or module and usage of protected members outside of subclasses",
"default": "none"
},
"reportTypeCommentUsage": {
"$id": "#/properties/reportTypeCommentUsage",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of deprecated type comment usage",
"default": "none"
},
"reportPrivateImportUsage": {
"$id": "#/properties/reportPrivateImportUsage",
"$ref": "#/definitions/diagnostic",