Browse Source

[CodeAction] Fixed most extract method tests.

newNRvisualizers
Mike Krüger 14 years ago
parent
commit
4a5d1e851b
  1. 5
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  2. 107
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs
  3. 23
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableLookupVisitor.cs
  4. 146
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableUsageAnalyzation.cs
  5. 19
      ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtractMethodTests.cs
  6. 12
      ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs

5
ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<SignAssembly>true</SignAssembly>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\ICSharpCode.NRefactory.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign>
<AssemblyOriginatorKeyMode>File</AssemblyOriginatorKeyMode>
@ -45,7 +45,7 @@ @@ -45,7 +45,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -376,6 +376,7 @@ @@ -376,6 +376,7 @@
<Compile Include="Refactoring\CodeActions\ImplementAbstractMembersAction.cs" />
<Compile Include="Refactoring\CodeActions\ExtractFieldAction.cs" />
<Compile Include="Completion\ICompletionContextProvider.cs" />
<Compile Include="Refactoring\CodeActions\ExtractMethod\VariableUsageAnalyzation.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">

107
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs

@ -80,11 +80,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod @@ -80,11 +80,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
if (!StaticVisitor.UsesNotStaticMember(context, expression))
method.Modifiers |= Modifiers.Static;
var task = script.InsertWithCursor(context.TranslateString("Extract method"), Script.InsertPosition.Before, method);
task.ContinueWith (delegate {
Action<Task> replaceStatements = delegate {
var target = new IdentifierExpression(methodName);
script.Replace(expression, new InvocationExpression(target));
script.Link(target, method.NameToken);
}, TaskScheduler.FromCurrentSynchronizationContext ());
};
if (task.IsCompleted) {
replaceStatements (null);
} else {
task.ContinueWith (replaceStatements, TaskScheduler.FromCurrentSynchronizationContext ());
}
});
}
@ -113,84 +120,68 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod @@ -113,84 +120,68 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
var usedVariables = VariableLookupVisitor.Analyze(context, statements);
var extractedCodeAnalysis = new DefiniteAssignmentAnalysis(
(Statement)statements [0].Parent,
context.Resolver,
context.CancellationToken);
var inExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
var lastStatement = statements [statements.Count - 1];
extractedCodeAnalysis.SetAnalyzedRange(statements [0], lastStatement);
var statusAfterMethod = new List<Tuple<IVariable, DefiniteAssignmentStatus>>();
foreach (var variable in usedVariables) {
extractedCodeAnalysis.Analyze(
variable.Name,
DefiniteAssignmentStatus.PotentiallyAssigned,
context.CancellationToken);
statusAfterMethod.Add(Tuple.Create(variable, extractedCodeAnalysis.GetStatusAfter(lastStatement)));
}
var stmt = statements [0].GetParent<BlockStatement>();
while (stmt.GetParent<BlockStatement> () != null) {
stmt = stmt.GetParent<BlockStatement>();
}
var wholeCodeAnalysis = new DefiniteAssignmentAnalysis(stmt, context.Resolver, context.CancellationToken);
var statusBeforeMethod = new Dictionary<IVariable, DefiniteAssignmentStatus>();
foreach (var variable in usedVariables) {
wholeCodeAnalysis.Analyze(variable.Name, DefiniteAssignmentStatus.PotentiallyAssigned, context.CancellationToken);
statusBeforeMethod [variable] = extractedCodeAnalysis.GetStatusBefore(statements [0]);
}
inExtractedRegion.SetAnalyzedRange(statements [0], lastStatement);
stmt.AcceptVisitor (inExtractedRegion);
var afterCodeAnalysis = new DefiniteAssignmentAnalysis(stmt, context.Resolver, context.CancellationToken);
var statusAtEnd = new Dictionary<IVariable, DefiniteAssignmentStatus>();
afterCodeAnalysis.SetAnalyzedRange(lastStatement, stmt.Statements.Last(), false, true);
var beforeExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
beforeExtractedRegion.SetAnalyzedRange(statements [0].Parent, statements [0], true, false);
stmt.AcceptVisitor (beforeExtractedRegion);
var afterExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
afterExtractedRegion.SetAnalyzedRange(lastStatement, stmt.Statements.Last(), false, true);
stmt.AcceptVisitor (afterExtractedRegion);
usedVariables.Sort ((l, r) => l.Region.Begin.CompareTo (r.Region.Begin));
foreach (var variable in usedVariables) {
afterCodeAnalysis.Analyze(variable.Name, DefiniteAssignmentStatus.PotentiallyAssigned, context.CancellationToken);
statusBeforeMethod [variable] = extractedCodeAnalysis.GetStatusBefore(statements [0]);
}
var beforeVisitor = new VariableLookupVisitor(context);
beforeVisitor.SetAnalyzedRange(stmt, statements [0], true, false);
stmt.AcceptVisitor(beforeVisitor);
var afterVisitor = new VariableLookupVisitor(context);
afterVisitor.SetAnalyzedRange(lastStatement, stmt, false, true);
stmt.AcceptVisitor(afterVisitor);
foreach (var status in statusAfterMethod) {
if (!beforeVisitor.UsedVariables.Contains(status.Item1) && !afterVisitor.UsedVariables.Contains(status.Item1))
if (!(variable is IParameter) && !beforeExtractedRegion.Has (variable) && !afterExtractedRegion.Has (variable))
continue;
Expression argumentExpression = new IdentifierExpression(status.Item1.Name);
Expression argumentExpression = new IdentifierExpression(variable.Name);
ParameterModifier mod;
switch (status.Item2) {
case DefiniteAssignmentStatus.AssignedAfterTrueExpression:
case DefiniteAssignmentStatus.AssignedAfterFalseExpression:
case DefiniteAssignmentStatus.PotentiallyAssigned:
mod = ParameterModifier.Ref;
argumentExpression = new DirectionExpression(FieldDirection.Ref, argumentExpression);
break;
case DefiniteAssignmentStatus.DefinitelyAssigned:
if (statusBeforeMethod [status.Item1] != DefiniteAssignmentStatus.PotentiallyAssigned)
goto case DefiniteAssignmentStatus.PotentiallyAssigned;
ParameterModifier mod = ParameterModifier.None;
if (inExtractedRegion.GetStatus (variable) == VariableState.Changed) {
if (beforeExtractedRegion.GetStatus (variable) == VariableState.None) {
mod = ParameterModifier.Out;
argumentExpression = new DirectionExpression(FieldDirection.Out, argumentExpression);
break;
// case DefiniteAssignmentStatus.Unassigned:
default:
mod = ParameterModifier.None;
break;
} else {
mod = ParameterModifier.Ref;
argumentExpression = new DirectionExpression(FieldDirection.Ref, argumentExpression);
}
}
method.Parameters.Add(
new ParameterDeclaration(context.CreateShortType(status.Item1.Type), status.Item1.Name, mod));
method.Parameters.Add(new ParameterDeclaration(context.CreateShortType(variable.Type), variable.Name, mod));
invocation.Arguments.Add(argumentExpression);
}
var task = script.InsertWithCursor(context.TranslateString("Extract method"), Script.InsertPosition.Before, method);
task.ContinueWith (delegate {
Action<Task> replaceStatements = delegate {
foreach (var node in statements.Skip (1)) {
script.Remove(node);
}
script.Replace(statements [0], new ExpressionStatement(invocation));
foreach (var variable in usedVariables) {
if ((variable is IParameter) || beforeExtractedRegion.Has (variable) || !afterExtractedRegion.Has (variable))
continue;
script.InsertBefore (statements [0], new VariableDeclarationStatement (context.CreateShortType(variable.Type), variable.Name));
}
var invocationStatement = new ExpressionStatement(invocation);
script.Replace(statements [0], invocationStatement);
script.Link(target, method.NameToken);
}, TaskScheduler.FromCurrentSynchronizationContext ());
};
if (task.IsCompleted) {
replaceStatements (null);
} else {
task.ContinueWith (replaceStatements, TaskScheduler.FromCurrentSynchronizationContext ());
}
});
}
}

