From b816ae52ff89a75e6353e4d8035e4313ecac7aee Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 22 Mar 2019 12:53:15 -0700 Subject: [PATCH 1/8] preliminary __all__ evaluation --- .../Evaluation/ExpressionEval.Operators.cs | 19 +++++++++++++++++++ src/Analysis/Ast/Impl/Modules/PythonModule.cs | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs index a90a85961..53767e442 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs @@ -13,8 +13,11 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; +using System.Collections.Generic; using Microsoft.Python.Analysis.Modules; using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Types.Collections; using Microsoft.Python.Analysis.Values; using Microsoft.Python.Parsing; using Microsoft.Python.Parsing.Ast; @@ -123,6 +126,22 @@ private IMember GetValueFromBinaryOp(Expression expr) { return left; } + if (binop.Operator == PythonOperator.Add + && left.GetPythonType()?.TypeId == BuiltinTypeId.List + && right.GetPythonType()?.TypeId == BuiltinTypeId.List) { + + var leftVar = GetValueFromExpression(binop.Left); + var rightVar = GetValueFromExpression(binop.Right); + + var leftContents = (leftVar as IPythonCollection)?.Contents; + var rightContents = (rightVar as IPythonCollection)?.Contents; + + var contents = new List(leftContents ?? Array.Empty()); + contents.AddRange(rightContents ?? Array.Empty()); + + return PythonCollectionType.CreateList(Module.Interpreter, GetLoc(expr), contents); + } + return left.IsUnknown() ? right : left; } } diff --git a/src/Analysis/Ast/Impl/Modules/PythonModule.cs b/src/Analysis/Ast/Impl/Modules/PythonModule.cs index 5b0972ebd..a171b58aa 100644 --- a/src/Analysis/Ast/Impl/Modules/PythonModule.cs +++ b/src/Analysis/Ast/Impl/Modules/PythonModule.cs @@ -155,6 +155,14 @@ public virtual IEnumerable GetMemberNames() { return Analysis.ExportedMemberNames; } + var all = Analysis.GlobalScope.Variables["__all__"]; + if (all?.Value is IPythonCollection collection) { + return collection.Contents + .OfType() + .Select(c => c.GetString()) + .Where(s => !string.IsNullOrEmpty(s)); + } + // drop imported modules and typing. return Analysis.GlobalScope.Variables .Where(v => !(v.Value?.GetPythonType() is PythonModule) From 0ca08b31c31846a1de689b1786df005f8c2d5f1f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 27 Mar 2019 14:56:04 -0700 Subject: [PATCH 2/8] remove __all__ lookup, using the other list for now --- src/Analysis/Ast/Impl/Modules/PythonModule.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Analysis/Ast/Impl/Modules/PythonModule.cs b/src/Analysis/Ast/Impl/Modules/PythonModule.cs index a171b58aa..5b0972ebd 100644 --- a/src/Analysis/Ast/Impl/Modules/PythonModule.cs +++ b/src/Analysis/Ast/Impl/Modules/PythonModule.cs @@ -155,14 +155,6 @@ public virtual IEnumerable GetMemberNames() { return Analysis.ExportedMemberNames; } - var all = Analysis.GlobalScope.Variables["__all__"]; - if (all?.Value is IPythonCollection collection) { - return collection.Contents - .OfType() - .Select(c => c.GetString()) - .Where(s => !string.IsNullOrEmpty(s)); - } - // drop imported modules and typing. return Analysis.GlobalScope.Variables .Where(v => !(v.Value?.GetPythonType() is PythonModule) From d878bac9edc975031062f0a7eb51358575573808 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 1 Apr 2019 09:38:41 -0700 Subject: [PATCH 3/8] more eval --- .../Ast/Impl/Analyzer/AnalysisWalker.cs | 5 +++ .../Analyzer/Handlers/AssignmentHandler.cs | 45 +++++++++++++++++++ .../Ast/Impl/Analyzer/ModuleWalker.cs | 7 +-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs b/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs index 836b45eab..e818a7721 100644 --- a/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs @@ -56,6 +56,11 @@ public override bool Walk(AssignmentStatement node) { return base.Walk(node); } + public override bool Walk(AugmentedAssignStatement node) { + AssignmentHandler.HandleAugmentedAssign(node); + return base.Walk(node); + } + public override bool Walk(ExpressionStatement node) { switch (node.Expression) { case ExpressionWithAnnotation ea: diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs index 15068b59f..613dd0772 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs @@ -13,9 +13,12 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Types.Collections; using Microsoft.Python.Analysis.Values; using Microsoft.Python.Core; using Microsoft.Python.Parsing.Ast; @@ -85,6 +88,48 @@ public void HandleAssignment(AssignmentStatement node) { TryHandleClassVariable(node, value); } + public void HandleAugmentedAssign(AugmentedAssignStatement node) { + if (node.Operator != Parsing.PythonOperator.Add) { + return; + } + + // TODO: handle more complicated lvars + if (!(node.Left is NameExpression ne)) { + return; + } + + if (node.Right is ErrorExpression) { + return; + } + + if (Eval.CurrentScope != Eval.GlobalScope || ne.Name != "__all__") { + return; + } + + var rightVar = Eval.GetValueFromExpression(node.Right); + var rightContents = (rightVar as IPythonCollection)?.Contents; + + if (rightContents == null) { + // TODO: Mark __all__ as unusable + return; + } + + Eval.LookupNameInScopes(ne.Name, out var scope, LookupOptions.Normal); + if (scope == null) { + // += on __all__ before it's declared, uh oh + return; + } + + var allContents = (scope.Variables[ne.Name].Value as IPythonCollection)?.Contents; + + var contents = new List(allContents ?? Array.Empty()); + contents.AddRange(rightContents ?? Array.Empty()); + var list = PythonCollectionType.CreateList(Module.Interpreter, Eval.GetLoc(ne), contents); + var source = list.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; + + Eval.DeclareVariable(ne.Name, list, source, Eval.GetLoc(ne)); + } + public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember value) { if (expr?.Annotation == null) { return; diff --git a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs index b8aa5144c..a119fed41 100644 --- a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs @@ -41,9 +41,10 @@ public ModuleWalker(IServiceContainer services, IPythonModule module, PythonAst } public override bool Walk(NameExpression node) { - if (Eval.CurrentScope == Eval.GlobalScope && node.Name == AllVariableName) { - _allReferencesCount++; - } + // if (Eval.CurrentScope == Eval.GlobalScope && node.Name == AllVariableName) { + // _allReferencesCount++; + // } + _allReferencesCount = 1; return base.Walk(node); } From f6d63946f12cd71ddca175d94e8ddf02bf2dcd4f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 1 Apr 2019 15:37:30 -0700 Subject: [PATCH 4/8] support append and extend, reorganize --- .../Ast/Impl/Analyzer/AnalysisWalker.cs | 5 - .../Analyzer/Handlers/AssignmentHandler.cs | 45 ------- .../Ast/Impl/Analyzer/ModuleWalker.cs | 112 +++++++++++++++++- 3 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs b/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs index e818a7721..836b45eab 100644 --- a/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs @@ -56,11 +56,6 @@ public override bool Walk(AssignmentStatement node) { return base.Walk(node); } - public override bool Walk(AugmentedAssignStatement node) { - AssignmentHandler.HandleAugmentedAssign(node); - return base.Walk(node); - } - public override bool Walk(ExpressionStatement node) { switch (node.Expression) { case ExpressionWithAnnotation ea: diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs index 613dd0772..15068b59f 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs @@ -13,12 +13,9 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. -using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Python.Analysis.Types; -using Microsoft.Python.Analysis.Types.Collections; using Microsoft.Python.Analysis.Values; using Microsoft.Python.Core; using Microsoft.Python.Parsing.Ast; @@ -88,48 +85,6 @@ public void HandleAssignment(AssignmentStatement node) { TryHandleClassVariable(node, value); } - public void HandleAugmentedAssign(AugmentedAssignStatement node) { - if (node.Operator != Parsing.PythonOperator.Add) { - return; - } - - // TODO: handle more complicated lvars - if (!(node.Left is NameExpression ne)) { - return; - } - - if (node.Right is ErrorExpression) { - return; - } - - if (Eval.CurrentScope != Eval.GlobalScope || ne.Name != "__all__") { - return; - } - - var rightVar = Eval.GetValueFromExpression(node.Right); - var rightContents = (rightVar as IPythonCollection)?.Contents; - - if (rightContents == null) { - // TODO: Mark __all__ as unusable - return; - } - - Eval.LookupNameInScopes(ne.Name, out var scope, LookupOptions.Normal); - if (scope == null) { - // += on __all__ before it's declared, uh oh - return; - } - - var allContents = (scope.Variables[ne.Name].Value as IPythonCollection)?.Contents; - - var contents = new List(allContents ?? Array.Empty()); - contents.AddRange(rightContents ?? Array.Empty()); - var list = PythonCollectionType.CreateList(Module.Interpreter, Eval.GetLoc(ne), contents); - var source = list.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; - - Eval.DeclareVariable(ne.Name, list, source, Eval.GetLoc(ne)); - } - public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember value) { if (expr?.Annotation == null) { return; diff --git a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs index a119fed41..89c3568e4 100644 --- a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs @@ -13,12 +13,15 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Python.Analysis.Analyzer.Evaluation; using Microsoft.Python.Analysis.Documents; using Microsoft.Python.Analysis.Modules; using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Types.Collections; using Microsoft.Python.Analysis.Values; using Microsoft.Python.Core; using Microsoft.Python.Core.Collections; @@ -33,6 +36,7 @@ internal class ModuleWalker : AnalysisWalker { // A hack to use __all__ export in the most simple case. private int _allReferencesCount; + private bool _allIsUsable = true; public ModuleWalker(IServiceContainer services, IPythonModule module, PythonAst ast) : base(new ExpressionEval(services, module, ast)) { @@ -41,13 +45,111 @@ public ModuleWalker(IServiceContainer services, IPythonModule module, PythonAst } public override bool Walk(NameExpression node) { - // if (Eval.CurrentScope == Eval.GlobalScope && node.Name == AllVariableName) { - // _allReferencesCount++; - // } - _allReferencesCount = 1; + if (Eval.CurrentScope == Eval.GlobalScope && node.Name == AllVariableName) { + _allReferencesCount++; + } + return base.Walk(node); + } + + public override bool Walk(AugmentedAssignStatement node) { + HandleAugmentedAllAssign(node); + return base.Walk(node); + } + + public override bool Walk(CallExpression node) { + HandleAllAppendExtend(node); return base.Walk(node); } + private void HandleAugmentedAllAssign(AugmentedAssignStatement node) { + if (!IsHandleableAll(node.Left)) { + return; + } + + if (node.Right is ErrorExpression) { + return; + } + + if (node.Operator != Parsing.PythonOperator.Add) { + _allIsUsable = false; + return; + } + + var rightVar = Eval.GetValueFromExpression(node.Right); + var rightContents = (rightVar as IPythonCollection)?.Contents; + + if (rightContents == null) { + _allIsUsable = false; + return; + } + + ExtendAll(node.Left, rightContents); + } + + private void HandleAllAppendExtend(CallExpression node) { + if (!(node.Target is MemberExpression me)) { + return; + } + + if (!IsHandleableAll(me.Target)) { + return; + } + + if (node.Args.Count == 0) { + return; + } + + IEnumerable content = null; + var v = Eval.GetValueFromExpression(node.Args[0].Expression); + if (v == null) { + _allIsUsable = false; + return; + } + + switch (me.Name) { + case "append": + content = Enumerable.Repeat(v, 1); + break; + case "extend": + content = (v as IPythonCollection)?.Contents; + break; + } + + if (content == null) { + _allIsUsable = false; + return; + } + + ExtendAll(node, content); + } + + private void ExtendAll(Node declNode, IEnumerable values) { + Eval.LookupNameInScopes(AllVariableName, out var scope, LookupOptions.Normal); + if (scope == null) { + return; + } + + var loc = Eval.GetLoc(declNode); + + var allContents = (scope.Variables[AllVariableName].Value as IPythonCollection)?.Contents; + + var contents = new List(allContents ?? Array.Empty()); + contents.AddRange(values ?? Array.Empty()); + var list = PythonCollectionType.CreateList(Module.Interpreter, loc, contents); + var source = list.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; + + Eval.DeclareVariable(AllVariableName, list, source, loc); + } + + private bool IsHandleableAll(Node node) { + // TODO: handle more complicated lvars + if (!(node is NameExpression ne)) { + return false; + } + + return Eval.CurrentScope == Eval.GlobalScope && ne.Name == AllVariableName; + } + public override bool Walk(PythonAst node) { Check.InvalidOperation(() => Ast == node, "walking wrong AST"); @@ -99,7 +201,7 @@ public void Complete() { SymbolTable.ReplacedByStubs.Clear(); MergeStub(); - if (_allReferencesCount == 1 && GlobalScope.Variables.TryGetVariable(AllVariableName, out var variable) && variable?.Value is IPythonCollection collection) { + if (_allIsUsable && _allReferencesCount >= 1 && GlobalScope.Variables.TryGetVariable(AllVariableName, out var variable) && variable?.Value is IPythonCollection collection) { ExportedMemberNames = collection.Contents .OfType() .Select(c => c.GetString()) From c10301a566b3a8bf06a581e22804c123aadaf315 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 1 Apr 2019 16:42:27 -0700 Subject: [PATCH 5/8] rename to contents --- src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs index 89c3568e4..f5243e81c 100644 --- a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs @@ -99,7 +99,7 @@ private void HandleAllAppendExtend(CallExpression node) { return; } - IEnumerable content = null; + IEnumerable contents = null; var v = Eval.GetValueFromExpression(node.Args[0].Expression); if (v == null) { _allIsUsable = false; @@ -108,19 +108,19 @@ private void HandleAllAppendExtend(CallExpression node) { switch (me.Name) { case "append": - content = Enumerable.Repeat(v, 1); + contents = Enumerable.Repeat(v, 1); break; case "extend": - content = (v as IPythonCollection)?.Contents; + contents = (v as IPythonCollection)?.Contents; break; } - if (content == null) { + if (contents == null) { _allIsUsable = false; return; } - ExtendAll(node, content); + ExtendAll(node, contents); } private void ExtendAll(Node declNode, IEnumerable values) { From c64c6b6f729075150f148bab4e7e049320263f96 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 2 Apr 2019 11:26:28 -0700 Subject: [PATCH 6/8] pull list creation out into function --- .../Analyzer/Evaluation/ExpressionEval.Operators.cs | 5 +---- src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs | 10 ++++------ .../Ast/Impl/Types/Collections/PythonCollectionType.cs | 6 ++++++ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs index 53767e442..1283112f5 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs @@ -136,10 +136,7 @@ private IMember GetValueFromBinaryOp(Expression expr) { var leftContents = (leftVar as IPythonCollection)?.Contents; var rightContents = (rightVar as IPythonCollection)?.Contents; - var contents = new List(leftContents ?? Array.Empty()); - contents.AddRange(rightContents ?? Array.Empty()); - - return PythonCollectionType.CreateList(Module.Interpreter, GetLoc(expr), contents); + return PythonCollectionType.CreateConcatenatedList(Module.Interpreter, GetLoc(expr), leftContents, rightContents); } return left.IsUnknown() ? right : left; diff --git a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs index f5243e81c..4ba11275b 100644 --- a/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs @@ -99,7 +99,7 @@ private void HandleAllAppendExtend(CallExpression node) { return; } - IEnumerable contents = null; + IReadOnlyList contents = null; var v = Eval.GetValueFromExpression(node.Args[0].Expression); if (v == null) { _allIsUsable = false; @@ -108,7 +108,7 @@ private void HandleAllAppendExtend(CallExpression node) { switch (me.Name) { case "append": - contents = Enumerable.Repeat(v, 1); + contents = new List() { v }; break; case "extend": contents = (v as IPythonCollection)?.Contents; @@ -123,7 +123,7 @@ private void HandleAllAppendExtend(CallExpression node) { ExtendAll(node, contents); } - private void ExtendAll(Node declNode, IEnumerable values) { + private void ExtendAll(Node declNode, IReadOnlyList values) { Eval.LookupNameInScopes(AllVariableName, out var scope, LookupOptions.Normal); if (scope == null) { return; @@ -133,9 +133,7 @@ private void ExtendAll(Node declNode, IEnumerable values) { var allContents = (scope.Variables[AllVariableName].Value as IPythonCollection)?.Contents; - var contents = new List(allContents ?? Array.Empty()); - contents.AddRange(values ?? Array.Empty()); - var list = PythonCollectionType.CreateList(Module.Interpreter, loc, contents); + var list = PythonCollectionType.CreateConcatenatedList(Module.Interpreter, loc, allContents, values); var source = list.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; Eval.DeclareVariable(AllVariableName, list, source, loc); diff --git a/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs b/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs index 68135c117..ac3352e3f 100644 --- a/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs +++ b/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs @@ -18,6 +18,7 @@ using System.Linq; using Microsoft.Python.Analysis.Values; using Microsoft.Python.Analysis.Values.Collections; +using Microsoft.Python.Core; namespace Microsoft.Python.Analysis.Types.Collections { /// @@ -99,6 +100,11 @@ public static IPythonCollection CreateList(IPythonInterpreter interpreter, Locat return new PythonCollection(collectionType, location, contents, flatten); } + public static IPythonCollection CreateConcatenatedList(IPythonInterpreter interpreter, LocationInfo location, params IReadOnlyList[] manyContents) { + var contents = manyContents?.ExcludeDefault().SelectMany().ToList() ?? new List(); + return CreateList(interpreter, location, contents); + } + public static IPythonCollection CreateTuple(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList contents) { var collectionType = new PythonCollectionType(null, BuiltinTypeId.Tuple, interpreter, false); return new PythonCollection(collectionType, location, contents); From 0596f33e25f4bb33d309d9b8e8d9a6e1e36615fa Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 2 Apr 2019 11:59:18 -0700 Subject: [PATCH 7/8] shorten operator code --- .../Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs index 1283112f5..0e4a08786 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs @@ -130,13 +130,10 @@ private IMember GetValueFromBinaryOp(Expression expr) { && left.GetPythonType()?.TypeId == BuiltinTypeId.List && right.GetPythonType()?.TypeId == BuiltinTypeId.List) { - var leftVar = GetValueFromExpression(binop.Left); - var rightVar = GetValueFromExpression(binop.Right); + var leftVar = GetValueFromExpression(binop.Left) as IPythonCollection; + var rightVar = GetValueFromExpression(binop.Right) as IPythonCollection; - var leftContents = (leftVar as IPythonCollection)?.Contents; - var rightContents = (rightVar as IPythonCollection)?.Contents; - - return PythonCollectionType.CreateConcatenatedList(Module.Interpreter, GetLoc(expr), leftContents, rightContents); + return PythonCollectionType.CreateConcatenatedList(Module.Interpreter, GetLoc(expr), leftVar?.Contents, rightVar?.Contents); } return left.IsUnknown() ? right : left; From a865e8c405827235fd88dfd86931dd6272714fe1 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 2 Apr 2019 13:27:28 -0700 Subject: [PATCH 8/8] testing --- src/LanguageServer/Test/ImportsTests.cs | 169 ++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/src/LanguageServer/Test/ImportsTests.cs b/src/LanguageServer/Test/ImportsTests.cs index d7df84ecc..cfcf130f1 100644 --- a/src/LanguageServer/Test/ImportsTests.cs +++ b/src/LanguageServer/Test/ImportsTests.cs @@ -459,5 +459,174 @@ public async Task FromImport_ModuleAffectsPackage(string appCodeImport) { comps = cs.GetCompletions(analysis, new SourceLocation(2, 21)); comps.Should().HaveLabels("X"); } + + [TestMethod, Priority(0)] + public async Task AllSimple() { + var module1Code = @" +class A: + def foo(self): + pass + pass + +class B: + def bar(self): + pass + pass + +__all__ = ['A'] +"; + + var appCode = @" +from module1 import * + +A(). +B(). +"; + + var module1Uri = TestData.GetTestSpecificUri("module1.py"); + var appUri = TestData.GetTestSpecificUri("app.py"); + + var root = Path.GetDirectoryName(appUri.AbsolutePath); + await CreateServicesAsync(root, PythonVersions.LatestAvailable3X); + var rdt = Services.GetService(); + var analyzer = Services.GetService(); + + rdt.OpenDocument(module1Uri, module1Code); + + var app = rdt.OpenDocument(appUri, appCode); + await analyzer.WaitForCompleteAnalysisAsync(); + var analysis = await app.GetAnalysisAsync(-1); + + var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion); + var comps = cs.GetCompletions(analysis, new SourceLocation(4, 5)); + comps.Should().HaveLabels("foo"); + + comps = cs.GetCompletions(analysis, new SourceLocation(5, 5)); + comps.Should().NotContainLabels("bar"); + } + + [DataRow(@" +other = ['B'] +__all__ = ['A'] + other")] + [DataRow(@" +other = ['B'] +__all__ = ['A'] +__all__ += other")] + [DataRow(@" +other = ['B'] +__all__ = ['A'] +__all__.extend(other)")] + [DataRow(@" +__all__ = ['A'] +__all__.append('B')")] + [DataTestMethod, Priority(0)] + public async Task AllComplex(string allCode) { + var module1Code = @" +class A: + def foo(self): + pass + pass + +class B: + def bar(self): + pass + pass + +class C: + def baz(self): + pass + pass +" + allCode; + + var appCode = @" +from module1 import * + +A(). +B(). +C(). +"; + + var module1Uri = TestData.GetTestSpecificUri("module1.py"); + var appUri = TestData.GetTestSpecificUri("app.py"); + + var root = Path.GetDirectoryName(appUri.AbsolutePath); + await CreateServicesAsync(root, PythonVersions.LatestAvailable3X); + var rdt = Services.GetService(); + var analyzer = Services.GetService(); + + rdt.OpenDocument(module1Uri, module1Code); + + var app = rdt.OpenDocument(appUri, appCode); + await analyzer.WaitForCompleteAnalysisAsync(); + var analysis = await app.GetAnalysisAsync(-1); + + var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion); + var comps = cs.GetCompletions(analysis, new SourceLocation(4, 5)); + comps.Should().HaveLabels("foo"); + + comps = cs.GetCompletions(analysis, new SourceLocation(5, 5)); + comps.Should().HaveLabels("bar"); + + comps = cs.GetCompletions(analysis, new SourceLocation(6, 5)); + comps.Should().NotContainLabels("baz"); + } + + [DataRow(@" +__all__ = ['A'] +__all__.something(A)")] + [DataRow(@" +__all__ = ['A'] +__all__ *= ['B']")] + [DataRow(@" +__all__ = ['A'] +__all__ += 1234")] + [DataRow(@" +__all__ = ['A'] +__all__.extend(123)")] + [DataRow(@" +__all__ = ['A'] +__all__.extend(nothing)")] + [DataTestMethod, Priority(0)] + public async Task AllUnsupported(string allCode) { + var module1Code = @" +class A: + def foo(self): + pass + pass + +class B: + def bar(self): + pass + pass +" + allCode; + + var appCode = @" +from module1 import * + +A(). +B(). +"; + + var module1Uri = TestData.GetTestSpecificUri("module1.py"); + var appUri = TestData.GetTestSpecificUri("app.py"); + + var root = Path.GetDirectoryName(appUri.AbsolutePath); + await CreateServicesAsync(root, PythonVersions.LatestAvailable3X); + var rdt = Services.GetService(); + var analyzer = Services.GetService(); + + rdt.OpenDocument(module1Uri, module1Code); + + var app = rdt.OpenDocument(appUri, appCode); + await analyzer.WaitForCompleteAnalysisAsync(); + var analysis = await app.GetAnalysisAsync(-1); + + var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion); + var comps = cs.GetCompletions(analysis, new SourceLocation(4, 5)); + comps.Should().HaveLabels("foo"); + + comps = cs.GetCompletions(analysis, new SourceLocation(5, 5)); + comps.Should().HaveLabels("bar"); + } } }