mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 16:06:39 +03:00
Added check for disallowed expression forms used with TypeAlias
and with PEP 695 type
statement. This addresses https://github.com/microsoft/pyright/issues/4320.
This commit is contained in:
parent
44b1c3046c
commit
8e4ad79e97
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { getEmptyRange } from '../common/textRange';
|
||||
import { NameNode, ParseNodeType } from '../parser/parseNodes';
|
||||
import { ExpressionNode, NameNode, ParseNodeType } from '../parser/parseNodes';
|
||||
import { AliasDeclaration, Declaration, DeclarationType, isAliasDeclaration, ModuleLoaderActions } from './declaration';
|
||||
import { getFileInfoFromNode } from './parseTreeUtils';
|
||||
|
||||
@ -123,8 +123,11 @@ export function isPossibleTypeAliasDeclaration(decl: Declaration) {
|
||||
|
||||
// Perform a sanity check on the RHS expression. Some expression
|
||||
// forms should never be considered legitimate for type aliases.
|
||||
const rhsOfAssignment = decl.node.parent.rightExpression;
|
||||
switch (rhsOfAssignment.nodeType) {
|
||||
return isLegalTypeAliasExpressionForm(decl.node.parent.rightExpression);
|
||||
}
|
||||
|
||||
export function isLegalTypeAliasExpressionForm(node: ExpressionNode) {
|
||||
switch (node.nodeType) {
|
||||
case ParseNodeType.Error:
|
||||
case ParseNodeType.UnaryOperation:
|
||||
case ParseNodeType.AssignmentExpression:
|
||||
|
@ -115,6 +115,7 @@ import {
|
||||
getNameNodeForDeclaration,
|
||||
isExplicitTypeAliasDeclaration,
|
||||
isFinalVariableDeclaration,
|
||||
isLegalTypeAliasExpressionForm,
|
||||
isPossibleTypeAliasDeclaration,
|
||||
} from './declarationUtils';
|
||||
import {
|
||||
@ -14779,6 +14780,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
|
||||
if (isDeclaredTypeAlias(node.leftExpression)) {
|
||||
typeAliasNameNode = (node.leftExpression as TypeAnnotationNode).valueExpression as NameNode;
|
||||
|
||||
if (!isLegalTypeAliasExpressionForm(node.rightExpression)) {
|
||||
addDiagnostic(
|
||||
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.typeAliasIllegalExpressionForm(),
|
||||
node.rightExpression
|
||||
);
|
||||
}
|
||||
} else if (node.leftExpression.nodeType === ParseNodeType.Name) {
|
||||
const symbolWithScope = lookUpSymbolRecursive(
|
||||
node.leftExpression,
|
||||
@ -15005,6 +15015,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
typeAliasTypeVar.details.recursiveTypeParameters = typeParameters;
|
||||
}
|
||||
|
||||
if (!isLegalTypeAliasExpressionForm(node.expression)) {
|
||||
addDiagnostic(
|
||||
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.typeAliasIllegalExpressionForm(),
|
||||
node.expression
|
||||
);
|
||||
}
|
||||
|
||||
const aliasTypeResult = getTypeOfExpressionExpectingType(node.expression);
|
||||
let isIncomplete = false;
|
||||
let aliasType = aliasTypeResult.type;
|
||||
|
@ -786,6 +786,7 @@ export namespace Localizer {
|
||||
export const tupleInAnnotation = () => getRawString('Diagnostic.tupleInAnnotation');
|
||||
export const tupleIndexOutOfRange = () =>
|
||||
new ParameterizedString<{ type: string; index: number }>(getRawString('Diagnostic.tupleIndexOutOfRange'));
|
||||
export const typeAliasIllegalExpressionForm = () => getRawString('Diagnostic.typeAliasIllegalExpressionForm');
|
||||
export const typeAliasIsRecursiveDirect = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.typeAliasIsRecursiveDirect'));
|
||||
export const typeAliasIsRecursiveIndirect = () =>
|
||||
|
@ -390,6 +390,7 @@
|
||||
"tupleAssignmentMismatch": "Expression with type \"{type}\" cannot be assigned to target tuple",
|
||||
"tupleInAnnotation": "Tuple expression not allowed in type annotation",
|
||||
"tupleIndexOutOfRange": "Index {index} is out of range for type {type}",
|
||||
"typeAliasIllegalExpressionForm": "Invalid expression form for type alias definition",
|
||||
"typeAliasIsRecursiveDirect": "Type alias \"{name}\" cannot use itself in its definition",
|
||||
"typeAliasIsRecursiveIndirect": "Type alias \"{name}\" cannot refer to itself indirectly in its definition",
|
||||
"typeAliasNotInModuleOrClass": "A TypeAlias can be defined only within a module or class scope",
|
||||
|
@ -34,3 +34,15 @@ if 1 < 2:
|
||||
else:
|
||||
type TA7 = int
|
||||
|
||||
|
||||
def func1() -> type[int]:
|
||||
...
|
||||
|
||||
# This should generate an error because a call expression is not
|
||||
# allowed in a type alias definition.
|
||||
type TA8 = func1()
|
||||
|
||||
# This should generate an error because a tuple and index expression is not
|
||||
# allowed in a type alias definition.
|
||||
type TA9 = (int, str, str)[0]
|
||||
|
||||
|
@ -489,11 +489,11 @@ test('TypeAlias4', () => {
|
||||
|
||||
configOptions.defaultPythonVersion = PythonVersion.V3_9;
|
||||
const analysisResults3_9 = TestUtils.typeAnalyzeSampleFiles(['typeAlias4.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults3_9, 8);
|
||||
TestUtils.validateResults(analysisResults3_9, 10);
|
||||
|
||||
configOptions.defaultPythonVersion = PythonVersion.V3_10;
|
||||
const analysisResults3_10 = TestUtils.typeAnalyzeSampleFiles(['typeAlias4.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults3_10, 7);
|
||||
TestUtils.validateResults(analysisResults3_10, 9);
|
||||
});
|
||||
|
||||
test('TypeAlias5', () => {
|
||||
|
@ -101,7 +101,7 @@ test('TypeAliasStatement1', () => {
|
||||
configOptions.defaultPythonVersion = PythonVersion.V3_12;
|
||||
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeAliasStatement1.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults, 1);
|
||||
TestUtils.validateResults(analysisResults, 3);
|
||||
});
|
||||
|
||||
test('TypeAliasStatement2', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user