23
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableLookupVisitor.cs

@ -33,18 +33,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod @@ -33,18 +33,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
class VariableLookupVisitor : DepthFirstAstVisitor
{
readonly RefactoringContext context;
public List<IVariable> UsedVariables = new List<IVariable> ();
TextLocation startLocation = TextLocation.Empty;
TextLocation endLocation = TextLocation.Empty;
public VariableLookupVisitor (RefactoringContext context)
{
this.context = context;
}
public bool Has (IVariable item1)
{
return UsedVariables.Contains (item1);
}
public void SetAnalyzedRange(AstNode start, AstNode end, bool startInclusive = true, bool endInclusive = true)
{
if (start == null)
@ -54,17 +58,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod @@ -54,17 +58,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
startLocation = startInclusive ? start.StartLocation : start.EndLocation;
endLocation = endInclusive ? end.EndLocation : end.StartLocation;
}
public override void VisitIdentifierExpression(IdentifierExpression identifierExpression)
{
if (startLocation.IsEmpty || startLocation <= identifierExpression.StartLocation && identifierExpression.EndLocation <= endLocation) {
var result = context.Resolve(identifierExpression);
var local = result as LocalResolveResult;
if (local != null && !UsedVariables.Contains(local.Variable))
if (local != null && !UsedVariables.Contains(local.Variable)) {
UsedVariables.Add(local.Variable);
}
}
}
public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement)
{
base.VisitVariableDeclarationStatement(variableDeclarationStatement);
@ -78,14 +83,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod @@ -78,14 +83,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
}
}
public static List<IVariable> Analyze(RefactoringContext context, Expression expression)
{
var visitor = new VariableLookupVisitor(context);
expression.AcceptVisitor(visitor);
return visitor.UsedVariables;
}
public static List<IVariable> Analyze(RefactoringContext context, List<Statement> statements)
{
var visitor = new VariableLookupVisitor(context);

146
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableUsageAnalyzation.cs

@ -0,0 +1,146 @@ @@ -0,0 +1,146 @@
//
// VariableUsageAnalyzation.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using System.Linq;
namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
{
public enum VariableState {
None,
Used,
Changed
}
public class VariableUsageAnalyzation : DepthFirstAstVisitor
{
readonly RefactoringContext context;
readonly List<IVariable> usedVariables;
Dictionary<IVariable, VariableState> states = new Dictionary<IVariable, VariableState> ();
TextLocation startLocation = TextLocation.Empty;
TextLocation endLocation = TextLocation.Empty;
public VariableUsageAnalyzation (RefactoringContext context, List<IVariable> usedVariables)
{
this.context = context;
this.usedVariables = usedVariables;
}
public bool Has(IVariable variable)
{
return states.ContainsKey (variable);
}
public void SetAnalyzedRange(AstNode start, AstNode end, bool startInclusive = true, bool endInclusive = true)
{
if (start == null)
throw new ArgumentNullException("start");
if (end == null)
throw new ArgumentNullException("end");
startLocation = startInclusive ? start.StartLocation : start.EndLocation;
endLocation = endInclusive ? end.EndLocation : end.StartLocation;
states.Clear ();
}
public VariableState GetStatus (IVariable variable)
{
VariableState state;
if (!states.TryGetValue (variable, out state))
return VariableState.None;
return state;
}
void SetState (string identifier, VariableState state)
{
var variable = usedVariables.FirstOrDefault (v => v.Name == identifier);
if (variable == null)
return;
VariableState oldState;
if (states.TryGetValue (variable, out oldState)) {
if (oldState < state)
states [variable] = state;
} else {
states [variable] = state;
}
}
public override void VisitIdentifierExpression(IdentifierExpression identifierExpression)
{
if (startLocation.IsEmpty || startLocation <= identifierExpression.StartLocation && identifierExpression.EndLocation <= endLocation) {
SetState (identifierExpression.Identifier, VariableState.Used);
}
}
public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression)
{
if (startLocation.IsEmpty || startLocation <= assignmentExpression.StartLocation && assignmentExpression.EndLocation <= endLocation) {
var left = assignmentExpression.Left as IdentifierExpression;
if (left != null)
SetState(left.Identifier, VariableState.Changed);
}
base.VisitAssignmentExpression (assignmentExpression);
}
public override void VisitDirectionExpression(DirectionExpression directionExpression)
{
if (startLocation.IsEmpty || startLocation <= directionExpression.StartLocation && directionExpression.EndLocation <= endLocation) {
var expr = directionExpression.Expression as IdentifierExpression;
if (expr != null)
SetState(expr.Identifier, VariableState.Changed);
}
base.VisitDirectionExpression (directionExpression);
}
public override void VisitVariableInitializer(VariableInitializer variableInitializer)
{
if (startLocation.IsEmpty || startLocation <= variableInitializer.StartLocation && variableInitializer.EndLocation <= endLocation) {
SetState(variableInitializer.Name, variableInitializer.Initializer.IsNull ? VariableState.None : VariableState.Changed);
}
base.VisitVariableInitializer(variableInitializer);
}
public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
{
if (startLocation.IsEmpty || startLocation <= unaryOperatorExpression.StartLocation && unaryOperatorExpression.EndLocation <= endLocation) {
if (unaryOperatorExpression.Operator == UnaryOperatorType.Increment || unaryOperatorExpression.Operator == UnaryOperatorType.Decrement ||
unaryOperatorExpression.Operator == UnaryOperatorType.PostIncrement || unaryOperatorExpression.Operator == UnaryOperatorType.PostDecrement) {
var expr = unaryOperatorExpression.Expression as IdentifierExpression;
if (expr != null)
SetState(expr.Identifier, VariableState.Changed);
}
}
base.VisitUnaryOperatorExpression (unaryOperatorExpression);
}
}
}

