mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 16:06:39 +03:00
Added out-of-bounds access check for index operations where the indexed type is a tuple object with known length and the index value is a negative integer literal value.
This commit is contained in:
parent
2b7191ad7f
commit
c66fc45f46
@ -20,6 +20,7 @@ import { DiagnosticRule } from '../common/diagnosticRules';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { Localizer } from '../localization/localize';
|
||||
import {
|
||||
ArgumentCategory,
|
||||
AssertNode,
|
||||
AssignmentExpressionNode,
|
||||
AssignmentNode,
|
||||
@ -119,6 +120,7 @@ import {
|
||||
getDeclaredGeneratorReturnType,
|
||||
getGeneratorTypeArgs,
|
||||
isEllipsisType,
|
||||
isLiteralType,
|
||||
isLiteralTypeOrUnion,
|
||||
isNoReturnType,
|
||||
isOpenEndedTupleClass,
|
||||
@ -880,27 +882,44 @@ export class Checker extends ParseTreeWalker {
|
||||
|
||||
// If the index is a literal integer, see if this is a tuple with
|
||||
// a known length and the integer value exceeds the length.
|
||||
const subscriptValue = ParseTreeUtils.getIntegerSubscriptValue(node);
|
||||
if (subscriptValue !== undefined) {
|
||||
const baseType = this._evaluator.getType(node.baseExpression);
|
||||
if (
|
||||
baseType &&
|
||||
isObject(baseType) &&
|
||||
baseType.classType.tupleTypeArguments &&
|
||||
!isOpenEndedTupleClass(baseType.classType)
|
||||
) {
|
||||
const tupleLength = baseType.classType.tupleTypeArguments.length;
|
||||
const baseType = this._evaluator.getType(node.baseExpression);
|
||||
if (
|
||||
baseType &&
|
||||
isObject(baseType) &&
|
||||
baseType.classType.tupleTypeArguments &&
|
||||
!isOpenEndedTupleClass(baseType.classType)
|
||||
) {
|
||||
const tupleLength = baseType.classType.tupleTypeArguments.length;
|
||||
|
||||
if (subscriptValue >= tupleLength) {
|
||||
this._evaluator.addDiagnostic(
|
||||
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.tupleIndexOutOfRange().format({
|
||||
length: tupleLength,
|
||||
index: subscriptValue,
|
||||
}),
|
||||
node
|
||||
);
|
||||
if (
|
||||
node.items.length === 1 &&
|
||||
!node.trailingComma &&
|
||||
node.items[0].argumentCategory === ArgumentCategory.Simple &&
|
||||
!node.items[0].name
|
||||
) {
|
||||
const subscriptType = this._evaluator.getType(node.items[0].valueExpression);
|
||||
if (
|
||||
subscriptType &&
|
||||
isObject(subscriptType) &&
|
||||
ClassType.isBuiltIn(subscriptType.classType, 'int') &&
|
||||
isLiteralType(subscriptType)
|
||||
) {
|
||||
const subscriptValue = subscriptType.classType.literalValue as number;
|
||||
|
||||
if (
|
||||
(subscriptValue >= 0 && subscriptValue >= tupleLength) ||
|
||||
(subscriptValue < 0 && subscriptValue + tupleLength < 0)
|
||||
) {
|
||||
this._evaluator.addDiagnostic(
|
||||
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.tupleIndexOutOfRange().format({
|
||||
length: tupleLength,
|
||||
index: subscriptValue,
|
||||
}),
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1254,23 +1254,6 @@ export function getCallNodeAndActiveParameterIndex(
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the integer subscript if a simple numeric literal is used.
|
||||
export function getIntegerSubscriptValue(node: IndexNode): number | undefined {
|
||||
if (
|
||||
node.items.length === 1 &&
|
||||
!node.trailingComma &&
|
||||
node.items[0].argumentCategory === ArgumentCategory.Simple &&
|
||||
!node.items[0].name
|
||||
) {
|
||||
const expr = node.items[0].valueExpression;
|
||||
if (expr.nodeType === ParseNodeType.Number && expr.isInteger && !expr.isImaginary) {
|
||||
return expr.value;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function printParseNodeType(type: ParseNodeType) {
|
||||
switch (type) {
|
||||
case ParseNodeType.Error:
|
||||
|
@ -147,6 +147,7 @@ def func13(a: Tuple[int, str], b: Tuple[()], c: Tuple[int, ...]):
|
||||
v7 = a[-1]
|
||||
t_v7: Literal["str"] = reveal_type(v7)
|
||||
|
||||
# This should generate an error.
|
||||
v8 = a[-3]
|
||||
t_v8: Literal["int | str"] = reveal_type(v8)
|
||||
|
||||
|
@ -783,7 +783,7 @@ test('Optional1', () => {
|
||||
test('Tuples1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuples1.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 10);
|
||||
TestUtils.validateResults(analysisResults, 11);
|
||||
});
|
||||
|
||||
test('Tuples2', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user