Added new diagnostic rule "reportInvalidStubStatement" (on by default in strict mode, off otherwise) that reports diagnostics for statements that should not appear within a type stub file.

This commit is contained in:
Eric Traut 2020-08-04 22:02:56 -07:00
parent 77cd54e4d2
commit 109e2dfd71
7 changed files with 103 additions and 0 deletions

View File

@ -345,6 +345,12 @@
"title": "Controls reporting usage of implicit concatenation of string literals",
"default": "warning"
},
"reportInvalidStubStatement": {
"$id": "#/properties/reportInvalidStubStatement",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of statements that have no semantic meaning within a type stub",
"default": "none"
},
"pythonVersion": {
"$id": "#/properties/pythonVersion",
"type": "string",

View File

@ -120,6 +120,8 @@ The following settings control pyrights diagnostic output (warnings or errors
**reportUnboundVariable** [boolean or string, optional]: Generate or suppress diagnostics for unbound and possibly unbound variables. The default value for this setting is 'error'.
**reportInvalidStubStatement** [boolean or string, optional]: Generate or suppress diagnostics for statements that are syntactically correct but have no purpose within a type stub file. 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

@ -770,10 +770,87 @@ export class Checker extends ParseTreeWalker {
}
}
if (!reportedUnreachable && this._fileInfo.isStubFile) {
this._validateStubStatement(statement);
}
this.walk(statement);
}
}
private _validateStubStatement(statement: StatementNode) {
switch (statement.nodeType) {
case ParseNodeType.If:
case ParseNodeType.Function:
case ParseNodeType.Class:
case ParseNodeType.Error: {
// These are allowed in a stub file.
break;
}
case ParseNodeType.While:
case ParseNodeType.For:
case ParseNodeType.Try:
case ParseNodeType.With: {
// These are not allowed.
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticRuleSet.reportInvalidStubStatement,
DiagnosticRule.reportInvalidStubStatement,
Localizer.Diagnostic.invalidStubStatement(),
statement
);
break;
}
case ParseNodeType.StatementList: {
for (const substatement of statement.statements) {
switch (substatement.nodeType) {
case ParseNodeType.Assert:
case ParseNodeType.AssignmentExpression:
case ParseNodeType.AugmentedAssignment:
case ParseNodeType.Await:
case ParseNodeType.BinaryOperation:
case ParseNodeType.Call:
case ParseNodeType.Constant:
case ParseNodeType.Del:
case ParseNodeType.Dictionary:
case ParseNodeType.Index:
case ParseNodeType.For:
case ParseNodeType.FormatString:
case ParseNodeType.Global:
case ParseNodeType.Lambda:
case ParseNodeType.List:
case ParseNodeType.MemberAccess:
case ParseNodeType.Name:
case ParseNodeType.Nonlocal:
case ParseNodeType.Number:
case ParseNodeType.Raise:
case ParseNodeType.Return:
case ParseNodeType.Set:
case ParseNodeType.Slice:
case ParseNodeType.Ternary:
case ParseNodeType.Tuple:
case ParseNodeType.Try:
case ParseNodeType.UnaryOperation:
case ParseNodeType.Unpack:
case ParseNodeType.While:
case ParseNodeType.With:
case ParseNodeType.WithItem:
case ParseNodeType.Yield:
case ParseNodeType.YieldFrom: {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticRuleSet.reportInvalidStubStatement,
DiagnosticRule.reportInvalidStubStatement,
Localizer.Diagnostic.invalidStubStatement(),
substatement
);
}
}
}
}
}
}
private _validateExceptionType(exceptionType: Type, errorNode: ParseNode) {
const baseExceptionType = this._evaluator.getBuiltInType(errorNode, 'BaseException');
const derivesFromBaseException = (classType: ClassType) => {

View File

@ -196,6 +196,10 @@ export interface DiagnosticRuleSet {
// Report usage of unbound or possibly unbound variables.
reportUnboundVariable: DiagnosticLevel;
// Report statements that are syntactically correct but
// have no semantic meaning within a type stub file.
reportInvalidStubStatement: DiagnosticLevel;
}
export function cloneDiagnosticRuleSet(diagSettings: DiagnosticRuleSet): DiagnosticRuleSet {
@ -256,6 +260,7 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportImplicitStringConcatenation,
DiagnosticRule.reportUndefinedVariable,
DiagnosticRule.reportUnboundVariable,
DiagnosticRule.reportInvalidStubStatement,
];
}
@ -313,6 +318,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportImplicitStringConcatenation: 'none',
reportUnboundVariable: 'warning',
reportUndefinedVariable: 'warning',
reportInvalidStubStatement: 'none',
};
return diagSettings;
@ -366,6 +372,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportImplicitStringConcatenation: 'none',
reportUnboundVariable: 'error',
reportUndefinedVariable: 'error',
reportInvalidStubStatement: 'none',
};
return diagSettings;
@ -419,6 +426,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportImplicitStringConcatenation: 'none',
reportUnboundVariable: 'error',
reportUndefinedVariable: 'error',
reportInvalidStubStatement: 'error',
};
return diagSettings;
@ -975,6 +983,13 @@ export class ConfigOptions {
DiagnosticRule.reportUnboundVariable,
defaultSettings.reportUnboundVariable
),
// Read the "reportInvalidStubStatement" entry.
reportInvalidStubStatement: this._convertDiagnosticLevel(
configObj.reportInvalidStubStatement,
DiagnosticRule.reportInvalidStubStatement,
defaultSettings.reportInvalidStubStatement
),
};
// Read the "venvPath".

View File

@ -52,4 +52,5 @@ export const enum DiagnosticRule {
reportImplicitStringConcatenation = 'reportImplicitStringConcatenation',
reportUndefinedVariable = 'reportUndefinedVariable',
reportUnboundVariable = 'reportUnboundVariable',
reportInvalidStubStatement = 'reportInvalidStubStatement',
}

View File

@ -329,6 +329,7 @@ export namespace Localizer {
export const initSubclassClsParam = () => getRawString('Diagnostic.initSubclassClsParam');
export const instanceMethodSelfParam = () => getRawString('Diagnostic.instanceMethodSelfParam');
export const invalidIdentifierChar = () => getRawString('Diagnostic.invalidIdentifierChar');
export const invalidStubStatement = () => getRawString('Diagnostic.invalidStubStatement');
export const invalidTokenChars = () =>
new ParameterizedString<{ text: string }>(getRawString('Diagnostic.invalidTokenChars'));
export const isInstanceInvalidType = () =>

View File

@ -136,6 +136,7 @@
"initSubclassClsParam": "__init_subclass__ override should take a \"cls\" parameter",
"instanceMethodSelfParam": "Instance methods should take a \"self\" parameter",
"invalidIdentifierChar": "Invalid character in identifier",
"invalidStubStatement": "Statement is meaningless within a type stub file",
"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",