Added missing check for Final variable assigned in a loop. (#8561)

This commit is contained in:
Eric Traut 2024-07-26 17:08:24 -07:00 committed by GitHub
parent ac8076818c
commit c385edf274
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 46 additions and 1 deletions

View File

@ -3058,6 +3058,8 @@ export class Checker extends ParseTreeWalker {
this._reportMultipleFinalDeclarations(name, symbol, scope.type);
this._reportFinalInLoop(symbol);
this._reportMultipleTypeAliasDeclarations(name, symbol);
this._reportInvalidOverload(name, symbol);
@ -3208,6 +3210,25 @@ export class Checker extends ParseTreeWalker {
});
}
private _reportFinalInLoop(symbol: Symbol) {
if (!this._evaluator.isFinalVariable(symbol)) {
return;
}
const decls = symbol.getDeclarations();
if (decls.length === 0) {
return;
}
if (ParseTreeUtils.isWithinLoop(decls[0].node)) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
LocMessage.finalInLoop(),
decls[0].node
);
}
}
private _reportMultipleFinalDeclarations(name: string, symbol: Symbol, scopeType: ScopeType) {
if (!this._evaluator.isFinalVariable(symbol)) {
return;

View File

@ -497,6 +497,7 @@ export namespace Localizer {
export const finalClassIsAbstract = () =>
new ParameterizedString<{ type: string }>(getRawString('Diagnostic.finalClassIsAbstract'));
export const finalContext = () => getRawString('Diagnostic.finalContext');
export const finalInLoop = () => getRawString('Diagnostic.finalInLoop');
export const finalMethodOverride = () =>
new ParameterizedString<{ name: string; className: string }>(
getRawString('Diagnostic.finalMethodOverride')

View File

@ -197,6 +197,7 @@
"expectedYieldExpr": "Expected expression in yield statement",
"finalClassIsAbstract": "Class \"{type}\" is marked final and must implement all abstract symbols",
"finalContext": "\"Final\" is not allowed in this context",
"finalInLoop": "A \"Final\" variable cannot be assigned within a loop",
"finalMethodOverride": "Method \"{name}\" cannot override final method defined in class \"{className}\"",
"finalNonMethod": "Function \"{name}\" cannot be marked @final because it is not a method",
"finalReassigned": "\"{name}\" is declared as Final and cannot be reassigned",

View File

@ -246,3 +246,25 @@ reveal_type(v11, expected_text='Literal[b""]')
v12: Final = b"2" and True
reveal_type(v12, expected_text="Literal[True]")
def func4():
while 1 < 1:
# This should generate an error because it's in a loop.
x1: Final = 1
for i in range(10):
if i < 3:
# This should generate an error because it's in a loop.
x2: Final[int] = 1
class ClassF:
while 1 < 2:
# This should generate an error because it's in a loop.
x1: Final = 1
for i in range(10):
if i < 3:
# This should generate an error because it's in a loop.
x2: Final[int] = 1

View File

@ -33,7 +33,7 @@ test('Final2', () => {
test('Final3', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['final3.py']);
TestUtils.validateResults(analysisResults, 38);
TestUtils.validateResults(analysisResults, 42);
});
test('Final4', () => {