mirror of
https://github.com/microsoft/pyright.git
synced 2024-11-03 21:30:08 +03:00
Modified diagnostic checks for list, dictionary, and tuple literal expressions within annotation expressions so they are emitted as part of the reportGeneralTypeIssue
diagnostic rule rather than unconditionally. This addresses https://github.com/microsoft/pylance-release/issues/4587. (#5471)
Co-authored-by: Eric Traut <erictr@microsoft.com>
This commit is contained in:
parent
bc89399c9b
commit
72491ca691
@ -1038,7 +1038,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
|
||||
case ParseNodeType.List:
|
||||
case ParseNodeType.Set: {
|
||||
typeResult = getTypeOfListOrSet(node, inferenceContext);
|
||||
typeResult = getTypeOfListOrSet(node, flags, inferenceContext);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1063,7 +1063,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}
|
||||
|
||||
case ParseNodeType.Dictionary: {
|
||||
typeResult = getTypeOfDictionary(node, inferenceContext);
|
||||
typeResult = getTypeOfDictionary(node, flags, inferenceContext);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -7304,6 +7304,23 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
flags: EvaluatorFlags,
|
||||
inferenceContext: InferenceContext | undefined
|
||||
): TypeResult {
|
||||
if (
|
||||
(flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0 &&
|
||||
node.parent?.nodeType !== ParseNodeType.Argument
|
||||
) {
|
||||
// This is allowed inside of an index trailer, specifically
|
||||
// to support Tuple[()], which is the documented way to annotate
|
||||
// a zero-length tuple.
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useTupleInstead());
|
||||
addDiagnostic(
|
||||
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.tupleInAnnotation() + diag.getString(),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
(flags & EvaluatorFlags.ExpectingInstantiableType) !== 0 &&
|
||||
node.expressions.length === 0 &&
|
||||
@ -7528,6 +7545,21 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
): TypeResult {
|
||||
let baseTypeResult: TypeResult | undefined;
|
||||
|
||||
if (
|
||||
(flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0 &&
|
||||
node.leftExpression.nodeType === ParseNodeType.Name &&
|
||||
node.leftExpression.value === 'type'
|
||||
) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useTypeInstead());
|
||||
addDiagnostic(
|
||||
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.typeCallNotAllowed() + diag.getString(),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
// Handle immediate calls of lambdas specially.
|
||||
if (node.leftExpression.nodeType === ParseNodeType.Lambda) {
|
||||
baseTypeResult = getTypeOfLambdaForCall(node, inferenceContext);
|
||||
@ -12251,7 +12283,25 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return returnType;
|
||||
}
|
||||
|
||||
function getTypeOfDictionary(node: DictionaryNode, inferenceContext: InferenceContext | undefined): TypeResult {
|
||||
function getTypeOfDictionary(
|
||||
node: DictionaryNode,
|
||||
flags: EvaluatorFlags,
|
||||
inferenceContext: InferenceContext | undefined
|
||||
): TypeResult {
|
||||
if (
|
||||
(flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0 &&
|
||||
node.parent?.nodeType !== ParseNodeType.Argument
|
||||
) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useDictInstead());
|
||||
addDiagnostic(
|
||||
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.dictInAnnotation() + diag.getString(),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
// If the expected type is a union, analyze for each of the subtypes
|
||||
// to find one that matches.
|
||||
let effectiveExpectedType = inferenceContext?.expectedType;
|
||||
@ -12730,7 +12780,26 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return isIncomplete;
|
||||
}
|
||||
|
||||
function getTypeOfListOrSet(node: ListNode | SetNode, inferenceContext: InferenceContext | undefined): TypeResult {
|
||||
function getTypeOfListOrSet(
|
||||
node: ListNode | SetNode,
|
||||
flags: EvaluatorFlags,
|
||||
inferenceContext: InferenceContext | undefined
|
||||
): TypeResult {
|
||||
if (
|
||||
(flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0 &&
|
||||
node.nodeType === ParseNodeType.List &&
|
||||
node.parent?.nodeType !== ParseNodeType.Argument
|
||||
) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useListInstead());
|
||||
addDiagnostic(
|
||||
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.listInAnnotation() + diag.getString(),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
// If the expected type is a union, recursively call for each of the subtypes
|
||||
// to find one that matches.
|
||||
let effectiveExpectedType = inferenceContext?.expectedType;
|
||||
|
@ -3436,14 +3436,6 @@ export class Parser {
|
||||
|
||||
this._isParsingTypeAnnotation = wasParsingTypeAnnotation;
|
||||
|
||||
if (this._isParsingTypeAnnotation) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
if (atomExpression.nodeType === ParseNodeType.Name && atomExpression.value === 'type') {
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useTypeInstead());
|
||||
this._addError(Localizer.Diagnostic.typeCallNotAllowed() + diag.getString(), callNode);
|
||||
}
|
||||
}
|
||||
|
||||
atomExpression = callNode;
|
||||
|
||||
if (atomExpression.maxChildDepth !== undefined && atomExpression.maxChildDepth >= maxChildNodeDepth) {
|
||||
@ -3818,18 +3810,6 @@ export class Parser {
|
||||
|
||||
if (nextToken.type === TokenType.OpenParenthesis) {
|
||||
const possibleTupleNode = this._parseTupleAtom();
|
||||
if (
|
||||
possibleTupleNode.nodeType === ParseNodeType.Tuple &&
|
||||
this._isParsingTypeAnnotation &&
|
||||
!this._isParsingIndexTrailer
|
||||
) {
|
||||
// This is allowed inside of an index trailer, specifically
|
||||
// to support Tuple[()], which is the documented way to annotate
|
||||
// a zero-length tuple.
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useTupleInstead());
|
||||
this._addError(Localizer.Diagnostic.tupleInAnnotation() + diag.getString(), possibleTupleNode);
|
||||
}
|
||||
|
||||
if (
|
||||
possibleTupleNode.nodeType === ParseNodeType.UnaryOperation ||
|
||||
@ -3853,21 +3833,9 @@ export class Parser {
|
||||
|
||||
return possibleTupleNode;
|
||||
} else if (nextToken.type === TokenType.OpenBracket) {
|
||||
const listNode = this._parseListAtom();
|
||||
if (this._isParsingTypeAnnotation && !this._isParsingIndexTrailer) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useListInstead());
|
||||
this._addError(Localizer.Diagnostic.listInAnnotation() + diag.getString(), listNode);
|
||||
}
|
||||
return listNode;
|
||||
return this._parseListAtom();
|
||||
} else if (nextToken.type === TokenType.OpenCurlyBrace) {
|
||||
const dictNode = this._parseDictionaryOrSetAtom();
|
||||
if (this._isParsingTypeAnnotation && !this._isParsingIndexTrailer) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
diag.addMessage(Localizer.DiagnosticAddendum.useDictInstead());
|
||||
this._addError(Localizer.Diagnostic.dictInAnnotation() + diag.getString(), dictNode);
|
||||
}
|
||||
return dictNode;
|
||||
return this._parseDictionaryOrSetAtom();
|
||||
}
|
||||
|
||||
if (nextToken.type === TokenType.Keyword) {
|
||||
|
@ -910,7 +910,7 @@ test('ParamSpec19', () => {
|
||||
|
||||
test('ParamSpec20', () => {
|
||||
const results = TestUtils.typeAnalyzeSampleFiles(['paramSpec20.py']);
|
||||
TestUtils.validateResults(results, 6);
|
||||
TestUtils.validateResults(results, 8);
|
||||
});
|
||||
|
||||
test('ParamSpec21', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user