Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 25b4846

Browse files
author
Mikhail Arkhipov
authored
Special handling of variables representing class members (microsoft#1424)
* Initial * Update test * Tell between class members with/without self better * Move interface * Fix reference search
1 parent 47fabae commit 25b4846

File tree

17 files changed

+235
-33
lines changed

17 files changed

+235
-33
lines changed

src/Analysis/Ast/Impl/Analyzer/Definitions/LookupOptions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public enum LookupOptions {
2323
Nonlocal = 2,
2424
Global = 4,
2525
Builtins = 8,
26-
Normal = Local | Nonlocal | Global | Builtins
26+
ClassMembers = 16,
27+
Normal = Local | Nonlocal | Global | Builtins,
28+
All = Normal | ClassMembers
2729
}
2830
}

src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Scopes.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,14 @@ public void DeclareVariable(string name, IMember value, VariableSource source, L
6363

6464
public IMember LookupNameInScopes(string name, out IScope scope, out IVariable v, LookupOptions options) {
6565
scope = null;
66+
var classMembers = (options & LookupOptions.ClassMembers) == LookupOptions.ClassMembers;
6667

6768
switch (options) {
69+
case LookupOptions.All:
6870
case LookupOptions.Normal:
6971
// Regular lookup: all scopes and builtins.
7072
for (var s = CurrentScope; s != null; s = (Scope)s.OuterScope) {
71-
if (s.Variables.Contains(name)) {
73+
if (s.Variables.TryGetVariable(name, out var v1) && (!v1.IsClassMember || classMembers)) {
7274
scope = s;
7375
break;
7476
}

src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember val
9393
private void TryHandleClassVariable(AssignmentStatement node, IMember value) {
9494
var mex = node.Left.OfType<MemberExpression>().FirstOrDefault();
9595
if (!string.IsNullOrEmpty(mex?.Name) && mex.Target is NameExpression nex && nex.Name.EqualsOrdinal("self")) {
96-
var m = Eval.LookupNameInScopes(nex.Name, out var scope, LookupOptions.Local);
96+
var m = Eval.LookupNameInScopes(nex.Name, out _, LookupOptions.Local);
9797
var cls = m.GetPythonType<IPythonClassType>();
9898
if (cls != null) {
9999
using (Eval.OpenScope(Eval.Module, cls.ClassDefinition, out _)) {

src/Analysis/Ast/Impl/Linting/UndefinedVariables/ExpressionWalker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public override bool Walk(NameExpression node) {
7878
}
7979

8080
var analysis = _walker.Analysis;
81-
var m = analysis.ExpressionEvaluator.LookupNameInScopes(node.Name, out var variableDefinitionScope, out var v);
81+
var m = analysis.ExpressionEvaluator.LookupNameInScopes(node.Name, out var variableDefinitionScope, out var v, LookupOptions.All);
8282
if (m == null) {
8383
_walker.ReportUndefinedVariable(node);
8484
}

src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
using System;
1+
// Copyright(c) Microsoft Corporation
2+
// All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
5+
// this file except in compliance with the License. You may obtain a copy of the
6+
// License at http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
9+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
10+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
11+
// MERCHANTABILITY OR NON-INFRINGEMENT.
12+
//
13+
// See the Apache Version 2.0 License for specific language governing
14+
// permissions and limitations under the License.
15+
16+
using System;
217
using System.Collections.Generic;
318
using System.Linq;
419
using Microsoft.Python.Analysis.Specializations.Typing;
@@ -7,7 +22,7 @@
722
using Microsoft.Python.Core;
823

924
namespace Microsoft.Python.Analysis.Types {
10-
internal partial class PythonClassType : PythonType, IPythonClassType, IGenericType, IEquatable<IPythonClassType> {
25+
internal partial class PythonClassType {
1126
private bool _isGeneric;
1227
private object _genericParameterLock = new object();
1328
private Dictionary<string, PythonClassType> _specificTypeCache;

src/Analysis/Ast/Impl/Types/PythonClassType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace Microsoft.Python.Analysis.Types {
3333
internal partial class PythonClassType : PythonType, IPythonClassType, IGenericType, IEquatable<IPythonClassType> {
3434
private static readonly string[] _classMethods = { "mro", "__dict__", @"__weakref__" };
3535

36-
private ReentrancyGuard<IPythonClassType> _memberGuard = new ReentrancyGuard<IPythonClassType>();
36+
private readonly ReentrancyGuard<IPythonClassType> _memberGuard = new ReentrancyGuard<IPythonClassType>();
3737
private string _genericName;
3838
private List<IPythonType> _bases;
3939
private IReadOnlyList<IPythonType> _mro;

src/Analysis/Ast/Impl/Types/PythonType.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,16 @@ internal bool TrySetTypeId(BuiltinTypeId typeId) {
115115
internal void AddMembers(IEnumerable<IVariable> variables, bool overwrite) {
116116
lock (_lock) {
117117
if (!_readonly) {
118-
foreach (var v in variables.Where(m => overwrite || !Members.ContainsKey(m.Name))) {
119-
// If variable holds function or a class, use value as member.
120-
// If it holds an instance, use the variable itself (i.e. it is a data member).
121-
WritableMembers[v.Name] = v.Value;
118+
foreach (var v in variables.OfType<Variable>()) {
119+
var hasMember = Members.ContainsKey(v.Name);
120+
if (overwrite || !hasMember) {
121+
// If variable holds function or a class, use value as member.
122+
// If it holds an instance, use the variable itself (i.e. it is a data member).
123+
WritableMembers[v.Name] = v.Value;
124+
}
125+
if (hasMember) {
126+
v.IsClassMember = true;
127+
}
122128
}
123129
}
124130
}

src/Analysis/Ast/Impl/Values/Definitions/IVariable.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ public interface IVariable: ILocatedMember {
3535
/// </summary>
3636
IMember Value { get; }
3737

38+
/// <summary>
39+
/// Variable represents class member.
40+
/// </summary>
41+
bool IsClassMember { get; }
42+
3843
/// <summary>
3944
/// Assigns value to the variable.
4045
/// </summary>

src/Analysis/Ast/Impl/Values/Variable.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public Variable(string name, IMember value, VariableSource source, Location loca
3232
public string Name { get; }
3333
public VariableSource Source { get; }
3434
public IMember Value { get; private set; }
35+
public bool IsClassMember { get; internal set; }
3536

3637
public void Assign(IMember value, Location location) {
3738
if (value is IVariable v) {

src/Analysis/Ast/Test/ClassesTests.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ def __init__(self):
632632
sw.Stop();
633633
// Desktop: product time is typically less few seconds second.
634634
// Test run time: typically ~ 20 sec.
635-
sw.ElapsedMilliseconds.Should().BeLessThan(60000);
635+
sw.ElapsedMilliseconds.Should().BeLessThan(60000);
636636
}
637637

638638
[TestMethod, Priority(0)]
@@ -651,6 +651,32 @@ def foo(*args, **kwargs):
651651
analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int);
652652
}
653653

654+
[TestMethod, Priority(0)]
655+
public async Task MemberCtorAssignment() {
656+
const string code = @"
657+
class x:
658+
y: int
659+
z = y
660+
661+
a = x().z
662+
";
663+
var analysis = await GetAnalysisAsync(code);
664+
analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int);
665+
}
666+
667+
[TestMethod, Priority(0)]
668+
public async Task AmbiguousMemberAssignment() {
669+
const string code = @"
670+
class x:
671+
x: int
672+
y = x
673+
674+
a = x().y
675+
";
676+
var analysis = await GetAnalysisAsync(code);
677+
analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int);
678+
}
679+
654680
[TestMethod, Priority(0)]
655681
public async Task IOErrorBase() {
656682
const string code = @"

0 commit comments

Comments
 (0)