From 938bc2dfdf8c8de3e49d176183db5bc0ddb55c70 Mon Sep 17 00:00:00 2001 From: mrward Date: Sat, 25 Sep 2010 19:17:30 +0100 Subject: [PATCH] Resolve IronPython local variables up to the current expression line and not beyond. --- .../Src/PythonLocalVariableResolver.cs | 27 ++++-- .../Project/Src/PythonResolverContext.cs | 12 +-- .../ResolveLocalClassInstanceTests.cs | 74 ++++++++++++++- .../Test/Utils/PythonResolverTestsHelper.cs | 5 + .../Project/ICSharpCode.Scripting.csproj | 1 + .../Project/Src/ScriptingLocalMethod.cs | 40 ++++++++ .../Test/ICSharpCode.Scripting.Tests.csproj | 2 + .../Resolver/ScriptingLocalMethodTests.cs | 91 +++++++++++++++++++ 8 files changed, 228 insertions(+), 24 deletions(-) create mode 100644 src/AddIns/BackendBindings/Scripting/Project/Src/ScriptingLocalMethod.cs create mode 100644 src/AddIns/BackendBindings/Scripting/Test/Resolver/ScriptingLocalMethodTests.cs diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs index f9d148dce6..b6bd9b8a93 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonLocalVariableResolver.cs @@ -1,20 +1,21 @@ // 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 Microsoft.Scripting; -using Microsoft.Scripting.Hosting; -using Microsoft.Scripting.Runtime; using System; using System.Collections.Generic; using System.Text; +using ICSharpCode.Scripting; using ICSharpCode.SharpDevelop.Dom; using IronPython; using IronPython.Compiler; using IronPython.Compiler.Ast; -using IronPython.Runtime; using IronPython.Hosting; +using IronPython.Runtime; using IronPython.Runtime.Exceptions; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Runtime; namespace ICSharpCode.PythonBinding { @@ -54,7 +55,7 @@ namespace ICSharpCode.PythonBinding { this.variableName = variableName; ast.Walk(this); - return TypeName; + return TypeName; } public override bool Walk(AssignmentStatement node) @@ -116,22 +117,28 @@ namespace ICSharpCode.PythonBinding public ResolveResult Resolve(PythonResolverContext resolverContext, ExpressionResult expressionResult) { - return GetLocalVariable(resolverContext, expressionResult.Expression); + return GetLocalVariable(resolverContext, expressionResult); } /// /// Tries to find the type that matches the local variable name. /// - LocalResolveResult GetLocalVariable(PythonResolverContext resolverContext, string expression) + LocalResolveResult GetLocalVariable(PythonResolverContext resolverContext, ExpressionResult expressionResult) { - PythonLocalVariableResolver resolver = new PythonLocalVariableResolver(); - string typeName = resolver.Resolve(expression, resolverContext.FileContent); + string code = GetLocalMethodCode(resolverContext.FileContent, expressionResult); + string typeName = Resolve(expressionResult.Expression, code); if (typeName != null) { - return CreateLocalResolveResult(typeName, expression, resolverContext); + return CreateLocalResolveResult(typeName, expressionResult.Expression, resolverContext); } return null; } + string GetLocalMethodCode(string fullCode, ExpressionResult expressionResult) + { + ScriptingLocalMethod localMethod = new ScriptingLocalMethod(fullCode); + return localMethod.GetCode(expressionResult.Region.BeginLine); + } + LocalResolveResult CreateLocalResolveResult(string typeName, string identifier, PythonResolverContext resolverContext) { IClass resolvedClass = resolverContext.GetClass(typeName); diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonResolverContext.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonResolverContext.cs index 76a7cdd799..7daa8e531f 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonResolverContext.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonResolverContext.cs @@ -23,21 +23,15 @@ namespace ICSharpCode.PythonBinding public PythonResolverContext(ParseInformation parseInfo, string fileContent) { this.fileContent = fileContent; - GetCompilationUnits(parseInfo); + GetCompilationUnit(parseInfo); GetProjectContent(); } - void GetCompilationUnits(ParseInformation parseInfo) - { - compilationUnit = GetCompilationUnit(parseInfo); - } - - ICompilationUnit GetCompilationUnit(ParseInformation parseInfo) + void GetCompilationUnit(ParseInformation parseInfo) { if (parseInfo != null) { - return parseInfo.CompilationUnit; + compilationUnit = parseInfo.CompilationUnit; } - return null; } void GetProjectContent() diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/ResolveLocalClassInstanceTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/ResolveLocalClassInstanceTests.cs index 04cacf214f..68d24f040d 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/ResolveLocalClassInstanceTests.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Resolver/ResolveLocalClassInstanceTests.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using ICSharpCode.SharpDevelop.Dom; using NUnit.Framework; using PythonBinding.Tests.Utils; using UnitTesting.Tests.Utils; @@ -30,18 +31,81 @@ namespace PythonBinding.Tests.Resolver resolverHelper.ProjectContent.ClassesInProjectContent.Add(testClass); resolverHelper.ProjectContent.SetClassToReturnFromGetClass("Test.Test1", testClass); + } + + [Test] + public void Resolve_LocalVariableIsCreatedOnPreviousLine_ResolveResultVariableNameIsA() + { string python = "a = Test.Test1()\r\n" + "a"; - resolverHelper.Resolve("a", python); + resolverHelper.Resolve("a", python); + + string name = resolverHelper.LocalResolveResult.VariableName; + + Assert.AreEqual("a", name); } - + [Test] - public void ResolveResultVariableName() + public void Resolve_LocalVariableIsCreatedOnPreviousLine_ResolveResultResolvedTypeIsTestClass() { - string name = resolverHelper.LocalResolveResult.VariableName; - Assert.AreEqual("a", name); + string python = + "a = Test.Test1()\r\n" + + "a"; + + resolverHelper.Resolve("a", python); + + IReturnType resolvedType = resolverHelper.LocalResolveResult.ResolvedType; + IClass underlyingClass = resolvedType.GetUnderlyingClass(); + + Assert.AreEqual(testClass, underlyingClass); + } + + [Test] + public void Resolve_LocalVariableIsReDefinedAfterLineBeingConsidered_ResolveResultResolvedTypeIsTestClass() + { + string python = + "a = Test.Test1()\r\n" + + "a\r\n" + + "a = Unknown.Unknown()\r\n"; + + ExpressionResult expression = new ExpressionResult("a"); + expression.Region = new DomRegion( + beginLine: 1, + beginColumn: 0, + endLine: 1, + endColumn: 1); + + resolverHelper.Resolve(expression, python); + + IReturnType resolvedType = resolverHelper.LocalResolveResult.ResolvedType; + IClass underlyingClass = resolvedType.GetUnderlyingClass(); + + Assert.AreEqual(testClass, underlyingClass); + } + + [Test] + public void Resolve_LocalVariableIsReDefinedAfterLineBeingConsideredAndExpressionRegionEndLineIsMinusOne_ResolveResultResolvedTypeIsTestClass() + { + string python = + "a = Test.Test1()\r\n" + + "a\r\n" + + "a = Unknown.Unknown()\r\n"; + + ExpressionResult expression = new ExpressionResult("a"); + expression.Region = new DomRegion( + beginLine: 1, + beginColumn: 0, + endLine: -1, + endColumn: 1); + + resolverHelper.Resolve(expression, python); + + IReturnType resolvedType = resolverHelper.LocalResolveResult.ResolvedType; + IClass underlyingClass = resolvedType.GetUnderlyingClass(); + + Assert.AreEqual(testClass, underlyingClass); } } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/PythonResolverTestsHelper.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/PythonResolverTestsHelper.cs index dcad496a05..bee429a27f 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/PythonResolverTestsHelper.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/PythonResolverTestsHelper.cs @@ -44,6 +44,11 @@ namespace PythonBinding.Tests.Utils public ResolveResult Resolve(string expression, string code) { ExpressionResult expressionResult = new ExpressionResult(expression); + return Resolve(expressionResult, code); + } + + public ResolveResult Resolve(ExpressionResult expressionResult, string code) + { ResolveResult = Resolver.Resolve(expressionResult, ParseInfo, code); return ResolveResult; } diff --git a/src/AddIns/BackendBindings/Scripting/Project/ICSharpCode.Scripting.csproj b/src/AddIns/BackendBindings/Scripting/Project/ICSharpCode.Scripting.csproj index b37c760e48..32dc5f572e 100644 --- a/src/AddIns/BackendBindings/Scripting/Project/ICSharpCode.Scripting.csproj +++ b/src/AddIns/BackendBindings/Scripting/Project/ICSharpCode.Scripting.csproj @@ -96,6 +96,7 @@ + diff --git a/src/AddIns/BackendBindings/Scripting/Project/Src/ScriptingLocalMethod.cs b/src/AddIns/BackendBindings/Scripting/Project/Src/ScriptingLocalMethod.cs new file mode 100644 index 0000000000..82422bdfd3 --- /dev/null +++ b/src/AddIns/BackendBindings/Scripting/Project/Src/ScriptingLocalMethod.cs @@ -0,0 +1,40 @@ +// 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; + +namespace ICSharpCode.Scripting +{ + /// + /// Used to extract the code for a method based on the range of lines. + /// + public class ScriptingLocalMethod + { + string code = String.Empty; + + public ScriptingLocalMethod(string code) + { + if (code != null) { + this.code = code; + } + } + + public string GetCode(int endLine) + { + int endIndex = FindIndexForEndOfLine(endLine); + if (endIndex > 0) { + return code.Substring(0, endIndex); + } + return code; + } + + int FindIndexForEndOfLine(int line) + { + int index = 0; + for (int i = 0; i <= line; ++i) { + index = code.IndexOf('\n', index) + 1; + } + return index; + } + } +} diff --git a/src/AddIns/BackendBindings/Scripting/Test/ICSharpCode.Scripting.Tests.csproj b/src/AddIns/BackendBindings/Scripting/Test/ICSharpCode.Scripting.Tests.csproj index ef80e6b8d3..0514adefab 100644 --- a/src/AddIns/BackendBindings/Scripting/Test/ICSharpCode.Scripting.Tests.csproj +++ b/src/AddIns/BackendBindings/Scripting/Test/ICSharpCode.Scripting.Tests.csproj @@ -62,6 +62,7 @@ + @@ -104,6 +105,7 @@ + diff --git a/src/AddIns/BackendBindings/Scripting/Test/Resolver/ScriptingLocalMethodTests.cs b/src/AddIns/BackendBindings/Scripting/Test/Resolver/ScriptingLocalMethodTests.cs new file mode 100644 index 0000000000..c20bc2aa39 --- /dev/null +++ b/src/AddIns/BackendBindings/Scripting/Test/Resolver/ScriptingLocalMethodTests.cs @@ -0,0 +1,91 @@ +// 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 NUnit.Framework; +using ICSharpCode.Scripting; + +namespace ICSharpCode.Scripting.Tests.Resolver +{ + [TestFixture] + public class ScriptingLocalMethodTests + { + ScriptingLocalMethod method; + + void CreateLocalMethod(string code) + { + method = new ScriptingLocalMethod(code); + } + + [Test] + public void GetCode_EndLineIsZeroAndTwoLinesOfCode_ReturnsFirstLineOfCode() + { + string fullCode = + "first\r\n" + + "second"; + + CreateLocalMethod(fullCode); + + int endLine = 0; + string code = method.GetCode(endLine); + + string expectedCode = "first\r\n"; + + Assert.AreEqual(expectedCode, code); + } + + [Test] + public void GetCode_EndLineIsOneAndThreeLinesOfCode_ReturnsFirstTwoLinesOfCode() + { + string fullCode = + "first\r\n" + + "second\r\n" + + "third"; + + CreateLocalMethod(fullCode); + + int endLine = 1; + string code = method.GetCode(endLine); + + string expectedCode = + "first\r\n" + + "second\r\n"; + + Assert.AreEqual(expectedCode, code); + } + + [Test] + public void GetCode_EndLineIsOneAndTwoLinesOfCode_ReturnsFirstTwoLinesOfCode() + { + string fullCode = + "first\r\n" + + "second"; + + CreateLocalMethod(fullCode); + + int endLine = 1; + string code = method.GetCode(endLine); + + string expectedCode = + "first\r\n" + + "second"; + + Assert.AreEqual(expectedCode, code); + } + + [Test] + public void GetCode_EndLineIsOneAndCodeIsNull_ReturnsEmptyString() + { + string fullCode = null; + + CreateLocalMethod(fullCode); + + int endLine = 1; + string code = method.GetCode(endLine); + + string expectedCode = String.Empty; + + Assert.AreEqual(expectedCode, code); + } + } +}