Added new pyright.typeCheckingMode setting for VS Code extension that allows you to set the default rule set for type checking. Three values are supported: "off", "basic", and "strict". This setting can be overridden in the pyrightconfig.json file with the typeCheckingMode config key.

This commit is contained in:
Eric Traut 2020-04-08 00:44:56 -07:00
parent 308f906200
commit fa34df5306
22 changed files with 319 additions and 211 deletions

View File

@ -104,6 +104,17 @@
"default": false,
"description": "Use library implementations to extract type information when type stub is not present.",
"scope": "resource"
},
"pyright.typeCheckingMode": {
"type": "string",
"default": "basic",
"enum": [
"off",
"basic",
"strict"
],
"description": "Defines the default rule set for type checking.",
"scope": "resource"
}
}
},

View File

@ -64,6 +64,17 @@
"pattern": "^(.*)$"
}
},
"typeCheckingMode": {
"$id": "#/properties/typeCheckingMode",
"type": "string",
"enum": [
"off",
"basic",
"strict"
],
"title": "Specifies the default rule set to use for type checking",
"default": "basic"
},
"typeshedPath": {
"$id": "#/properties/typeshedPath",
"type": "string",

View File

@ -30,6 +30,8 @@ Relative paths specified within the config file are relative to the config file
**executionEnvironments** [array of objects, optional]: Specifies a list of execution environments (see below). Execution environments are searched from start to finish by comparing the path of a source file with the root path specified in the execution environment.
**typeCheckingMode** ["off", "basic", "strict"]: Specifies the default rule set to use. Some rules can be overridden using additional configuration flags documented below. THe default value for this setting is "basic". If set to "off", all type-checking rules are disabled, but Python syntax and semantic errors are still reported.
## Type Check Diagnostics Settings
The following settings control pyrights diagnostic output (warnings or errors). Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, or `"error"` can be used to specify the diagnostic level.

View File

@ -6,6 +6,8 @@ The Pyright VS Code extension honors the following settings.
**pyright.openFilesOnly** [boolean]: Determines whether pyright analyzes (and reports errors for) all files in the workspace, as indicated by the config file. If this option is set to true, pyright analyzes only open files.
**pyright.typeCheckingMode** ["off", "basic", "strict"]: Determines the default type-checking level used by pyright. This can be overridden in the configuration file.
**pyright.useLibraryCodeForTypes** [boolean]: Determines whether pyright reads, parses and analyzes library code to extract type information in the absence of type stub files. This can add significant overhead and may result in poor-quality type information. The default value for this option is false.
**python.analysis.typeshedPaths** [array of paths]: Paths to look for typeshed modules. Pyright currently honors only the first path in the array.

View File

@ -8,7 +8,7 @@
* by the binder and checker.
*/
import { DiagnosticSettings, ExecutionEnvironment } from '../common/configOptions';
import { DiagnosticRuleSet, ExecutionEnvironment } from '../common/configOptions';
import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
import { TextRange } from '../common/textRange';
import { TextRangeCollection } from '../common/textRangeCollection';
@ -31,7 +31,7 @@ export interface AnalyzerFileInfo {
collectionsModulePath?: string;
diagnosticSink: TextRangeDiagnosticSink;
executionEnvironment: ExecutionEnvironment;
diagnosticSettings: DiagnosticSettings;
diagnosticRuleSet: DiagnosticRuleSet;
fileContents: string;
lines: TextRangeCollection<TextRange>;
filePath: string;

View File

@ -257,7 +257,7 @@ export class Binder extends ParseTreeWalker {
if (importResult) {
if (!importResult.isImportFound) {
this._addDiagnostic(
this._fileInfo.diagnosticSettings.reportMissingImports,
this._fileInfo.diagnosticRuleSet.reportMissingImports,
DiagnosticRule.reportMissingImports,
`Import "${importResult.importName}" could not be resolved`,
node
@ -265,7 +265,7 @@ export class Binder extends ParseTreeWalker {
} else if (importResult.importType === ImportType.ThirdParty) {
if (!importResult.isStubFile) {
const diagnostic = this._addDiagnostic(
this._fileInfo.diagnosticSettings.reportMissingTypeStubs,
this._fileInfo.diagnosticRuleSet.reportMissingTypeStubs,
DiagnosticRule.reportMissingTypeStubs,
`Stub file not found for "${importResult.importName}"`,
node
@ -991,7 +991,7 @@ export class Binder extends ParseTreeWalker {
if (error.errorType === StringTokenUtils.UnescapeErrorType.InvalidEscapeSequence) {
this._addDiagnostic(
this._fileInfo.diagnosticSettings.reportInvalidStringEscapeSequence,
this._fileInfo.diagnosticRuleSet.reportInvalidStringEscapeSequence,
DiagnosticRule.reportInvalidStringEscapeSequence,
'Unsupported escape sequence in string literal',
textRange
@ -2070,7 +2070,7 @@ export class Binder extends ParseTreeWalker {
let symbol = memberAccessInfo.classScope.lookUpSymbol(name.value);
if (!symbol) {
symbol = memberAccessInfo.classScope.addSymbol(name.value, SymbolFlags.InitiallyUnbound);
const honorPrivateNaming = this._fileInfo.diagnosticSettings.reportPrivateUsage !== 'none';
const honorPrivateNaming = this._fileInfo.diagnosticRuleSet.reportPrivateUsage !== 'none';
if (isPrivateOrProtectedName(name.value) && honorPrivateNaming) {
symbol.setIsPrivateMember();
}
@ -2166,7 +2166,7 @@ export class Binder extends ParseTreeWalker {
let symbol = memberAccessInfo.classScope.lookUpSymbol(name.value);
if (!symbol) {
symbol = memberAccessInfo.classScope.addSymbol(name.value, SymbolFlags.InitiallyUnbound);
const honorPrivateNaming = this._fileInfo.diagnosticSettings.reportPrivateUsage !== 'none';
const honorPrivateNaming = this._fileInfo.diagnosticRuleSet.reportPrivateUsage !== 'none';
if (isPrivateOrProtectedName(name.value) && honorPrivateNaming) {
symbol.setIsPrivateMember();
}

View File

@ -166,7 +166,7 @@ export class Checker extends ParseTreeWalker {
const paramType = functionTypeResult.functionType.details.parameters[index].type;
if (paramType.category === TypeCategory.Unknown) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownParameterType,
this._fileInfo.diagnosticRuleSet.reportUnknownParameterType,
DiagnosticRule.reportUnknownParameterType,
`Type of parameter "${param.name.value}" is unknown`,
param.name
@ -175,7 +175,7 @@ export class Checker extends ParseTreeWalker {
const diagAddendum = new DiagnosticAddendum();
diagAddendum.addMessage(`Parameter type is "${this._evaluator.printType(paramType)}"`);
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownParameterType,
this._fileInfo.diagnosticRuleSet.reportUnknownParameterType,
DiagnosticRule.reportUnknownParameterType,
`Type of parameter "${param.name.value}" is partially unknown` + diagAddendum.getString(),
param.name
@ -235,14 +235,14 @@ export class Checker extends ParseTreeWalker {
if (paramType) {
if (paramType.category === TypeCategory.Unknown) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownLambdaType,
this._fileInfo.diagnosticRuleSet.reportUnknownLambdaType,
DiagnosticRule.reportUnknownLambdaType,
`Type of "${param.name.value}" is unknown`,
param.name
);
} else if (containsUnknown(paramType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownLambdaType,
this._fileInfo.diagnosticRuleSet.reportUnknownLambdaType,
DiagnosticRule.reportUnknownLambdaType,
`Type of "${param.name.value}", ` +
`"${this._evaluator.printType(paramType)}", is partially unknown`,
@ -257,14 +257,14 @@ export class Checker extends ParseTreeWalker {
if (returnType) {
if (returnType.category === TypeCategory.Unknown) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownLambdaType,
this._fileInfo.diagnosticRuleSet.reportUnknownLambdaType,
DiagnosticRule.reportUnknownLambdaType,
`Return type of lambda is unknown`,
node.expression
);
} else if (containsUnknown(returnType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownLambdaType,
this._fileInfo.diagnosticRuleSet.reportUnknownLambdaType,
DiagnosticRule.reportUnknownLambdaType,
`Return type of lambda, "${this._evaluator.printType(returnType)}", is partially unknown`,
node.expression
@ -284,7 +284,7 @@ export class Checker extends ParseTreeWalker {
if (ParseTreeUtils.isWithinDefaultParamInitializer(node) && !this._fileInfo.isStubFile) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportCallInDefaultInitializer,
this._fileInfo.diagnosticRuleSet.reportCallInDefaultInitializer,
DiagnosticRule.reportCallInDefaultInitializer,
`Function calls within default value initializer are not permitted`,
node
@ -341,7 +341,7 @@ export class Checker extends ParseTreeWalker {
if (declaredReturnType) {
if (isNoReturnType(declaredReturnType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Function with declared return type "NoReturn" cannot include a return statement`,
node
@ -354,7 +354,7 @@ export class Checker extends ParseTreeWalker {
const specializedDeclaredType = specializeType(declaredReturnType, undefined);
if (!this._evaluator.canAssignType(specializedDeclaredType, returnType, diagAddendum)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expression of type "${this._evaluator.printType(returnType)}" cannot be assigned ` +
`to return type "${this._evaluator.printType(specializedDeclaredType)}"` +
@ -367,14 +367,14 @@ export class Checker extends ParseTreeWalker {
if (returnType.category === TypeCategory.Unknown) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownVariableType,
this._fileInfo.diagnosticRuleSet.reportUnknownVariableType,
DiagnosticRule.reportUnknownVariableType,
`Return type is unknown`,
node.returnExpression!
);
} else if (containsUnknown(returnType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownVariableType,
this._fileInfo.diagnosticRuleSet.reportUnknownVariableType,
DiagnosticRule.reportUnknownVariableType,
`Return type, "${this._evaluator.printType(returnType)}", is partially unknown`,
node.returnExpression!
@ -447,7 +447,7 @@ export class Checker extends ParseTreeWalker {
if (diagAddendum.getMessageCount() > 0) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expected exception class or object` + diagAddendum.getString(),
node.typeExpression
@ -517,7 +517,7 @@ export class Checker extends ParseTreeWalker {
if (type.classType.typeArguments.length > 0) {
this._evaluator.addDiagnosticForTextRange(
this._fileInfo,
this._fileInfo.diagnosticSettings.reportAssertAlwaysTrue,
this._fileInfo.diagnosticRuleSet.reportAssertAlwaysTrue,
DiagnosticRule.reportAssertAlwaysTrue,
`Assert expression always evaluates to true`,
node.testExpression
@ -591,7 +591,7 @@ export class Checker extends ParseTreeWalker {
if (node.strings.length > 1) {
this._evaluator.addDiagnosticForTextRange(
this._fileInfo,
this._fileInfo.diagnosticSettings.reportImplicitStringConcatenation,
this._fileInfo.diagnosticRuleSet.reportImplicitStringConcatenation,
DiagnosticRule.reportImplicitStringConcatenation,
`Implicit string concatenation not allowed`,
node
@ -834,7 +834,7 @@ export class Checker extends ParseTreeWalker {
for (const otherDecl of otherDecls) {
if (otherDecl.type === DeclarationType.Class) {
const diag = this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Class declaration "${name}" is obscured by a ${primaryDeclType}declaration of the same name`,
otherDecl.node.name
@ -842,7 +842,7 @@ export class Checker extends ParseTreeWalker {
addPrimaryDeclInfo(diag);
} else if (otherDecl.type === DeclarationType.Function) {
const diag = this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Function declaration "${name}" is obscured by a ${primaryDeclType}declaration of the same name`,
otherDecl.node.name
@ -851,7 +851,7 @@ export class Checker extends ParseTreeWalker {
} else if (otherDecl.type === DeclarationType.Parameter) {
if (otherDecl.node.name) {
const diag = this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Parameter "${name}" is obscured by a ${primaryDeclType}declaration of the same name`,
otherDecl.node.name
@ -876,7 +876,7 @@ export class Checker extends ParseTreeWalker {
if (!duplicateIsOk) {
const diag = this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Declared type for "${name}" is obscured by an incompatible ${primaryDeclType}declaration`,
otherDecl.node
@ -887,7 +887,7 @@ export class Checker extends ParseTreeWalker {
} else if (primaryType && !isProperty(primaryType)) {
if (primaryDecl.type === DeclarationType.Function || primaryDecl.type === DeclarationType.Class) {
const diag = this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Declared ${primaryDeclType}already exists for "${name}"`,
otherDecl.node
@ -929,7 +929,7 @@ export class Checker extends ParseTreeWalker {
switch (decl.type) {
case DeclarationType.Alias:
diagnosticLevel = this._fileInfo.diagnosticSettings.reportUnusedImport;
diagnosticLevel = this._fileInfo.diagnosticRuleSet.reportUnusedImport;
rule = DiagnosticRule.reportUnusedImport;
if (decl.node.nodeType === ParseNodeType.ImportAs) {
if (decl.node.alias) {
@ -948,7 +948,7 @@ export class Checker extends ParseTreeWalker {
this._evaluator.addDiagnosticForTextRange(
this._fileInfo,
this._fileInfo.diagnosticSettings.reportUnusedImport,
this._fileInfo.diagnosticRuleSet.reportUnusedImport,
DiagnosticRule.reportUnusedImport,
`Import "${multipartName}" is not accessed`,
textRange
@ -980,7 +980,7 @@ export class Checker extends ParseTreeWalker {
if (!isPrivate) {
return;
}
diagnosticLevel = this._fileInfo.diagnosticSettings.reportUnusedVariable;
diagnosticLevel = this._fileInfo.diagnosticRuleSet.reportUnusedVariable;
if (decl.node.nodeType === ParseNodeType.Name) {
nameNode = decl.node;
rule = DiagnosticRule.reportUnusedVariable;
@ -992,7 +992,7 @@ export class Checker extends ParseTreeWalker {
if (!isPrivate) {
return;
}
diagnosticLevel = this._fileInfo.diagnosticSettings.reportUnusedClass;
diagnosticLevel = this._fileInfo.diagnosticRuleSet.reportUnusedClass;
nameNode = decl.node.name;
rule = DiagnosticRule.reportUnusedClass;
message = `Class "${nameNode.value}" is not accessed`;
@ -1002,7 +1002,7 @@ export class Checker extends ParseTreeWalker {
if (!isPrivate) {
return;
}
diagnosticLevel = this._fileInfo.diagnosticSettings.reportUnusedFunction;
diagnosticLevel = this._fileInfo.diagnosticRuleSet.reportUnusedFunction;
nameNode = decl.node.name;
rule = DiagnosticRule.reportUnusedFunction;
message = `Function "${nameNode.value}" is not accessed`;
@ -1169,7 +1169,7 @@ export class Checker extends ParseTreeWalker {
const callType = isInstanceCheck ? 'instance' : 'subclass';
if (filteredType.category === TypeCategory.Never) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnnecessaryIsInstance,
this._fileInfo.diagnosticRuleSet.reportUnnecessaryIsInstance,
DiagnosticRule.reportUnnecessaryIsInstance,
`Unnecessary ${callName} call; "${this._evaluator.printType(arg0Type)}" ` +
`is never ${callType} of "${this._evaluator.printType(getTestType())}"`,
@ -1177,7 +1177,7 @@ export class Checker extends ParseTreeWalker {
);
} else if (isTypeSame(filteredType, arg0Type)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnnecessaryIsInstance,
this._fileInfo.diagnosticRuleSet.reportUnnecessaryIsInstance,
DiagnosticRule.reportUnnecessaryIsInstance,
`Unnecessary ${callName} call; "${this._evaluator.printType(arg0Type)}" ` +
`is always ${callType} of "${this._evaluator.printType(getTestType())}"`,
@ -1208,7 +1208,7 @@ export class Checker extends ParseTreeWalker {
}
private _conditionallyReportPrivateUsage(node: NameNode) {
if (this._fileInfo.diagnosticSettings.reportPrivateUsage === 'none') {
if (this._fileInfo.diagnosticRuleSet.reportPrivateUsage === 'none') {
return;
}
@ -1293,7 +1293,7 @@ export class Checker extends ParseTreeWalker {
if (classOrModuleNode && !ParseTreeUtils.isNodeContainedWithin(node, classOrModuleNode)) {
if (isProtectedAccess) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportPrivateUsage,
this._fileInfo.diagnosticRuleSet.reportPrivateUsage,
DiagnosticRule.reportPrivateUsage,
`"${nameValue}" is protected and used outside of a derived class`,
node
@ -1302,7 +1302,7 @@ export class Checker extends ParseTreeWalker {
const scopeName = classOrModuleNode.nodeType === ParseNodeType.Class ? 'class' : 'module';
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportPrivateUsage,
this._fileInfo.diagnosticRuleSet.reportPrivateUsage,
DiagnosticRule.reportPrivateUsage,
`"${nameValue}" is private and used outside of the ${scopeName} in which it is declared`,
node
@ -1355,14 +1355,14 @@ export class Checker extends ParseTreeWalker {
if (declaredReturnType) {
if (declaredReturnType.category === TypeCategory.Unknown) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownVariableType,
this._fileInfo.diagnosticRuleSet.reportUnknownVariableType,
DiagnosticRule.reportUnknownVariableType,
`Declared return type is unknown`,
node.returnTypeAnnotation
);
} else if (containsUnknown(declaredReturnType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownVariableType,
this._fileInfo.diagnosticRuleSet.reportUnknownVariableType,
DiagnosticRule.reportUnknownVariableType,
`Declared return type, "${this._evaluator.printType(
declaredReturnType
@ -1387,7 +1387,7 @@ export class Checker extends ParseTreeWalker {
// the return type matches.
if (!ParseTreeUtils.isSuiteEmpty(node.suite)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Function with declared type of "NoReturn" cannot return "None"`,
node.returnTypeAnnotation
@ -1405,7 +1405,7 @@ export class Checker extends ParseTreeWalker {
// the return type matches.
if (!ParseTreeUtils.isSuiteEmpty(node.suite)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Function with declared type of "${this._evaluator.printType(
declaredReturnType
@ -1420,14 +1420,14 @@ export class Checker extends ParseTreeWalker {
const inferredReturnType = this._evaluator.getFunctionInferredReturnType(functionType);
if (inferredReturnType.category === TypeCategory.Unknown) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownParameterType,
this._fileInfo.diagnosticRuleSet.reportUnknownParameterType,
DiagnosticRule.reportUnknownParameterType,
`Inferred return type is unknown`,
node.name
);
} else if (containsUnknown(inferredReturnType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportUnknownParameterType,
this._fileInfo.diagnosticRuleSet.reportUnknownParameterType,
DiagnosticRule.reportUnknownParameterType,
`Return type "${this._evaluator.printType(inferredReturnType)}" is partially unknown`,
node.name
@ -1486,7 +1486,7 @@ export class Checker extends ParseTreeWalker {
const decl = getLastTypedDeclaredForSymbol(symbol);
if (decl && decl.type === DeclarationType.Function) {
const diag = this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportIncompatibleMethodOverride,
this._fileInfo.diagnosticRuleSet.reportIncompatibleMethodOverride,
DiagnosticRule.reportIncompatibleMethodOverride,
`Method "${name}" overrides class "${baseClassAndSymbol.classType.details.name}" ` +
`in an incompatible manner` +
@ -1535,7 +1535,7 @@ export class Checker extends ParseTreeWalker {
(node.parameters[0].name.value !== 'cls' && node.parameters[0].name.value !== 'mcs')
) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportSelfClsParameterName,
this._fileInfo.diagnosticRuleSet.reportSelfClsParameterName,
DiagnosticRule.reportSelfClsParameterName,
`The __new__ override should take a "cls" parameter`,
node.parameters.length > 0 ? node.parameters[0] : node.name
@ -1545,7 +1545,7 @@ export class Checker extends ParseTreeWalker {
// __init_subclass__ overrides should have a "cls" parameter.
if (node.parameters.length === 0 || !node.parameters[0].name || node.parameters[0].name.value !== 'cls') {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportSelfClsParameterName,
this._fileInfo.diagnosticRuleSet.reportSelfClsParameterName,
DiagnosticRule.reportSelfClsParameterName,
`The __init_subclass__ override should take a "cls" parameter`,
node.parameters.length > 0 ? node.parameters[0] : node.name
@ -1557,7 +1557,7 @@ export class Checker extends ParseTreeWalker {
const paramName = node.parameters[0].name.value;
if (paramName === 'self' || paramName === 'cls') {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportSelfClsParameterName,
this._fileInfo.diagnosticRuleSet.reportSelfClsParameterName,
DiagnosticRule.reportSelfClsParameterName,
`Static methods should not take a "self" or "cls" parameter`,
node.parameters[0].name
@ -1575,7 +1575,7 @@ export class Checker extends ParseTreeWalker {
if (paramName !== 'cls') {
if (!this._fileInfo.isStubFile || (!paramName.startsWith('_') && paramName !== 'metacls')) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportSelfClsParameterName,
this._fileInfo.diagnosticRuleSet.reportSelfClsParameterName,
DiagnosticRule.reportSelfClsParameterName,
`Class methods should take a "cls" parameter`,
node.parameters.length > 0 ? node.parameters[0] : node.name
@ -1619,7 +1619,7 @@ export class Checker extends ParseTreeWalker {
if (!isLegalMetaclassName) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportSelfClsParameterName,
this._fileInfo.diagnosticRuleSet.reportSelfClsParameterName,
DiagnosticRule.reportSelfClsParameterName,
`Instance methods should take a "self" parameter`,
node.parameters.length > 0 ? node.parameters[0] : node.name
@ -1647,7 +1647,7 @@ export class Checker extends ParseTreeWalker {
if (declaredYieldType) {
if (isNoReturnType(declaredYieldType)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Function with declared return type "NoReturn" cannot include a yield statement`,
node
@ -1656,7 +1656,7 @@ export class Checker extends ParseTreeWalker {
const diagAddendum = new DiagnosticAddendum();
if (!this._evaluator.canAssignType(declaredYieldType, adjustedYieldType, diagAddendum)) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expression of type "${this._evaluator.printType(adjustedYieldType)}" cannot be assigned ` +
`to yield type "${this._evaluator.printType(declaredYieldType)}"` +
@ -1684,7 +1684,7 @@ export class Checker extends ParseTreeWalker {
const prevImport = symbolMap.get(importFromAs.name.value);
if (prevImport) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportDuplicateImport,
this._fileInfo.diagnosticRuleSet.reportDuplicateImport,
DiagnosticRule.reportDuplicateImport,
`"${importFromAs.name.value}" is imported more than once`,
importFromAs.name
@ -1700,7 +1700,7 @@ export class Checker extends ParseTreeWalker {
const prevImport = importModuleMap.get(importStatement.moduleName);
if (prevImport) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticSettings.reportDuplicateImport,
this._fileInfo.diagnosticRuleSet.reportDuplicateImport,
DiagnosticRule.reportDuplicateImport,
`"${importStatement.moduleName}" is imported more than once`,
importStatement.subnode

View File

@ -9,25 +9,25 @@
*/
import {
cloneDiagnosticSettings,
cloneDiagnosticRuleSet,
DiagnosticLevel,
DiagnosticSettings,
getBooleanDiagnosticSettings,
getDiagLevelSettings,
getStrictDiagnosticSettings,
DiagnosticRuleSet,
getBooleanDiagnosticRules,
getDiagLevelDiagnosticRules,
getStrictDiagnosticRuleSet,
} from '../common/configOptions';
import { TextRangeCollection } from '../common/textRangeCollection';
import { Token } from '../parser/tokenizerTypes';
export function getFileLevelDirectives(
tokens: TextRangeCollection<Token>,
defaultSettings: DiagnosticSettings,
defaultRuleSet: DiagnosticRuleSet,
useStrict: boolean
): DiagnosticSettings {
let settings = cloneDiagnosticSettings(defaultSettings);
): DiagnosticRuleSet {
let ruleSet = cloneDiagnosticRuleSet(defaultRuleSet);
if (useStrict) {
_applyStrictSettings(settings);
_applyStrictRules(ruleSet);
}
for (let i = 0; i < tokens.count; i++) {
@ -36,37 +36,37 @@ export function getFileLevelDirectives(
for (const comment of token.comments) {
const value = comment.value.trim();
settings = _parsePyrightComment(value, settings);
ruleSet = _parsePyrightComment(value, ruleSet);
}
}
}
return settings;
return ruleSet;
}
function _applyStrictSettings(settings: DiagnosticSettings) {
const strictSettings = getStrictDiagnosticSettings();
const boolSettingNames = getBooleanDiagnosticSettings();
const diagSettingNames = getDiagLevelSettings();
function _applyStrictRules(ruleSet: DiagnosticRuleSet) {
const strictRuleSet = getStrictDiagnosticRuleSet();
const boolRuleNames = getBooleanDiagnosticRules();
const diagRuleNames = getDiagLevelDiagnosticRules();
// Enable the strict settings as appropriate.
for (const setting of boolSettingNames) {
if ((strictSettings as any)[setting]) {
(settings as any)[setting] = true;
// Enable the strict rules as appropriate.
for (const ruleName of boolRuleNames) {
if ((strictRuleSet as any)[ruleName]) {
(ruleSet as any)[ruleName] = true;
}
}
for (const setting of diagSettingNames) {
const strictValue: DiagnosticLevel = (strictSettings as any)[setting];
const prevValue: DiagnosticLevel = (settings as any)[setting];
for (const ruleName of diagRuleNames) {
const strictValue: DiagnosticLevel = (strictRuleSet as any)[ruleName];
const prevValue: DiagnosticLevel = (ruleSet as any)[ruleName];
if (strictValue === 'error' || (strictValue === 'warning' && prevValue !== 'error')) {
(settings as any)[setting] = strictValue;
(ruleSet as any)[ruleName] = strictValue;
}
}
}
function _parsePyrightComment(commentValue: string, settings: DiagnosticSettings) {
function _parsePyrightComment(commentValue: string, ruleSet: DiagnosticRuleSet) {
// Is this a pyright or mspython-specific comment?
const validPrefixes = ['pyright:', 'mspython:'];
const prefix = validPrefixes.find((p) => commentValue.startsWith(p));
@ -75,42 +75,42 @@ function _parsePyrightComment(commentValue: string, settings: DiagnosticSettings
const operandList = operands.split(',').map((s) => s.trim());
// If it contains a "strict" operand, replace the existing
// diagnostic settings with their strict counterparts.
// diagnostic rules with their strict counterparts.
if (operandList.some((s) => s === 'strict')) {
_applyStrictSettings(settings);
_applyStrictRules(ruleSet);
}
for (const operand of operandList) {
settings = _parsePyrightOperand(operand, settings);
ruleSet = _parsePyrightOperand(operand, ruleSet);
}
}
return settings;
return ruleSet;
}
function _parsePyrightOperand(operand: string, settings: DiagnosticSettings) {
function _parsePyrightOperand(operand: string, ruleSet: DiagnosticRuleSet) {
const operandSplit = operand.split('=').map((s) => s.trim());
if (operandSplit.length !== 2) {
return settings;
return ruleSet;
}
const settingName = operandSplit[0];
const boolSettings = getBooleanDiagnosticSettings();
const diagLevelSettings = getDiagLevelSettings();
const ruleName = operandSplit[0];
const boolRules = getBooleanDiagnosticRules();
const diagLevelRules = getDiagLevelDiagnosticRules();
if (diagLevelSettings.find((s) => s === settingName)) {
if (diagLevelRules.find((r) => r === ruleName)) {
const diagLevelValue = _parseDiagLevel(operandSplit[1]);
if (diagLevelValue !== undefined) {
(settings as any)[settingName] = diagLevelValue;
(ruleSet as any)[ruleName] = diagLevelValue;
}
} else if (boolSettings.find((s) => s === settingName)) {
} else if (boolRules.find((r) => r === ruleName)) {
const boolValue = _parseBoolSetting(operandSplit[1]);
if (boolValue !== undefined) {
(settings as any)[settingName] = boolValue;
(ruleSet as any)[ruleName] = boolValue;
}
}
return settings;
return ruleSet;
}
function _parseDiagLevel(value: string): DiagnosticLevel | undefined {

View File

@ -569,7 +569,7 @@ export class Program {
// Don't bother checking third-party imports or typeshed files unless they're open.
if (
fileToCheck.isThirdPartyImport ||
(fileToCheck.isTypeshedFile && this._configOptions.diagnosticSettings.reportTypeshedErrors === 'none')
(fileToCheck.isTypeshedFile && this._configOptions.diagnosticRuleSet.reportTypeshedErrors === 'none')
) {
if (!fileToCheck.isOpenByClient) {
return false;
@ -592,7 +592,7 @@ export class Program {
fileToCheck.sourceFile.check(this._evaluator);
// Detect import cycles that involve the file.
if (this._configOptions.diagnosticSettings.reportImportCycles !== 'none') {
if (this._configOptions.diagnosticRuleSet.reportImportCycles !== 'none') {
// Don't detect import cycles when doing type stub generation. Some
// third-party modules are pretty convoluted.
if (!this._allowedThirdPartyImports) {
@ -1215,7 +1215,7 @@ export class Program {
}
});
} else if (options.verboseOutput) {
if (!sourceFileInfo.isTypeshedFile || options.diagnosticSettings.reportTypeshedErrors !== 'none') {
if (!sourceFileInfo.isTypeshedFile || options.diagnosticRuleSet.reportTypeshedErrors !== 'none') {
this._console.log(
`Could not import '${importResult.importName}' ` +
`in file '${sourceFileInfo.sourceFile.getFilePath()}'`

View File

@ -86,6 +86,7 @@ export class AnalyzerService {
private _onCompletionCallback: AnalysisCompleteCallback | undefined;
private _watchForSourceChanges = false;
private _watchForLibraryChanges = false;
private _typeCheckingMode: string | undefined;
private _verboseOutput = false;
private _maxAnalysisTime?: MaxAnalysisTime;
private _analyzeTimer: any;
@ -144,6 +145,7 @@ export class AnalyzerService {
setOptions(commandLineOptions: CommandLineOptions): void {
this._watchForSourceChanges = !!commandLineOptions.watchForSourceChanges;
this._watchForLibraryChanges = !!commandLineOptions.watchForLibraryChanges;
this._typeCheckingMode = commandLineOptions.typeCheckingMode;
this._verboseOutput = !!commandLineOptions.verboseOutput;
this._configOptions = this._getConfigOptions(commandLineOptions);
this._program.setConfigOptions(this._configOptions);
@ -336,7 +338,7 @@ export class AnalyzerService {
}
}
const configOptions = new ConfigOptions(projectRoot);
const configOptions = new ConfigOptions(projectRoot, this._typeCheckingMode);
const defaultExcludes = ['**/node_modules', '**/__pycache__', '.git'];
if (commandLineOptions.fileSpecs.length > 0) {
@ -364,7 +366,7 @@ export class AnalyzerService {
this._console.log(`Loading configuration file at ${configFilePath}`);
const configJsonObj = this._parseConfigFile(configFilePath);
if (configJsonObj) {
configOptions.initializeFromJson(configJsonObj, this._console);
configOptions.initializeFromJson(configJsonObj, this._typeCheckingMode, this._console);
const configFileDir = getDirectoryPath(configFilePath);
@ -1000,7 +1002,7 @@ export class AnalyzerService {
this._console.log(`Reloading configuration file at ${this._configFilePath}`);
const configJsonObj = this._parseConfigFile(this._configFilePath);
if (configJsonObj) {
this._configOptions.initializeFromJson(configJsonObj, this._console);
this._configOptions.initializeFromJson(configJsonObj, this._typeCheckingMode, this._console);
}
this._applyConfigOptions();

View File

@ -16,7 +16,7 @@ import {
} from 'vscode-languageserver';
import { OperationCanceledException } from '../common/cancellationUtils';
import { ConfigOptions, ExecutionEnvironment, getDefaultDiagnosticSettings } from '../common/configOptions';
import { ConfigOptions, ExecutionEnvironment, getDefaultDiagnosticRuleSet } from '../common/configOptions';
import { ConsoleInterface, StandardConsole } from '../common/console';
import { assert } from '../common/debug';
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
@ -124,7 +124,7 @@ export class SourceFile {
private _checkerDiagnostics: Diagnostic[] = [];
// Settings that control which diagnostics should be output.
private _diagnosticSettings = getDefaultDiagnosticSettings();
private _diagnosticRuleSet = getDefaultDiagnosticRuleSet();
// Circular dependencies that have been reported in this file.
private _circularDependencies: CircularDependency[] = [];
@ -210,7 +210,7 @@ export class SourceFile {
diagList = diagList.concat(this._parseDiagnostics, this._bindDiagnostics, this._checkerDiagnostics);
// Filter the diagnostics based on "type: ignore" lines.
if (options.diagnosticSettings.enableTypeIgnoreComments) {
if (options.diagnosticRuleSet.enableTypeIgnoreComments) {
const typeIgnoreLines = this._parseResults ? this._parseResults.tokenizerOutput.typeIgnoreLines : {};
if (Object.keys(typeIgnoreLines).length > 0) {
diagList = diagList.filter((d) => {
@ -225,9 +225,9 @@ export class SourceFile {
}
}
if (options.diagnosticSettings.reportImportCycles !== 'none' && this._circularDependencies.length > 0) {
if (options.diagnosticRuleSet.reportImportCycles !== 'none' && this._circularDependencies.length > 0) {
const category =
options.diagnosticSettings.reportImportCycles === 'warning'
options.diagnosticRuleSet.reportImportCycles === 'warning'
? DiagnosticCategory.Warning
: DiagnosticCategory.Error;
@ -257,9 +257,9 @@ export class SourceFile {
}
if (this._isTypeshedStubFile) {
if (options.diagnosticSettings.reportTypeshedErrors === 'none') {
if (options.diagnosticRuleSet.reportTypeshedErrors === 'none') {
includeWarningsAndErrors = false;
} else if (options.diagnosticSettings.reportTypeshedErrors === 'warning') {
} else if (options.diagnosticRuleSet.reportTypeshedErrors === 'warning') {
// Convert all the errors to warnings.
diagList = diagList.map((diag) => {
if (diag.category === DiagnosticCategory.Error) {
@ -277,7 +277,7 @@ export class SourceFile {
// If there is a "type: ignore" comment at the top of the file, clear
// the diagnostic list.
if (options.diagnosticSettings.enableTypeIgnoreComments) {
if (options.diagnosticRuleSet.enableTypeIgnoreComments) {
if (this._parseResults && this._parseResults.tokenizerOutput.typeIgnoreAll) {
diagList = [];
}
@ -506,9 +506,9 @@ export class SourceFile {
const useStrict =
configOptions.strict.find((strictFileSpec) => strictFileSpec.regExp.test(this._filePath)) !== undefined;
this._diagnosticSettings = CommentUtils.getFileLevelDirectives(
this._diagnosticRuleSet = CommentUtils.getFileLevelDirectives(
this._parseResults.tokenizerOutput.tokens,
configOptions.diagnosticSettings,
configOptions.diagnosticRuleSet,
useStrict
);
} catch (e) {
@ -879,7 +879,7 @@ export class SourceFile {
collectionsModulePath: this._collectionsModulePath,
diagnosticSink: analysisDiagnostics,
executionEnvironment: configOptions.findExecEnvironment(this._filePath),
diagnosticSettings: this._diagnosticSettings,
diagnosticRuleSet: this._diagnosticRuleSet,
fileContents,
lines: this._parseResults!.tokenizerOutput.lines,
filePath: this._filePath,

View File

@ -711,7 +711,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!typeResult) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expected type but received a string literal`,
node
@ -1387,7 +1387,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (errorNode) {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${printType(subtype)}" is not awaitable`,
errorNode
@ -1417,7 +1417,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (type.category === TypeCategory.Union && type.subtypes.some((t) => isNoneOrNever(t))) {
if (errorNode) {
addDiagnostic(
getFileInfo(errorNode).diagnosticSettings.reportOptionalIterable,
getFileInfo(errorNode).diagnosticRuleSet.reportOptionalIterable,
DiagnosticRule.reportOptionalIterable,
`Object of type "None" cannot be used as iterable value`,
errorNode
@ -1876,7 +1876,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!canAssignType(declaredType, type, diagAddendum)) {
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expression of type "${printType(type)}" cannot be assigned to declared type "${printType(
declaredType
@ -1902,7 +1902,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (
!isConstant &&
(!isPrivate || getFileInfo(nameNode).diagnosticSettings.reportPrivateUsage === 'none')
(!isPrivate || getFileInfo(nameNode).diagnosticRuleSet.reportPrivateUsage === 'none')
) {
destType = stripLiteralValue(destType);
}
@ -1917,7 +1917,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// isn't the first assignment, generate an error.
if (nameNode !== declarations[0].node) {
addDiagnostic(
fileInfo.diagnosticSettings.reportConstantRedefinition,
fileInfo.diagnosticRuleSet.reportConstantRedefinition,
DiagnosticRule.reportConstantRedefinition,
`"${nameValue}" is constant and cannot be redefined`,
nameNode
@ -2022,7 +2022,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
) {
if (typedDecls[0].isConstant) {
addDiagnostic(
fileInfo.diagnosticSettings.reportConstantRedefinition,
fileInfo.diagnosticRuleSet.reportConstantRedefinition,
DiagnosticRule.reportConstantRedefinition,
`"${node.memberName.value}" is constant and cannot be redefined`,
node.memberName
@ -2086,7 +2086,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// There was no declared type, so we need to infer the type.
if (srcExprNode) {
reportPossibleUnknownAssignment(
fileInfo.diagnosticSettings.reportUnknownMemberType,
fileInfo.diagnosticRuleSet.reportUnknownMemberType,
DiagnosticRule.reportUnknownMemberType,
node.memberName,
srcType,
@ -2137,7 +2137,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
} else {
const fileInfo = getFileInfo(target);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Tuple size mismatch: expected at least ${target.expressions.length} entries but got ${entryCount}`,
target
@ -2152,7 +2152,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
} else {
const fileInfo = getFileInfo(target);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Tuple size mismatch: expected ${target.expressions.length} but got ${entryCount}`,
target
@ -2215,7 +2215,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
}
reportPossibleUnknownAssignment(
getFileInfo(target).diagnosticSettings.reportUnknownVariableType,
getFileInfo(target).diagnosticRuleSet.reportUnknownVariableType,
DiagnosticRule.reportUnknownVariableType,
target,
type,
@ -2305,7 +2305,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
default: {
const fileInfo = getFileInfo(target);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expression cannot be assignment target`,
target
@ -2361,7 +2361,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
default: {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expression cannot be deleted`,
node
@ -2543,14 +2543,14 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (isUnbound(type)) {
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${name}" is unbound`,
node
);
} else if (isPossiblyUnbound(type)) {
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${name}" is possibly unbound`,
node
@ -2562,7 +2562,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// Handle the special case of "reveal_type".
if (name !== 'reveal_type') {
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${name}" is not defined`,
node
@ -2675,7 +2675,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
} else {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${memberName}" is not a known member of module`,
node.memberName
@ -2689,7 +2689,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
type = doForSubtypes(baseType, (subtype) => {
if (isNoneOrNever(subtype)) {
addDiagnostic(
getFileInfo(node).diagnosticSettings.reportOptionalMemberAccess,
getFileInfo(node).diagnosticRuleSet.reportOptionalMemberAccess,
DiagnosticRule.reportOptionalMemberAccess,
`"${memberName}" is not a known member of "None"`,
node.memberName
@ -2738,7 +2738,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Cannot ${operationName} member "${memberName}" for type "${printType(baseType)}"` + diag.getString(),
node.memberName
@ -3177,7 +3177,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (isNoneOrNever(subtype)) {
addDiagnostic(
getFileInfo(node).diagnosticSettings.reportOptionalSubscript,
getFileInfo(node).diagnosticRuleSet.reportOptionalSubscript,
DiagnosticRule.reportOptionalSubscript,
`Object of type "None" cannot be subscripted`,
node.baseExpression
@ -3189,7 +3189,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!isUnbound(subtype)) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Object of type "${printType(subtype)}" cannot be subscripted`,
node.baseExpression
@ -3245,7 +3245,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
} else if (usage.method === 'del' && entry.isRequired) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${entryName}" is a required key and cannot be deleted`,
node
@ -3269,7 +3269,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Could not ${operationName} item in TypedDict` + diag.getString(),
node
@ -3301,7 +3301,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!itemMethodType) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Object of type "${printType(baseType)}" does not define "${magicMethodName}"`,
node.baseExpression
@ -3575,7 +3575,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (reportError) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Second argument to "super" call must be object or class that derives from "${printType(
targetClassType
@ -3667,7 +3667,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
) {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${className}" cannot be instantiated directly`,
errorNode
@ -3704,7 +3704,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Cannot instantiate abstract class "${callType.details.name}"` + diagAddendum.getString(),
errorNode
@ -3724,7 +3724,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// as a function rather than a class, so we need to check for it here.
if (callType.details.builtInName === 'namedtuple') {
addDiagnostic(
getFileInfo(errorNode).diagnosticSettings.reportUntypedNamedTuple,
getFileInfo(errorNode).diagnosticRuleSet.reportUntypedNamedTuple,
DiagnosticRule.reportUntypedNamedTuple,
`"namedtuple" provides no types for tuple entries; use "NamedTuple" instead`,
errorNode
@ -3775,7 +3775,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
) {
if (isTypeSame(castToType, castFromType.classType)) {
addDiagnostic(
getFileInfo(errorNode).diagnosticSettings.reportUnnecessaryCast,
getFileInfo(errorNode).diagnosticRuleSet.reportUnnecessaryCast,
DiagnosticRule.reportUnnecessaryCast,
`Unnecessary "cast" call; type is already ${printType(castFromType)}`,
errorNode
@ -3803,7 +3803,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
diagAddendum.addMessage(`Argument types: (${argTypes.join(', ')})`);
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`No overloads for "${exprString}" match parameters` + diagAddendum.getString(),
errorNode
@ -3858,7 +3858,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
callType.subtypes.forEach((typeEntry) => {
if (isNoneOrNever(typeEntry)) {
addDiagnostic(
getFileInfo(errorNode).diagnosticSettings.reportOptionalCall,
getFileInfo(errorNode).diagnosticRuleSet.reportOptionalCall,
DiagnosticRule.reportOptionalCall,
`Object of type "None" cannot be called`,
errorNode
@ -3898,7 +3898,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!type) {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${ParseTreeUtils.printExpression(errorNode)}" has type "${printType(callType)}" and is not callable`,
errorNode
@ -4050,7 +4050,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!validatedTypes && argList.length > 0) {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expected no arguments to "${type.details.name}" constructor`,
errorNode
@ -4164,7 +4164,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
diagAddendum.addMessage(`Argument types: (${argTypes.join(', ')})`);
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`No overloads for "${exprString}" match parameters` + diagAddendum.getString(),
errorNode
@ -4179,7 +4179,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
} else {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${callType.details.name}" cannot be instantiated`,
errorNode
@ -4218,7 +4218,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
for (const type of callType.subtypes) {
if (isNoneOrNever(type)) {
addDiagnostic(
getFileInfo(errorNode).diagnosticSettings.reportOptionalCall,
getFileInfo(errorNode).diagnosticRuleSet.reportOptionalCall,
DiagnosticRule.reportOptionalCall,
`Object of type "None" cannot be called`,
errorNode
@ -4348,7 +4348,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (argIndex < positionalOnlyIndex && argList[argIndex].name) {
const fileInfo = getFileInfo(argList[argIndex].name!);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expected positional argument`,
argList[argIndex].name!
@ -4360,7 +4360,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const adjustedCount = positionalParamCount;
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Expected ${adjustedCount} positional ${adjustedCount === 1 ? 'argument' : 'arguments'}`,
argList[argIndex].valueExpression || errorNode
@ -4632,7 +4632,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const optionalParamName = argParam.paramName ? `"${argParam.paramName}" ` : '';
const fileInfo = getFileInfo(argParam.errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Argument of type "${printType(
argType
@ -4646,7 +4646,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const fileInfo = getFileInfo(argParam.errorNode);
if (simplifiedType.category === TypeCategory.Unknown) {
addDiagnostic(
fileInfo.diagnosticSettings.reportUnknownArgumentType,
fileInfo.diagnosticRuleSet.reportUnknownArgumentType,
DiagnosticRule.reportUnknownArgumentType,
`Type of argument is unknown`,
argParam.errorNode
@ -4663,7 +4663,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const diagAddendum = new DiagnosticAddendum();
diagAddendum.addMessage(`Argument type is "${printType(simplifiedType)}"`);
addDiagnostic(
fileInfo.diagnosticSettings.reportUnknownArgumentType,
fileInfo.diagnosticRuleSet.reportUnknownArgumentType,
DiagnosticRule.reportUnknownArgumentType,
`Type of argument is partially unknown` + diagAddendum.getString(),
argParam.errorNode
@ -5329,7 +5329,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (node.operator !== OperatorType.Not) {
if (isOptionalType(exprType)) {
addDiagnostic(
getFileInfo(node).diagnosticSettings.reportOptionalOperand,
getFileInfo(node).diagnosticRuleSet.reportOptionalOperand,
DiagnosticRule.reportOptionalOperand,
`Operator "${ParseTreeUtils.printOperator(node.operator)}" not supported for "None" type`,
node.expression
@ -5355,7 +5355,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!type) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Operator "${ParseTreeUtils.printOperator(node.operator)}" not supported for type "${printType(
exprType
@ -5400,7 +5400,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// None is a valid operand for these operators.
if (node.operator !== OperatorType.Equals && node.operator !== OperatorType.NotEquals) {
addDiagnostic(
getFileInfo(node).diagnosticSettings.reportOptionalOperand,
getFileInfo(node).diagnosticRuleSet.reportOptionalOperand,
DiagnosticRule.reportOptionalOperand,
`Operator "${ParseTreeUtils.printOperator(node.operator)}" not supported for "None" type`,
node.leftExpression
@ -5591,7 +5591,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!type || type.category === TypeCategory.Never) {
const fileInfo = getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Operator "${ParseTreeUtils.printOperator(operator)}" not supported for types "${printType(
leftType
@ -5877,7 +5877,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// are the same type, we'll assume that all values in this dictionary should
// be the same.
if (valueTypes.length > 0) {
if (getFileInfo(node).diagnosticSettings.strictDictionaryInference) {
if (getFileInfo(node).diagnosticRuleSet.strictDictionaryInference) {
valueType = combineTypes(valueTypes);
} else {
valueType = areTypesSame(valueTypes) ? valueTypes[0] : UnknownType.create();
@ -5946,7 +5946,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (entryTypes.length > 0) {
// If there was an expected type or we're using strict list inference,
// combine the types into a union.
if (expectedType || getFileInfo(node).diagnosticSettings.strictListInference) {
if (expectedType || getFileInfo(node).diagnosticRuleSet.strictListInference) {
inferredEntryType = combineTypes(entryTypes);
} else {
// Is the list homogeneous? If so, use stricter rules. Otherwise relax the rules.
@ -6160,7 +6160,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!canAssignType(optionalIntObject, exprType, diag)) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Index for slice operation must be an int value or "None"` + diag.getString(),
indexExpr
@ -6778,7 +6778,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
argType = transformTypeObjectToClass(argType);
if (argType.category !== TypeCategory.Class) {
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Argument to class must be a base class`,
arg
@ -6832,7 +6832,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
argType.subtypes.some((t) => t.category === TypeCategory.Unknown))
) {
addDiagnostic(
fileInfo.diagnosticSettings.reportUntypedBaseClass,
fileInfo.diagnosticRuleSet.reportUntypedBaseClass,
DiagnosticRule.reportUntypedBaseClass,
`Base class type is unknown, obscuring type of derived class`,
arg
@ -6980,7 +6980,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// Report this error only on the first unknown type.
if (!foundUnknown) {
addDiagnostic(
fileInfo.diagnosticSettings.reportUntypedClassDecorator,
fileInfo.diagnosticRuleSet.reportUntypedClassDecorator,
DiagnosticRule.reportUntypedClassDecorator,
`Untyped class decorator obscures type of class; ignoring decorator`,
node.decorators[i].leftExpression
@ -7168,7 +7168,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (param.defaultValue.constType === KeywordType.None) {
isNoneWithoutOptional = true;
if (!fileInfo.diagnosticSettings.strictParameterNoneValue) {
if (!fileInfo.diagnosticRuleSet.strictParameterNoneValue) {
annotatedType = combineTypes([annotatedType, NoneType.create()]);
}
}
@ -7194,7 +7194,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!canAssignType(concreteAnnotatedType, defaultValueType, diagAddendum)) {
const diag = addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Value of type "${printType(
defaultValueType
@ -7276,7 +7276,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
// Report this error only on the first unknown type.
if (!foundUnknown) {
addDiagnostic(
fileInfo.diagnosticSettings.reportUntypedFunctionDecorator,
fileInfo.diagnosticRuleSet.reportUntypedFunctionDecorator,
DiagnosticRule.reportUntypedFunctionDecorator,
`Untyped function decorator obscures type of function; ignoring decorator`,
node.decorators[i].leftExpression
@ -7933,7 +7933,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (isOptionalType(exprType)) {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportOptionalContextManager,
fileInfo.diagnosticRuleSet.reportOptionalContextManager,
DiagnosticRule.reportOptionalContextManager,
`Object of type "None" cannot be used with "with"`,
node.expression
@ -7997,7 +7997,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const fileInfo = getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Object of type "${printType(
subtype
@ -8083,7 +8083,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
!importLookupInfo.symbolTable.get('__getattr__')
) {
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`"${node.name.value}" is unknown import symbol`,
node.name
@ -9428,7 +9428,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
if (!canAssignToTypeVar(typeParameters[index], typeArgType, diag)) {
const fileInfo = getFileInfo(typeArgs![index].node);
addDiagnostic(
fileInfo.diagnosticSettings.reportGeneralTypeIssues,
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
`Type "${printType(typeArgType)}" cannot be assigned to TypeVar "${
typeParameters[index].name

View File

@ -61,6 +61,10 @@ export class CommandLineOptions {
// execution environments.
autoSearchPaths?: boolean;
// Default type-checking rule set. Should be one of 'off',
// 'basic', or 'strict'.
typeCheckingMode?: string;
// Indicates that the settings came from VS Code rather than
// from the command-line. Useful for providing clearer error
// messages.

View File

@ -43,7 +43,7 @@ export class ExecutionEnvironment {
export type DiagnosticLevel = 'none' | 'warning' | 'error';
export interface DiagnosticSettings {
export interface DiagnosticRuleSet {
// Use strict inference rules for list expressions?
strictListInference: boolean;
@ -167,12 +167,12 @@ export interface DiagnosticSettings {
reportImplicitStringConcatenation: DiagnosticLevel;
}
export function cloneDiagnosticSettings(diagSettings: DiagnosticSettings): DiagnosticSettings {
export function cloneDiagnosticRuleSet(diagSettings: DiagnosticRuleSet): DiagnosticRuleSet {
// Create a shallow copy of the existing object.
return Object.assign({}, diagSettings);
}
export function getBooleanDiagnosticSettings() {
export function getBooleanDiagnosticRules() {
return [
DiagnosticRule.strictListInference,
DiagnosticRule.strictDictionaryInference,
@ -185,7 +185,7 @@ export function getBooleanDiagnosticSettings() {
];
}
export function getDiagLevelSettings() {
export function getDiagLevelDiagnosticRules() {
return [
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportTypeshedErrors,
@ -225,8 +225,8 @@ export function getDiagLevelSettings() {
];
}
export function getStrictDiagnosticSettings(): DiagnosticSettings {
const diagSettings: DiagnosticSettings = {
export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
const diagSettings: DiagnosticRuleSet = {
strictListInference: true,
strictDictionaryInference: true,
strictParameterNoneValue: true,
@ -271,8 +271,54 @@ export function getStrictDiagnosticSettings(): DiagnosticSettings {
return diagSettings;
}
export function getDefaultDiagnosticSettings(): DiagnosticSettings {
const diagSettings: DiagnosticSettings = {
export function getNoTypeCheckingDiagnosticRuleSet(): DiagnosticRuleSet {
const diagSettings: DiagnosticRuleSet = {
strictListInference: false,
strictDictionaryInference: false,
strictParameterNoneValue: false,
enableTypeIgnoreComments: true,
reportGeneralTypeIssues: 'none',
reportTypeshedErrors: 'none',
reportMissingImports: 'none',
reportMissingTypeStubs: 'none',
reportImportCycles: 'none',
reportUnusedImport: 'none',
reportUnusedClass: 'none',
reportUnusedFunction: 'none',
reportUnusedVariable: 'none',
reportDuplicateImport: 'none',
reportOptionalSubscript: 'none',
reportOptionalMemberAccess: 'none',
reportOptionalCall: 'none',
reportOptionalIterable: 'none',
reportOptionalContextManager: 'none',
reportOptionalOperand: 'none',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
reportUntypedBaseClass: 'none',
reportUntypedNamedTuple: 'none',
reportPrivateUsage: 'none',
reportConstantRedefinition: 'none',
reportIncompatibleMethodOverride: 'none',
reportInvalidStringEscapeSequence: 'none',
reportUnknownParameterType: 'none',
reportUnknownArgumentType: 'none',
reportUnknownLambdaType: 'none',
reportUnknownVariableType: 'none',
reportUnknownMemberType: 'none',
reportCallInDefaultInitializer: 'none',
reportUnnecessaryIsInstance: 'none',
reportUnnecessaryCast: 'none',
reportAssertAlwaysTrue: 'none',
reportSelfClsParameterName: 'none',
reportImplicitStringConcatenation: 'none',
};
return diagSettings;
}
export function getDefaultDiagnosticRuleSet(): DiagnosticRuleSet {
const diagSettings: DiagnosticRuleSet = {
strictListInference: false,
strictDictionaryInference: false,
strictParameterNoneValue: false,
@ -320,9 +366,9 @@ export function getDefaultDiagnosticSettings(): DiagnosticSettings {
// Internal configuration options. These are derived from a combination
// of the command line and from a JSON-based config file.
export class ConfigOptions {
constructor(projectRoot: string) {
constructor(projectRoot: string, typeCheckingMode?: string) {
this.projectRoot = projectRoot;
this.diagnosticSettings = getDefaultDiagnosticSettings();
this.diagnosticRuleSet = ConfigOptions.getDiagnosticRuleSet(typeCheckingMode);
}
// Absolute directory of project. All relative paths in the config
@ -374,9 +420,9 @@ export class ConfigOptions {
useLibraryCodeForTypes: boolean;
//---------------------------------------------------------------
// Diagnostics Settings
// Diagnostics Rule Set
diagnosticSettings: DiagnosticSettings;
diagnosticRuleSet: DiagnosticRuleSet;
//---------------------------------------------------------------
// Parsing and Import Resolution Settings
@ -406,6 +452,18 @@ export class ConfigOptions {
// Run additional analysis as part of test cases?
internalTestMode?: boolean;
static getDiagnosticRuleSet(typeCheckingMode?: string): DiagnosticRuleSet {
if (typeCheckingMode === 'strict') {
return getStrictDiagnosticRuleSet();
}
if (typeCheckingMode === 'off') {
return getNoTypeCheckingDiagnosticRuleSet();
}
return getDefaultDiagnosticRuleSet();
}
// Finds the best execution environment for a given file path. The
// specified file path should be absolute.
// If no matching execution environment can be found, a default
@ -440,7 +498,7 @@ export class ConfigOptions {
}
// Initialize the structure from a JSON object.
initializeFromJson(configObj: any, console: ConsoleInterface) {
initializeFromJson(configObj: any, typeCheckingMode: string | undefined, console: ConsoleInterface) {
// Read the "include" entry.
this.include = [];
if (configObj.include !== undefined) {
@ -517,9 +575,23 @@ export class ConfigOptions {
}
}
const defaultSettings = getDefaultDiagnosticSettings();
// If there is a "typeCheckingMode", it can override the provided setting.
let configTypeCheckingMode: string | undefined;
if (configObj.typeCheckingMode !== undefined) {
if (
configObj.typeCheckingMode === 'off' ||
configObj.typeCheckingMode === 'basic' ||
configObj.typeCheckingMode === 'strict'
) {
configTypeCheckingMode = configObj.typeCheckingMode;
} else {
console.log(`Config "typeCheckingMode" entry must contain "off", "basic", or "strict".`);
}
}
this.diagnosticSettings = {
const defaultSettings = ConfigOptions.getDiagnosticRuleSet(configTypeCheckingMode || typeCheckingMode);
this.diagnosticRuleSet = {
// Use strict inference rules for list expressions?
strictListInference: this._convertBoolean(
configObj.strictListInference,

View File

@ -67,6 +67,7 @@ export interface ServerSettings {
pythonPath?: string;
typeshedPath?: string;
openFilesOnly?: boolean;
typeCheckingMode?: string;
useLibraryCodeForTypes?: boolean;
disableLanguageServices?: boolean;
autoSearchPaths?: boolean;

View File

@ -40,6 +40,7 @@ function _getCommandLineOptions(
commandLineOptions.checkOnlyOpenFiles = serverSettings.openFilesOnly;
commandLineOptions.useLibraryCodeForTypes = serverSettings.useLibraryCodeForTypes;
commandLineOptions.watchForLibraryChanges = serverSettings.watchForLibraryChanges;
commandLineOptions.typeCheckingMode = serverSettings.typeCheckingMode;
// Disable watching of source files in the VS Code extension if we're
// analyzing only open files. The file system watcher code has caused

View File

@ -46,10 +46,12 @@ class PyrightServer extends LanguageServerBase {
serverSettings.openFilesOnly = !!pyrightSection.openFilesOnly;
serverSettings.useLibraryCodeForTypes = !!pyrightSection.useLibraryCodeForTypes;
serverSettings.disableLanguageServices = !!pyrightSection.disableLanguageServices;
serverSettings.typeCheckingMode = pyrightSection.typeCheckingMode;
} else {
serverSettings.openFilesOnly = true;
serverSettings.useLibraryCodeForTypes = false;
serverSettings.disableLanguageServices = false;
serverSettings.typeCheckingMode = undefined;
}
} catch (error) {
this.console.log(`Error reading settings: ${error}`);

View File

@ -496,22 +496,22 @@ test('Optional1', () => {
validateResults(analysisResults, 0);
// Turn on warnings.
configOptions.diagnosticSettings.reportOptionalSubscript = 'warning';
configOptions.diagnosticSettings.reportOptionalMemberAccess = 'warning';
configOptions.diagnosticSettings.reportOptionalCall = 'warning';
configOptions.diagnosticSettings.reportOptionalIterable = 'warning';
configOptions.diagnosticSettings.reportOptionalContextManager = 'warning';
configOptions.diagnosticSettings.reportOptionalOperand = 'warning';
configOptions.diagnosticRuleSet.reportOptionalSubscript = 'warning';
configOptions.diagnosticRuleSet.reportOptionalMemberAccess = 'warning';
configOptions.diagnosticRuleSet.reportOptionalCall = 'warning';
configOptions.diagnosticRuleSet.reportOptionalIterable = 'warning';
configOptions.diagnosticRuleSet.reportOptionalContextManager = 'warning';
configOptions.diagnosticRuleSet.reportOptionalOperand = 'warning';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['optional1.py'], configOptions);
validateResults(analysisResults, 0, 7);
// Turn on errors.
configOptions.diagnosticSettings.reportOptionalSubscript = 'error';
configOptions.diagnosticSettings.reportOptionalMemberAccess = 'error';
configOptions.diagnosticSettings.reportOptionalCall = 'error';
configOptions.diagnosticSettings.reportOptionalIterable = 'error';
configOptions.diagnosticSettings.reportOptionalContextManager = 'error';
configOptions.diagnosticSettings.reportOptionalOperand = 'error';
configOptions.diagnosticRuleSet.reportOptionalSubscript = 'error';
configOptions.diagnosticRuleSet.reportOptionalMemberAccess = 'error';
configOptions.diagnosticRuleSet.reportOptionalCall = 'error';
configOptions.diagnosticRuleSet.reportOptionalIterable = 'error';
configOptions.diagnosticRuleSet.reportOptionalContextManager = 'error';
configOptions.diagnosticRuleSet.reportOptionalOperand = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['optional1.py'], configOptions);
validateResults(analysisResults, 7);
});
@ -524,7 +524,7 @@ test('Private1', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportPrivateUsage = 'error';
configOptions.diagnosticRuleSet.reportPrivateUsage = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['private1.py'], configOptions);
validateResults(analysisResults, 4);
});
@ -537,7 +537,7 @@ test('Constant1', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportConstantRedefinition = 'error';
configOptions.diagnosticRuleSet.reportConstantRedefinition = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['constant1.py'], configOptions);
validateResults(analysisResults, 5);
});
@ -766,7 +766,7 @@ test('Classes2', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportIncompatibleMethodOverride = 'error';
configOptions.diagnosticRuleSet.reportIncompatibleMethodOverride = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['classes2.py'], configOptions);
validateResults(analysisResults, 2);
});
@ -851,7 +851,7 @@ test('DefaultInitializer1', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportCallInDefaultInitializer = 'error';
configOptions.diagnosticRuleSet.reportCallInDefaultInitializer = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['defaultInitializer1.py'], configOptions);
validateResults(analysisResults, 2);
});
@ -881,7 +881,7 @@ test('UnnecessaryIsInstance1', () => {
validateResults(analysisResults, 1);
// Turn on errors.
configOptions.diagnosticSettings.reportUnnecessaryIsInstance = 'error';
configOptions.diagnosticRuleSet.reportUnnecessaryIsInstance = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryIsInstance1.py'], configOptions);
validateResults(analysisResults, 4);
});
@ -893,7 +893,7 @@ test('UnnecessaryIsSubclass1', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportUnnecessaryIsInstance = 'error';
configOptions.diagnosticRuleSet.reportUnnecessaryIsInstance = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryIsSubclass1.py'], configOptions);
validateResults(analysisResults, 3);
});
@ -911,7 +911,7 @@ test('UnnecessaryCast', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportUnnecessaryCast = 'error';
configOptions.diagnosticRuleSet.reportUnnecessaryCast = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryCast1.py'], configOptions);
validateResults(analysisResults, 1);
});
@ -924,12 +924,12 @@ test('AssertAlwaysTrue', () => {
validateResults(analysisResults, 0, 1);
// Enable it as an error.
configOptions.diagnosticSettings.reportAssertAlwaysTrue = 'error';
configOptions.diagnosticRuleSet.reportAssertAlwaysTrue = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['assert1.py'], configOptions);
validateResults(analysisResults, 1, 0);
// Turn off the diagnostic.
configOptions.diagnosticSettings.reportAssertAlwaysTrue = 'none';
configOptions.diagnosticRuleSet.reportAssertAlwaysTrue = 'none';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['assert1.py'], configOptions);
validateResults(analysisResults, 0, 0);
});
@ -1169,7 +1169,7 @@ test('TypeIgnore1', () => {
validateResults(analysisResults, 0);
// Disable type ignore
configOptions.diagnosticSettings.enableTypeIgnoreComments = false;
configOptions.diagnosticRuleSet.enableTypeIgnoreComments = false;
analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore1.py'], configOptions);
validateResults(analysisResults, 2);
});
@ -1181,7 +1181,7 @@ test('TypeIgnore2', () => {
validateResults(analysisResults, 0);
// Disable type ignore
configOptions.diagnosticSettings.enableTypeIgnoreComments = false;
configOptions.diagnosticRuleSet.enableTypeIgnoreComments = false;
analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore2.py'], configOptions);
validateResults(analysisResults, 3);
});
@ -1289,7 +1289,7 @@ test('DuplicateImports1', () => {
validateResults(analysisResults, 0);
// Turn on errors.
configOptions.diagnosticSettings.reportDuplicateImport = 'error';
configOptions.diagnosticRuleSet.reportDuplicateImport = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['duplicateImports1.py'], configOptions);
validateResults(analysisResults, 2);
});
@ -1320,11 +1320,11 @@ test('ParamName1', () => {
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['paramNames1.py'], configOptions);
validateResults(analysisResults, 0, 4);
configOptions.diagnosticSettings.reportSelfClsParameterName = 'none';
configOptions.diagnosticRuleSet.reportSelfClsParameterName = 'none';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['paramNames1.py'], configOptions);
validateResults(analysisResults, 0, 0);
configOptions.diagnosticSettings.reportSelfClsParameterName = 'error';
configOptions.diagnosticRuleSet.reportSelfClsParameterName = 'error';
analysisResults = TestUtils.typeAnalyzeSampleFiles(['paramNames1.py'], configOptions);
validateResults(analysisResults, 4, 0);
});

View File

@ -179,7 +179,7 @@ test('PythonPlatform', () => {
"extraPaths" : []
}]}`);
configOptions.initializeFromJson(json, nullConsole);
configOptions.initializeFromJson(json, undefined, nullConsole);
const env = configOptions.executionEnvironments[0];
assert.equal(env.pythonPlatform, 'platform');

View File

@ -107,7 +107,7 @@ export class TestState {
throw new Error(`Failed to parse test ${file.fileName}: ${e.message}`);
}
configOptions.initializeFromJson(configJson, nullConsole);
configOptions.initializeFromJson(configJson, 'basic', nullConsole);
this._applyTestConfigOptions(configOptions);
} else {
files[file.fileName] = new vfs.File(file.content, { meta: file.fileOptions, encoding: 'utf8' });

View File

@ -118,7 +118,7 @@ test('Configuration', () => {
assert.equal(state.fs.cwd(), normalizeSlashes('/'));
assert(state.fs.existsSync(normalizeSlashes(combinePaths(factory.srcFolder, 'file1.py'))));
assert.equal(state.configOptions.diagnosticSettings.reportMissingImports, 'error');
assert.equal(state.configOptions.diagnosticRuleSet.reportMissingImports, 'error');
assert.equal(state.configOptions.typingsPath, normalizeSlashes('/src/typestubs'));
});

View File

@ -15,7 +15,7 @@ import { Binder } from '../analyzer/binder';
import { ImportResolver } from '../analyzer/importResolver';
import { Program } from '../analyzer/program';
import { TestWalker } from '../analyzer/testWalker';
import { cloneDiagnosticSettings, ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
import { cloneDiagnosticRuleSet, ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
import { fail } from '../common/debug';
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
@ -96,7 +96,7 @@ export function buildAnalyzerFileInfo(
builtinsScope: undefined,
diagnosticSink: analysisDiagnostics,
executionEnvironment: configOptions.findExecEnvironment(filePath),
diagnosticSettings: cloneDiagnosticSettings(configOptions.diagnosticSettings),
diagnosticRuleSet: cloneDiagnosticRuleSet(configOptions.diagnosticRuleSet),
fileContents,
lines: parseResults.tokenizerOutput.lines,
filePath,