diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs index 42b2f958c..6c887ef0f 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs @@ -71,12 +71,26 @@ public void HandleAssignment(AssignmentStatement node) { } var source = value.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; - Eval.DeclareVariable(ne.Name, value ?? Module.Interpreter.UnknownType, source, Eval.GetLocationOfName(ne)); + var location = Eval.GetLocationOfName(ne); + if (IsValidAssignment(ne.Name, location)) { + Eval.DeclareVariable(ne.Name, value ?? Module.Interpreter.UnknownType, source, location); + } } TryHandleClassVariable(node, value); } + private bool IsValidAssignment(string name, Location loc) { + if (Eval.GetInScope(name) is ILocatedMember m) { + // Class and function definition are processed first, so only override + // if assignment happens after declaration + if (loc.IndexSpan.Start < m.Location.IndexSpan.Start) { + return false; + } + } + return true; + } + public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember value) { if (expr?.Annotation == null) { return; diff --git a/src/Analysis/Ast/Test/AssignmentTests.cs b/src/Analysis/Ast/Test/AssignmentTests.cs index 9c443b1e4..b586c1200 100644 --- a/src/Analysis/Ast/Test/AssignmentTests.cs +++ b/src/Analysis/Ast/Test/AssignmentTests.cs @@ -308,6 +308,48 @@ def func(self): .Which.Should().HaveMembers(listMemberNames); } + [TestMethod, Priority(0)] + public async Task AssignBeforeClassDef() { + const string code = @" +D = 5 +class D: ... +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveClass("D"); + } + + [TestMethod, Priority(0)] + public async Task AssignAfterClassDef() { + const string code = @" +class D: ... +D = 5 +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("D").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task AssignBeforeFunctionDef() { + const string code = @" +D = 5 +def D(): + pass +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveFunction("D"); + } + + [TestMethod, Priority(0)] + public async Task AssignAfterFunctionDef() { + const string code = @" +def D(): + pass +D = 5 +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("D").OfType(BuiltinTypeId.Int); + } + [TestMethod, Priority(0)] public async Task StrIndex() { const string code = @" diff --git a/src/Analysis/Ast/Test/LintInheritNonClassTests.cs b/src/Analysis/Ast/Test/LintInheritNonClassTests.cs index df97806ca..fa837630b 100644 --- a/src/Analysis/Ast/Test/LintInheritNonClassTests.cs +++ b/src/Analysis/Ast/Test/LintInheritNonClassTests.cs @@ -474,6 +474,17 @@ class Test3(SupportsInt): class Test3(FrozenSet): mystr = 'test' +"; + var analysis = await GetAnalysisAsync(code); + analysis.Diagnostics.Should().BeEmpty(); + } + + [TestMethod, Priority(0)] + public async Task InheritFromEnum() { + const string code = @" +from enum import Enum, EnumMeta + +class C(Enum): ... "; var analysis = await GetAnalysisAsync(code); analysis.Diagnostics.Should().BeEmpty();