diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseExplicitType.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseExplicitType.cs index b31bf78cb3..22e9ff2131 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseExplicitType.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseExplicitType.cs @@ -36,32 +36,54 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public bool IsValid (RefactoringContext context) { var varDecl = GetVariableDeclarationStatement (context); - if (varDecl == null) - return false; - var type = context.Resolve (varDecl.Variables.First ().Initializer).Type; + IType type; + if (varDecl != null) { + type = context.Resolve (varDecl.Variables.First ().Initializer).Type; + } else { + var foreachStatement = GetForeachStatement (context); + if (foreachStatement == null) + return false; + type = context.Resolve (foreachStatement.VariableType).Type; + } + return !type.Equals (SharedTypes.Null) && !type.Equals (SharedTypes.UnknownType); } public void Run (RefactoringContext context) { - var varDecl = GetVariableDeclarationStatement (context); - using (var script = context.StartScript ()) { - var type = context.Resolve (varDecl.Variables.First ().Initializer).Type; - script.Replace (varDecl.Type, context.CreateShortType (type)); + var varDecl = GetVariableDeclarationStatement (context); + if (varDecl != null) { + var type = context.Resolve (varDecl.Variables.First ().Initializer).Type; + script.Replace (varDecl.Type, context.CreateShortType (type)); + } else { + var foreachStatement = GetForeachStatement (context); + var type = context.Resolve (foreachStatement.VariableType).Type; + script.Replace (foreachStatement.VariableType, context.CreateShortType (type)); + } } } + static readonly AstType varType = new SimpleType ("var"); + static VariableDeclarationStatement GetVariableDeclarationStatement (RefactoringContext context) { var result = context.GetNode (); - if (result != null && result.Variables.Count == 1 && !result.Variables.First ().Initializer.IsNull && result.Type.Contains (context.Location.Line, context.Location.Column) && result.Type.IsMatch (new SimpleType ("var"))) { + if (result != null && result.Variables.Count == 1 && !result.Variables.First ().Initializer.IsNull && result.Type.Contains (context.Location.Line, context.Location.Column) && result.Type.IsMatch (varType)) { if (context.Resolve (result.Variables.First ().Initializer) == null) return null; return result; } return null; } + + static ForeachStatement GetForeachStatement (RefactoringContext context) + { + var result = context.GetNode (); + if (result != null && result.VariableType.Contains (context.Location) && result.VariableType.IsMatch (varType)) + return result; + return null; + } } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseVarKeyword.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseVarKeyword.cs index 8aa9c8b66e..049034005e 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseVarKeyword.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/UseVarKeyword.cs @@ -29,25 +29,39 @@ using ICSharpCode.NRefactory.PatternMatching; namespace ICSharpCode.NRefactory.CSharp.Refactoring { - public class UseVarKeyword: IContextAction + public class UseVarKeyword : IContextAction { public bool IsValid (RefactoringContext context) { - return GetVariableDeclarationStatement (context) != null; + return GetVariableDeclarationStatement (context) != null || GetForeachStatement (context) != null; } public void Run (RefactoringContext context) { - var varDecl = GetVariableDeclarationStatement (context); using (var script = context.StartScript ()) { - script.Replace (varDecl.Type, new SimpleType ("var")); + var varDecl = GetVariableDeclarationStatement (context); + if (varDecl != null) { + script.Replace (varDecl.Type, new SimpleType ("var")); + } else { + script.Replace (GetForeachStatement (context).VariableType, new SimpleType ("var")); + } } } + static readonly AstType varType = new SimpleType ("var"); + static VariableDeclarationStatement GetVariableDeclarationStatement (RefactoringContext context) { var result = context.GetNode (); - if (result != null && result.Variables.Count == 1 && !result.Variables.First ().Initializer.IsNull && result.Type.Contains (context.Location.Line, context.Location.Column) && !result.Type.IsMatch (new SimpleType ("var"))) + if (result != null && result.Variables.Count == 1 && !result.Variables.First ().Initializer.IsNull && result.Type.Contains (context.Location) && !result.Type.IsMatch (varType)) + return result; + return null; + } + + static ForeachStatement GetForeachStatement (RefactoringContext context) + { + var result = context.GetNode (); + if (result != null && result.VariableType.Contains (context.Location) && !result.VariableType.IsMatch (varType)) return result; return null; } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/ContextActionTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/ContextActionTestBase.cs new file mode 100644 index 0000000000..4625175739 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/ContextActionTestBase.cs @@ -0,0 +1,48 @@ +// +// ContextActionTestBase.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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 NUnit.Framework; +using ICSharpCode.NRefactory.CSharp.Refactoring; + +namespace ICSharpCode.NRefactory.CSharp.ContextActions +{ + public abstract class ContextActionTestBase + { + protected static string RunContextAction (IContextAction action, string input) + { + var context = new TestRefactoringContext (input); + if (!action.IsValid (context)) + Console.WriteLine ("invalid node is:" + context.GetNode ()); + Assert.IsTrue (action.IsValid (context), action.GetType () + " is invalid."); + + action.Run (context); + + return context.doc.Text; + + } + } +} diff --git a/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs new file mode 100644 index 0000000000..1a683ae85c --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs @@ -0,0 +1,211 @@ +// +// TestRefactoringContext.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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 NUnit.Framework; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.FormattingTests; +using System.Linq; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp.ContextActions +{ + class TestRefactoringContext : RefactoringContext + { + internal IDocument doc; + CSharpParsedFile file; + SimpleProjectContent ctx = new SimpleProjectContent (); + + public override ITypeResolveContext TypeResolveContext { + get { + return ctx; + } + } + + public override bool HasCSharp3Support { + get { + return true; + } + } + + + public override CSharpFormattingOptions FormattingOptions { + get { + return new CSharpFormattingOptions (); + } + } + + public override AstType CreateShortType (IType fullType) + { + var csResolver = new CSharpResolver (TypeResolveContext, System.Threading.CancellationToken.None); + csResolver.CurrentMember = file.GetMember (Location); + csResolver.CurrentTypeDefinition = file.GetInnermostTypeDefinition (Location); + csResolver.CurrentUsingScope = file.GetUsingScope (Location); + var builder = new TypeSystemAstBuilder (csResolver); + return builder.ConvertType (fullType); + } + + public override void ReplaceReferences (IMember member, MemberDeclaration replaceWidth) + { + throw new NotImplementedException (); + } + class MyScript : Script + { + TestRefactoringContext trc; + + public MyScript (TestRefactoringContext trc) : base (trc) + { + this.trc = trc; + } + + public override void Dispose () + { + trc.doc = new ReadOnlyDocument (TestBase.ApplyChanges (trc.doc.Text, new List (Actions.Cast()))); + } + + public override void InsertWithCursor (string operation, AstNode node, InsertPosition defaultPosition) + { + throw new NotImplementedException (); + } + } + public override Script StartScript () + { + return new MyScript (this); + } + + #region Text stuff + public override string EolMarker { get { return Environment.NewLine; } } + + public override bool IsSomethingSelected { get { return SelectionStart >= 0; } } + + public override string SelectedText { get { return IsSomethingSelected ? doc.GetText (SelectionStart, SelectionLength) : ""; } } + + int selectionStart; + public override int SelectionStart { get { return selectionStart; } } + + int selectionEnd; + public override int SelectionEnd { get { return selectionEnd; } } + + public override int SelectionLength { get { return IsSomethingSelected ? SelectionEnd - SelectionStart : 0; } } + + public override int GetOffset (TextLocation location) + { + return doc.GetOffset (location); + } + + public override TextLocation GetLocation (int offset) + { + return doc.GetLocation (offset); + } + + public override string GetText (int offset, int length) + { + return doc.GetText (offset, length); + } + #endregion + + #region Resolving + public override ResolveResult Resolve (AstNode node) + { + var csResolver = new CSharpResolver (TypeResolveContext, System.Threading.CancellationToken.None); + var navigator = new NodeListResolveVisitorNavigator (new[] { node }); + + var visitor = new ICSharpCode.NRefactory.CSharp.Resolver.ResolveVisitor (csResolver, file, navigator); + visitor.Scan (Unit); + return visitor.GetResolveResult (node); + } + #endregion + + + + public TestRefactoringContext (string content) + { + int idx = content.IndexOf ("$"); + if (idx >= 0) + content = content.Substring (0, idx) + content.Substring (idx + 1); + doc = new ReadOnlyDocument (content); + var parser = new CSharpParser (); + Unit = parser.Parse (content); + if (parser.HasErrors) + parser.ErrorPrinter.Errors.ForEach (e => Console.WriteLine (e.Message)); + Assert.IsFalse (parser.HasErrors, "File contains parsing errors."); + file = new TypeSystemConvertVisitor (ctx, "program.cs").Convert (Unit); + ctx.UpdateProjectContent (null, file); + if (idx >= 0) + Location = doc.GetLocation (idx); + } + + internal static void Print (AstNode node) + { + var v = new CSharpOutputVisitor (Console.Out, new CSharpFormattingOptions ()); + node.AcceptVisitor (v, null); + } + + #region IActionFactory implementation + public override TextReplaceAction CreateTextReplaceAction (int offset, int removedChars, string insertedText) + { + return new TestBase.TestTextReplaceAction (offset, removedChars, insertedText); + } + + public override NodeOutputAction CreateNodeOutputAction (int offset, int removedChars, NodeOutput output) + { + return new TestNodeOutputAction (offset, removedChars, output); + } + + public override NodeSelectionAction CreateNodeSelectionAction (AstNode node) + { + throw new NotImplementedException (); + } + + public override FormatTextAction CreateFormatTextAction (Func callback) + { + throw new NotImplementedException (); + } + + public override CreateLinkAction CreateLinkAction (IEnumerable linkedNodes) + { + throw new NotImplementedException (); + } + + public class TestNodeOutputAction : NodeOutputAction + { + public TestNodeOutputAction (int offset, int removedChars, NodeOutput output) : base (offset, removedChars, output) + { + } + + public override void Perform (Script script) + { + } + } + #endregion + } + +} diff --git a/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/UseExplicitTypeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/UseExplicitTypeTests.cs new file mode 100644 index 0000000000..d7be0e17c3 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/UseExplicitTypeTests.cs @@ -0,0 +1,78 @@ +// +// UseVarKeywordTests.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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 NUnit.Framework; +using ICSharpCode.NRefactory.CSharp.Refactoring; + +namespace ICSharpCode.NRefactory.CSharp.ContextActions +{ + [TestFixture] + public class UseExplicitTypeTests : ContextActionTestBase + { + [Test()] + public void SimpleVarDeclaration () + { + string result = RunContextAction (new UseExplicitType (), +@"class TestClass +{ + void Test () + { + $var aVar = this; + } +}"); + Assert.AreEqual (@"class TestClass +{ + void Test () + { + TestClass aVar = this; + } +}", result); + } + + [Test()] + public void ForeachDeclaration () + { + string result = RunContextAction (new UseExplicitType (), +@"class TestClass +{ + void Test () + { + foreach ($var aVar in new TestClass[] { }) { + } + } +}"); + Assert.AreEqual (@"class TestClass +{ + void Test () + { + foreach (TestClass aVar in new TestClass[] { }) { + } + } +}", result); + } + } +} + diff --git a/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/UseVarKeywordTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/UseVarKeywordTests.cs new file mode 100644 index 0000000000..0f079a2a51 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/UseVarKeywordTests.cs @@ -0,0 +1,79 @@ +// +// UseVarKeywordTests.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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 NUnit.Framework; +using ICSharpCode.NRefactory.CSharp.Refactoring; + +namespace ICSharpCode.NRefactory.CSharp.ContextActions +{ + [TestFixture] + public class UseVarKeywordTests : ContextActionTestBase + { + [Test()] + public void SimpleVarDeclaration () + { + string result = RunContextAction (new UseVarKeyword (), +@"class TestClass +{ + void Test () + { + $TestClass aVar = this; + } +}"); + Assert.AreEqual (@"class TestClass +{ + void Test () + { + var aVar = this; + } +}", result); + } + + [Test()] + public void ForeachDeclaration () + { + string result = RunContextAction (new UseVarKeyword (), +@"class TestClass +{ + void Test () + { + foreach ($TestClass aVar in this) { + } + } +}"); + Assert.AreEqual (@"class TestClass +{ + void Test () + { + foreach (var aVar in this) { + } + } +}", result); + } + } +} + diff --git a/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs b/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs index b127d10529..8e5fdcb5f7 100644 --- a/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs +++ b/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs @@ -13,7 +13,7 @@ namespace ICSharpCode.NRefactory.FormattingTests { static IActionFactory factory = new TestFactory (); - class TestTextReplaceAction : TextReplaceAction + internal class TestTextReplaceAction : TextReplaceAction { public TestTextReplaceAction (int offset, int removedChars, string insertedText) : base (offset, removedChars, insertedText) { @@ -32,7 +32,7 @@ namespace ICSharpCode.NRefactory.FormattingTests } } - static string ApplyChanges (string text, List changes) + public static string ApplyChanges (string text, List changes) { changes.Sort ((x, y) => y.Offset.CompareTo (x.Offset)); StringBuilder b = new StringBuilder(text); @@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.FormattingTests var compilationUnit = new CSharpParser ().Parse (new StringReader (input)); compilationUnit.AcceptVisitor (visitor, null); - return new ReadOnlyDocument(ApplyChanges (input, visitor.Changes)); + return new ReadOnlyDocument (ApplyChanges (input, visitor.Changes)); } protected static IDocument Test (CSharpFormattingOptions policy, string input, string expectedOutput) diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 52880c7bcf..809d7fa76a 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -199,6 +199,10 @@ + + + + @@ -219,6 +223,7 @@ + \ No newline at end of file