19
ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtractMethodTests.cs

@ -28,7 +28,6 @@ using ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod; @@ -28,7 +28,6 @@ using ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod;
namespace ICSharpCode.NRefactory.CSharp.CodeActions
{
[Ignore("FIXME!!")]
[TestFixture]
public class ExtractMethodTests : ContextActionTestBase
{
@ -91,7 +90,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -91,7 +90,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
");
}
[Ignore("FIXME!!")]
[Test()]
public void ExtractMethodStaticResultStatementTest()
{
@ -146,7 +144,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -146,7 +144,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
");
}
[Ignore("FIXME!!")]
[Test()]
public void ExtractMethodMultiVariableTest()
{
@ -164,7 +161,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -164,7 +161,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
", @"class TestClass
{
int member;
void NewMethod (ref int j, int i, out int k)
void NewMethod (int i, ref int j, out int k)
{
j = i + j;
k = j + member;
@ -185,7 +182,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -185,7 +182,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
[Test()]
public void TestBug607990()
{
Test<ExtractMethodAction>(@"class TestClass
Test<ExtractMethodAction>(@"using System;
class TestClass
{
void TestMethod ()
{
@ -193,7 +191,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -193,7 +191,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
obj1.ToString();->
}
}
", @"class TestClass
", @"using System;
class TestClass
{
static void NewMethod ()
{
@ -212,7 +211,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -212,7 +211,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
/// <summary>
/// Bug 616193 - Extract method passes param with does not exists any more in main method
/// </summary>
[Ignore("FIXME!!")]
[Test()]
public void TestBug616193()
{
@ -248,7 +246,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -248,7 +246,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
/// <summary>
/// Bug 616199 - Extract method forgets to return a local var which is used in main method
/// </summary>
[Ignore("FIXME!!")]
[Ignore("Fix me!")]
[Test()]
public void TestBug616199()
{
@ -331,8 +329,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -331,8 +329,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
");
}
[Ignore("FIXME!!")]
[Ignore("Fix me!")]
[Test()]
public void ExtractMethodMultiVariableWithLocalReturnVariableTest()
{
@ -352,7 +349,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -352,7 +349,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
", @"class TestClass
{
int member;
void NewMethod (ref int j, int i, out int k, out int test)
void NewMethod (int i, ref int j, out int k, out int test)
{
j = i + j;
k = j + member;

12
ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs

@ -105,9 +105,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -105,9 +105,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
foreach (var node in nodes) {
InsertBefore(entity, node);
}
var t = new Task (() => {});
t.RunSynchronously ();
return t;
var tcs = new TaskCompletionSource<object> ();
tcs.SetResult (null);
return tcs.Task;
}
public override Task InsertWithCursor (string operation, ITypeDefinition parentType, IEnumerable<AstNode> nodes)
@ -121,9 +121,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -121,9 +121,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
InsertText (startOffset, output.Text);
output.RegisterTrackedSegments (this, startOffset);
}
var t = new Task (() => {});
t.RunSynchronously ();
return t;
var tcs = new TaskCompletionSource<object> ();
tcs.SetResult (null);
return tcs.Task;
}
void Rename (AstNode node, string newName)

Loading…
Cancel
Save