From b3bfd0ef5a86872cbaa932bb7ea050088afeef50 Mon Sep 17 00:00:00 2001 From: mrward Date: Sun, 3 Oct 2010 13:17:22 +0100 Subject: [PATCH] Fix problem with an IronPython local variable not being resolved after assigning a value to its property and then calling a method on the variable. --- .../Project/PythonBinding.csproj | 1 + .../Src/PythonLocalVariableAssignment.cs | 74 +++++++++++++++++++ .../Src/PythonLocalVariableResolver.cs | 58 +-------------- .../Test/PythonBinding.Tests.csproj | 1 + .../PythonLocalVariableAssignmentTests.cs | 33 +++++++++ .../PythonLocalVariableResolverTests.cs | 30 +++++--- 6 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableAssignment.cs create mode 100644 src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableAssignmentTests.cs diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj index cf40f49d34..28cb8b0091 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj @@ -106,6 +106,7 @@ + diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableAssignment.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableAssignment.cs new file mode 100644 index 0000000000..ce70fa80a3 --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableAssignment.cs @@ -0,0 +1,74 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using IronPython.Compiler.Ast; + +namespace ICSharpCode.PythonBinding +{ + public class PythonLocalVariableAssignment + { + AssignmentStatement assignment; + string variableName = String.Empty; + string typeName = String.Empty; + + public PythonLocalVariableAssignment(AssignmentStatement assignment) + { + this.assignment = assignment; + ParseAssignment(); + } + + public string TypeName { + get { return typeName; } + } + + public string VariableName { + get { return variableName; } + } + + public bool IsLocalVariableAssignment() + { + return !String.IsNullOrEmpty(variableName); + } + + void ParseAssignment() + { + NameExpression nameExpression = assignment.Left[0] as NameExpression; + CallExpression callExpression = assignment.Right as CallExpression; + if ((nameExpression != null) && (callExpression != null)) { + variableName = nameExpression.Name; + typeName = GetTypeName(callExpression.Target); + } + } + + /// + /// Gets the fully qualified name of the type from the expression. + /// + /// + /// The expression is the first target of a call expression. + /// + /// A call expression is a method or constructor call (right hand side of expression below): + /// + /// a = Root.Test.Class1() + /// + /// So the expression passed to this method will be a field expression in the + /// above example which refers to Class1. The next target will be a field + /// expression referring to Test. The The last target will be a name expression + /// referring to Root. + /// + /// If we have + /// + /// a = Class1() + /// + /// then the expression will be a name expression referring to Class1. + /// + string GetTypeName(Expression expression) + { + NameExpression nameExpression = expression as NameExpression; + if (nameExpression != null) { + return nameExpression.Name; + } + return PythonControlFieldExpression.GetMemberName(expression as MemberExpression); + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs index dc94c1c47c..01ae483503 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs @@ -27,8 +27,6 @@ namespace ICSharpCode.PythonBinding PythonClassResolver classResolver; string variableName = String.Empty; string typeName; - AssignmentStatement currentAssignStatement; - bool foundVariableAssignment; public PythonLocalVariableResolver(PythonClassResolver classResolver) { @@ -66,63 +64,15 @@ namespace ICSharpCode.PythonBinding public override bool Walk(AssignmentStatement node) { - currentAssignStatement = node; - foundVariableAssignment = false; - return base.Walk(node); - } - - public override bool Walk(NameExpression node) - { - if (currentAssignStatement != null) { - string nodeName = node.Name; - if (nodeName == variableName) { - foundVariableAssignment = true; + PythonLocalVariableAssignment localVariableAssignment = new PythonLocalVariableAssignment(node); + if (localVariableAssignment.IsLocalVariableAssignment()) { + if (localVariableAssignment.VariableName == variableName) { + typeName = localVariableAssignment.TypeName; } } return base.Walk(node); } - public override bool Walk(CallExpression node) - { - if (foundVariableAssignment) { - typeName = GetTypeName(node.Target); - currentAssignStatement = null; - foundVariableAssignment = false; - } - return base.Walk(node); - } - - /// - /// Gets the fully qualified name of the type from the expression. - /// - /// - /// - /// The expression is the first target of a call expression. - /// - /// A call expression is a method or constructor call (right hand side of expression below): - /// - /// a = Root.Test.Class1() - /// - /// So the expression passed to this method will be a field expression in the - /// above example which refers to Class1. The next target will be a field - /// expression referring to Test. The The last target will be a name expression - /// referring to Root. - /// - /// If we have - /// - /// a = Class1() - /// - /// then the expression will be a name expression referring to Class1. - /// - public static string GetTypeName(Expression node) - { - NameExpression nameExpression = node as NameExpression; - if (nameExpression != null) { - return nameExpression.Name; - } - return PythonControlFieldExpression.GetMemberName(node as MemberExpression); - } - public ResolveResult Resolve(PythonResolverContext resolverContext) { return GetLocalVariable(resolverContext); diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj index 308b86f889..f2b8498cbb 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj @@ -351,6 +351,7 @@ + diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableAssignmentTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableAssignmentTests.cs new file mode 100644 index 0000000000..d287b39b63 --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableAssignmentTests.cs @@ -0,0 +1,33 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using ICSharpCode.PythonBinding; +using IronPython.Compiler.Ast; +using NUnit.Framework; +using PythonBinding.Tests.Utils; + +namespace PythonBinding.Tests.Resolver +{ + [TestFixture] + public class PythonLocalVariableAssignmentTests + { + [Test] + public void TypeName_CallExpressionTargetIsNotNameOrMemberExpression_ReturnsEmptyStringAndDoesNotGetStuckInInfiniteLoop() + { + string code = "a = 2"; + AssignmentStatement statement = PythonParserHelper.GetAssignmentStatement(code); + Expression constantExpression = statement.Right; + + CallExpression callExpression = new CallExpression(constantExpression, new Arg[0]); + List expressions = new List(statement.Left); + statement = new AssignmentStatement(expressions.ToArray(), callExpression); + + PythonLocalVariableAssignment localVariableAssignment = new PythonLocalVariableAssignment(statement); + string typeName = localVariableAssignment.TypeName; + + Assert.AreEqual(String.Empty, typeName); + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableResolverTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableResolverTests.cs index 24e5d2b6aa..5a2db77695 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableResolverTests.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/PythonLocalVariableResolverTests.cs @@ -26,8 +26,9 @@ namespace PythonBinding.Tests.Resolver { string code = "a = Class1()"; Resolve("a", code); + string expectedTypeName = "Class1"; - Assert.AreEqual("Class1", typeName); + Assert.AreEqual(expectedTypeName, typeName); } /// @@ -42,8 +43,9 @@ namespace PythonBinding.Tests.Resolver "b = Class2()"; Resolve("a", code); + string expectedTypeName = "Class1"; - Assert.AreEqual("Class1", typeName); + Assert.AreEqual(expectedTypeName, typeName); } [Test] @@ -59,6 +61,7 @@ namespace PythonBinding.Tests.Resolver public void Resolve_CodeIsNull_ReturnsNull() { Resolve("a", null); + Assert.IsNull(typeName); } @@ -67,7 +70,9 @@ namespace PythonBinding.Tests.Resolver { string code = "a = Test.Class1()"; Resolve("a", code); - Assert.AreEqual("Test.Class1", typeName); + string expectedTypeName = "Test.Class1"; + + Assert.AreEqual(expectedTypeName, typeName); } [Test] @@ -75,16 +80,23 @@ namespace PythonBinding.Tests.Resolver { string code = "a = Root.Test.Class1()"; Resolve("a", code); - Assert.AreEqual("Root.Test.Class1", typeName); + string expectedTypeName = "Root.Test.Class1"; + + Assert.AreEqual(expectedTypeName, typeName); } [Test] - public void GetTypeName_ExpressionIsNotNameOrMemberExpression_ReturnsEmptyStringAndDoesNotGetStuckInInfiniteLoop() + public void Resolve_AssignmentToPropertyOnLocalVariableThenMethodCallOnLocalVariable_CorrectLocalVariableTypeReturned() { - AssignmentStatement statement = PythonParserHelper.GetAssignmentStatement("a = 2"); - Expression expression = statement.Right; - string typeName = PythonLocalVariableResolver.GetTypeName(expression); - Assert.AreEqual(String.Empty, typeName); + string code = + "connection = OracleClient.OracleConnection()\r\n" + + "connection.ConnectionString = connectionString\r\n" + + "connection.Open()\r\n" + + "connection"; + + Resolve("connection", code); + + Assert.AreEqual("OracleClient.OracleConnection", typeName); } } }