(lambdaParams);
+ paramList = SyntaxFactory.ParameterList(lambdaParamList);
+ }
+
+ var lambda = SyntaxFactory.ParenthesizedLambdaExpression(paramList, methodBody as CSharpSyntaxNode);
+ newArgument = SyntaxFactory.Argument(lambda);
+
+ // Replace location with final method body.
+ root = root.ReplaceNode(argument, newArgument.WithAdditionalAnnotations(Formatter.Annotation));
+ }
+ }
+
+ // Replace document syntax root.
+ solution = solution.WithDocumentSyntaxRoot(document.Id, root);
+ }
+
+ // Because the method location may have changed after replacing references, find it again.
+ methodSymbol = SymbolFinder.FindSourceDeclarationsAsync(
+ solution,
+ methodSymbol.Name,
+ false,
+ cancellationToken).Result.Single();
+
+ // Remove inlined method.
+ foreach (var methodLocation in methodSymbol.Locations)
+ {
+ if (!methodLocation.IsInSource) continue;
+
+ var tree = methodLocation.SourceTree;
+ var documentId = solution.GetDocumentId(tree);
+ var root = await tree.GetRootAsync(cancellationToken);
+ var node = root.FindNode(methodLocation.SourceSpan);
+ root = root.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
+ solution = solution.WithDocumentSyntaxRoot(documentId, root);
+ }
+
+ // Return new solution.
+ return await Task.FromResult(solution);
+ }
+
+ ///
+ /// Replace invocation with method body.
+ ///
+ /// The root of the document.
+ /// The invocation node to replace.
+ /// The body of the inlined method.
+ /// The parameters of the inlined method.
+ ///
+ /// The new root with the invocation node replaced by the inline method body.
+ ///
+ SyntaxNode ReplaceInvocationExpression(
+ SyntaxNode root,
+ InvocationExpressionSyntax invocationNode,
+ SyntaxNode methodBody,
+ ParameterListSyntax methodParams)
+ {
+ var replacementNode = methodBody;
+
+ var args = invocationNode.ArgumentList.Arguments;
+ for (var paramIdx = 0; paramIdx < methodParams.Parameters.Count(); paramIdx++)
+ {
+ var param = methodParams.Parameters[paramIdx];
+ var arg = (paramIdx < args.Count) ? args[paramIdx].Expression : param.Default.Value;
+
+ var ids = replacementNode.DescendantNodesAndSelf()
+ .Where(n => n.IsKind(SyntaxKind.IdentifierName))
+ .Where(n => (n as IdentifierNameSyntax).Identifier.ValueText == param.Identifier.ValueText);
+
+ replacementNode = replacementNode.ReplaceNodes(ids, (n1, n2) => arg).WithAdditionalAnnotations(Formatter.Annotation);
+ }
+
+ // Replace location with final method body.
+ return root.ReplaceNode(invocationNode, replacementNode.WithAdditionalAnnotations(Formatter.Annotation));
+ }
+
+ }
+}
diff --git a/RefactoringEssentials/CodeRefactorings.CSharp.html b/RefactoringEssentials/CodeRefactorings.CSharp.html
index c1324518..72b91aee 100644
--- a/RefactoringEssentials/CodeRefactorings.CSharp.html
+++ b/RefactoringEssentials/CodeRefactorings.CSharp.html
@@ -15,7 +15,7 @@
-->
Supported Refactorings
- 101 code refactorings for C#
+ 102 code refactorings for C#
- Adds another accessor (AddAnotherAccessorCodeRefactoringProvider)
- Add braces (AddBracesCodeRefactoringProvider)
@@ -82,6 +82,7 @@ Supported Refactorings
- Import static class with using directive in file (ImportStaticClassWithUsingCodeRefactoringProvider)
- Initialize auto property from constructor parameter (InitializeAutoPropertyFromConstructorParameterCodeRefactoringProvider)
- Initialize field from constructor parameter (InitializeFieldFromConstructorParameterCodeRefactoringProvider)
+ - Put the method's body into the body of its callers and remove the method. (InlineMethodAction)
- Insert anonymous method signature (InsertAnonymousMethodSignatureCodeRefactoringProvider)
- Invert conditional operator (InvertConditionalOperatorCodeRefactoringProvider)
- Invert if (InvertIfCodeRefactoringProvider)
diff --git a/RefactoringEssentials/Properties/AssemblyInfoBase.tt b/RefactoringEssentials/Properties/AssemblyInfoBase.tt
index e61cd12a..daf80ebc 100644
--- a/RefactoringEssentials/Properties/AssemblyInfoBase.tt
+++ b/RefactoringEssentials/Properties/AssemblyInfoBase.tt
@@ -1,20 +1,20 @@
-<#@ template debug="false" hostspecific="true" language="C#" #>
-<#@ output extension=".cs" #>
-<#@ include file="../Versioning.t4.template" #>
-<# ReadVersions(Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "../RefactoringEssentials.version")); #>
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyCompany("ICSharpCode")]
-[assembly: AssemblyProduct("Refactoring Essentials")]
-[assembly: AssemblyCopyright("Copyright 2010-2016 AlphaSierraPapa and Xamarin Inc.")]
-[assembly: AssemblyVersion("<#= generatedFullVersion #>")]
-[assembly: AssemblyFileVersion("<#= generatedNuGetVersion #>")]
-
-// This sets the default COM visibility of types in the assembly to invisible.
-// If you need to expose a type to COM, use [ComVisible(true)] on that type.
+<#@ template debug="false" hostspecific="true" language="C#" #>
+<#@ output extension=".cs" #>
+<#@ include file="../Versioning.t4.template" #>
+<# ReadVersions(Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "../RefactoringEssentials.version")); #>
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyCompany("ICSharpCode")]
+[assembly: AssemblyProduct("Refactoring Essentials")]
+[assembly: AssemblyCopyright("Copyright 2010-2016 AlphaSierraPapa and Xamarin Inc.")]
+[assembly: AssemblyVersion("<#= generatedFullVersion #>")]
+[assembly: AssemblyFileVersion("<#= generatedNuGetVersion #>")]
+
+// This sets the default COM visibility of types in the assembly to invisible.
+// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/RefactoringEssentials/RefactoringEssentials.Library.nuspec b/RefactoringEssentials/RefactoringEssentials.Library.nuspec
index 9732a708..3ee32286 100644
--- a/RefactoringEssentials/RefactoringEssentials.Library.nuspec
+++ b/RefactoringEssentials/RefactoringEssentials.Library.nuspec
@@ -1,36 +1,36 @@
-
-
-
-
- RefactoringEssentials.Library
- 3.2.0
- Refactoring Essentials
- IC#Code
- IC#Code
- https://visualstudiogallery.msdn.microsoft.com/site/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc/eula?licenseType=None
- http://vsrefactoringessentials.com/
- https://raw.githubusercontent.com/icsharpcode/RefactoringEssentials/master/RefactoringEssentials/Images/refactoringessentials-logo32.png
- false
- Analyzers and Code Fixes as a library, intended for IDE builders
- Please see https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2 for more information.
- Copyright (c) 2014-2015 AlphaSierraPapa
- VS 2015 Roslyn Analyzer Refactoring
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ RefactoringEssentials.Library
+ 3.2.0
+ Refactoring Essentials
+ IC#Code
+ IC#Code
+ https://visualstudiogallery.msdn.microsoft.com/site/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc/eula?licenseType=None
+ http://vsrefactoringessentials.com/
+ https://raw.githubusercontent.com/icsharpcode/RefactoringEssentials/master/RefactoringEssentials/Images/refactoringessentials-logo32.png
+ false
+ Analyzers and Code Fixes as a library, intended for IDE builders
+ Please see https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2 for more information.
+ Copyright (c) 2014-2015 AlphaSierraPapa
+ VS 2015 Roslyn Analyzer Refactoring
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RefactoringEssentials/RefactoringEssentials.csproj b/RefactoringEssentials/RefactoringEssentials.csproj
index 2315fa6b..8d0d908a 100644
--- a/RefactoringEssentials/RefactoringEssentials.csproj
+++ b/RefactoringEssentials/RefactoringEssentials.csproj
@@ -56,6 +56,7 @@
+
@@ -793,4 +794,4 @@
4
true
-
+
\ No newline at end of file
diff --git a/RefactoringEssentials/RefactoringEssentials.nuspec b/RefactoringEssentials/RefactoringEssentials.nuspec
index fbecb39d..b7e87a61 100644
--- a/RefactoringEssentials/RefactoringEssentials.nuspec
+++ b/RefactoringEssentials/RefactoringEssentials.nuspec
@@ -1,27 +1,27 @@
-
-
-
-
- RefactoringEssentials
- 3.2.0
- Refactoring Essentials
- IC#Code
- IC#Code
- https://visualstudiogallery.msdn.microsoft.com/site/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc/eula?licenseType=None
- http://vsrefactoringessentials.com/
- https://raw.githubusercontent.com/icsharpcode/RefactoringEssentials/master/RefactoringEssentials/Images/refactoringessentials-logo32.png
- false
- Analyzers and Code Fixes for Visual Studio 2015 (Roslyn-based). Intended for build integration. If you need Visual Studio integration only, please check out the VSIX (Visual Studio extension) from our Web site.
-
- Please see https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2 for more information.
-
- Copyright (c) 2014-2015 AlphaSierraPapa
- VS 2015 Roslyn Analyzer Refactoring
- true
-
-
-
-
-
-
-
+
+
+
+
+ RefactoringEssentials
+ 3.2.0
+ Refactoring Essentials
+ IC#Code
+ IC#Code
+ https://visualstudiogallery.msdn.microsoft.com/site/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc/eula?licenseType=None
+ http://vsrefactoringessentials.com/
+ https://raw.githubusercontent.com/icsharpcode/RefactoringEssentials/master/RefactoringEssentials/Images/refactoringessentials-logo32.png
+ false
+ Analyzers and Code Fixes for Visual Studio 2015 (Roslyn-based). Intended for build integration. If you need Visual Studio integration only, please check out the VSIX (Visual Studio extension) from our Web site.
+
+ Please see https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2 for more information.
+
+ Copyright (c) 2014-2015 AlphaSierraPapa
+ VS 2015 Roslyn Analyzer Refactoring
+ true
+
+
+
+
+
+
+
diff --git a/RefactoringEssentials/RefactoringEssentials.tt b/RefactoringEssentials/RefactoringEssentials.tt
index 8b12badd..35baf46c 100644
--- a/RefactoringEssentials/RefactoringEssentials.tt
+++ b/RefactoringEssentials/RefactoringEssentials.tt
@@ -1,30 +1,30 @@
-<#@ template debug="false" hostspecific="true" language="C#" #>
-<#@ output extension=".nuspec" #>
-
-<#@ include file="Versioning.t4.template" #>
-<# ReadVersions(Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "RefactoringEssentials.version")); #>
-
-
- RefactoringEssentials
- <#= generatedNuGetVersion #>
- Refactoring Essentials
- IC#Code
- IC#Code
- https://visualstudiogallery.msdn.microsoft.com/site/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc/eula?licenseType=None
- http://vsrefactoringessentials.com/
- https://raw.githubusercontent.com/icsharpcode/RefactoringEssentials/master/RefactoringEssentials/Images/refactoringessentials-logo32.png
- false
- Analyzers and Code Fixes for Visual Studio 2015 (Roslyn-based). Intended for build integration. If you need Visual Studio integration only, please check out the VSIX (Visual Studio extension) from our Web site.
-
- Please see <#= releaseNotesLink #> for more information.
-
- Copyright (c) 2014-2015 AlphaSierraPapa
- VS 2015 Roslyn Analyzer Refactoring
- true
-
-
-
-
-
-
-
+<#@ template debug="false" hostspecific="true" language="C#" #>
+<#@ output extension=".nuspec" #>
+
+<#@ include file="Versioning.t4.template" #>
+<# ReadVersions(Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "RefactoringEssentials.version")); #>
+
+
+ RefactoringEssentials
+ <#= generatedNuGetVersion #>
+ Refactoring Essentials
+ IC#Code
+ IC#Code
+ https://visualstudiogallery.msdn.microsoft.com/site/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc/eula?licenseType=None
+ http://vsrefactoringessentials.com/
+ https://raw.githubusercontent.com/icsharpcode/RefactoringEssentials/master/RefactoringEssentials/Images/refactoringessentials-logo32.png
+ false
+ Analyzers and Code Fixes for Visual Studio 2015 (Roslyn-based). Intended for build integration. If you need Visual Studio integration only, please check out the VSIX (Visual Studio extension) from our Web site.
+
+ Please see <#= releaseNotesLink #> for more information.
+
+ Copyright (c) 2014-2015 AlphaSierraPapa
+ VS 2015 Roslyn Analyzer Refactoring
+ true
+
+
+
+
+
+
+
diff --git a/RefactoringEssentials/RefactoringEssentials.version b/RefactoringEssentials/RefactoringEssentials.version
index a52d61bc..bf97b205 100644
--- a/RefactoringEssentials/RefactoringEssentials.version
+++ b/RefactoringEssentials/RefactoringEssentials.version
@@ -1,3 +1,3 @@
-nuget_version=3.2.0
-full_version=3.2.0.0
+nuget_version=3.2.0
+full_version=3.2.0.0
release_notes_link=https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2
\ No newline at end of file
diff --git a/RefactoringEssentials/Util/Reflection.md b/RefactoringEssentials/Util/Reflection.md
index caba69ba..c8d654a0 100644
--- a/RefactoringEssentials/Util/Reflection.md
+++ b/RefactoringEssentials/Util/Reflection.md
@@ -1,103 +1,103 @@
-Reflection in Utils
-=====================
-
-* CaseCorrector
- * `Microsoft.CodeAnalysis.CaseCorrection.CaseCorrector` (*Microsoft.CodeAnalysis.Workspaces*)
- * `Annotation`
- * `CaseCorrectAsync()`
-
-* CSharpSyntaxContext
- * `Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery.AbstractSyntaxContext` (*Microsoft.CodeAnalysis.Workspaces*)
- * `LeftToken`
- * `TargetToken`
- * `SyntaxTree`
- * `IsPreProcessorDirectiveContext`
- * `IsAnyExpressionContext`
- * `IsStatementContext`
- * `IsAttributeNameContext`
- * `IsInQuery`
- * `Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery.CSharpSyntaxContext` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `CreateContext()`
- * `IsIsOrAsTypeContext`
- * `IsInstanceContext`
- * `IsNonAttributeExpressionContext`
- * `IsPreProcessorKeywordContext`
- * `ContainingTypeDeclaration`
- * `IsParameterTypeContext`
- * `IsMemberDeclarationContext()`
- * `IsTypeDeclarationContext()`
- * `IsInNonUserCode`
- * `IsIsOrAsContext`
- * `IsTypeAttributeContext()`
- * `IsDefiniteCastTypeContext`
- * `IsObjectCreationTypeContext`
- * `IsGenericTypeArgumentContext`
- * `IsLocalVariableDeclarationContext`
- * `IsFixedVariableDeclarationContext`
- * `IsPossibleLambdaOrAnonymousMethodParameterTypeContext`
- * `IsImplicitOrExplicitOperatorTypeContext`
- * `IsPrimaryFunctionExpressionContext`
- * `IsCrefContext`
- * `IsDelegateReturnTypeContext`
- * `IsEnumBaseListContext`
- * `IsConstantExpressionContext`
- * `IsMemberAttributeContext`
- * `PrecedingModifiers`
- * `IsTypeOfExpressionContext`
- * `ContainingTypeOrEnumDeclaration`
-
-* ExpressionSyntaxExtensions
- * `Microsoft.CodeAnalysis.CSharp.Extensions.ExpressionSyntaxExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `CastIfPossible()`
- * `TryReduceOrSimplifyExplicitName()`
-
-* ITypeSymbolExtensions
- * `Microsoft.CodeAnalysis.CSharp.Extensions.ITypeSymbolExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `GenerateTypeSyntax()`
- * `ContainingTypesOrSelfHasUnsafeKeyword()`
- * `Microsoft.CodeAnalysis.Shared.Extensions.ITypeSymbolExtensions` (*Microsoft.CodeAnalysis.Workspaces*)
- * `InheritsFromOrEqualsIgnoringConstruction()`
- * `RemoveUnavailableTypeParameters()`
- * `RemoveUnnamedErrorTypes()`
- * `ReplaceTypeParametersBasedOnTypeConstraints()`
- * `SubstituteTypes()` (2x)
-
-* SignatureComparer
- * `Microsoft.CodeAnalysis.Shared.Utilities.SignatureComparer` (*Microsoft.CodeAnalysis.Workspaces*)
- * `HaveSameSignature()` (5x)
- * `HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors()`
-
-* SpeculationAnalyzer
- * `Microsoft.CodeAnalysis.Shared.Utilities.AbstractSpeculationAnalyzer´8` (*Microsoft.CodeAnalysis.Workspaces*)
- * `SymbolsForOriginalAndReplacedNodesAreCompatible()`
- * `ReplacementChangesSemantics()`
- * `SymbolInfosAreCompatible()`
- * `Microsoft.CodeAnalysis.CSharp.Utilities.SpeculationAnalyzer` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `CreateSpeculativeSemanticModelForNode()`
-
-* SyntaxExtensions
- * `Microsoft.CodeAnalysis.CSharp.Extensions.ParenthesizedExpressionSyntaxExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `CanRemoveParentheses()`
- * `Microsoft.CodeAnalysis.CSharp.Extensions.MemberDeclarationSyntaxExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `GetLocalDeclarationMap()`
- * `Microsoft.CodeAnalysis.CSharp.Extensions.MemberDeclarationSyntaxExtensions+LocalDeclarationMap` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `GetLocalDeclarationMap()`
- * `Microsoft.CodeAnalysis.Shared.Extensions.SyntaxTokenExtensions` (*Microsoft.CodeAnalysis.Workspaces*)
- * `GetAncestors()`
- * `CanReplaceWithReducedName()` (2x)
-
-* SyntaxNodeExtensions
- * `Microsoft.CodeAnalysis.CSharp.Extensions.SyntaxNodeExtensions`(*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `ContainsInterleavedDirective()`
-
-* TypeExtensions
- * `Microsoft.CodeAnalysis.CSharp.Extensions.ITypeSymbolExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
- * `GenerateTypeSyntax()`
- * `Microsoft.CodeAnalysis.FindSymbols.DependentTypeFinder` (*Microsoft.CodeAnalysis.Workspaces*)
- * `FindDerivedClassesAsync()`
-
-* TypeGenerator
- * `Microsoft.CodeAnalysis.CodeGeneration.TypeGenerator` (*Microsoft.CodeAnalysis.Workspaces*)
- * `CreateArrayTypeSymbol()`
- * `CreatePointerTypeSymbol()`
- * `Construct()`
+Reflection in Utils
+=====================
+
+* CaseCorrector
+ * `Microsoft.CodeAnalysis.CaseCorrection.CaseCorrector` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `Annotation`
+ * `CaseCorrectAsync()`
+
+* CSharpSyntaxContext
+ * `Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery.AbstractSyntaxContext` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `LeftToken`
+ * `TargetToken`
+ * `SyntaxTree`
+ * `IsPreProcessorDirectiveContext`
+ * `IsAnyExpressionContext`
+ * `IsStatementContext`
+ * `IsAttributeNameContext`
+ * `IsInQuery`
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery.CSharpSyntaxContext` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `CreateContext()`
+ * `IsIsOrAsTypeContext`
+ * `IsInstanceContext`
+ * `IsNonAttributeExpressionContext`
+ * `IsPreProcessorKeywordContext`
+ * `ContainingTypeDeclaration`
+ * `IsParameterTypeContext`
+ * `IsMemberDeclarationContext()`
+ * `IsTypeDeclarationContext()`
+ * `IsInNonUserCode`
+ * `IsIsOrAsContext`
+ * `IsTypeAttributeContext()`
+ * `IsDefiniteCastTypeContext`
+ * `IsObjectCreationTypeContext`
+ * `IsGenericTypeArgumentContext`
+ * `IsLocalVariableDeclarationContext`
+ * `IsFixedVariableDeclarationContext`
+ * `IsPossibleLambdaOrAnonymousMethodParameterTypeContext`
+ * `IsImplicitOrExplicitOperatorTypeContext`
+ * `IsPrimaryFunctionExpressionContext`
+ * `IsCrefContext`
+ * `IsDelegateReturnTypeContext`
+ * `IsEnumBaseListContext`
+ * `IsConstantExpressionContext`
+ * `IsMemberAttributeContext`
+ * `PrecedingModifiers`
+ * `IsTypeOfExpressionContext`
+ * `ContainingTypeOrEnumDeclaration`
+
+* ExpressionSyntaxExtensions
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.ExpressionSyntaxExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `CastIfPossible()`
+ * `TryReduceOrSimplifyExplicitName()`
+
+* ITypeSymbolExtensions
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.ITypeSymbolExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `GenerateTypeSyntax()`
+ * `ContainingTypesOrSelfHasUnsafeKeyword()`
+ * `Microsoft.CodeAnalysis.Shared.Extensions.ITypeSymbolExtensions` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `InheritsFromOrEqualsIgnoringConstruction()`
+ * `RemoveUnavailableTypeParameters()`
+ * `RemoveUnnamedErrorTypes()`
+ * `ReplaceTypeParametersBasedOnTypeConstraints()`
+ * `SubstituteTypes()` (2x)
+
+* SignatureComparer
+ * `Microsoft.CodeAnalysis.Shared.Utilities.SignatureComparer` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `HaveSameSignature()` (5x)
+ * `HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors()`
+
+* SpeculationAnalyzer
+ * `Microsoft.CodeAnalysis.Shared.Utilities.AbstractSpeculationAnalyzer´8` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `SymbolsForOriginalAndReplacedNodesAreCompatible()`
+ * `ReplacementChangesSemantics()`
+ * `SymbolInfosAreCompatible()`
+ * `Microsoft.CodeAnalysis.CSharp.Utilities.SpeculationAnalyzer` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `CreateSpeculativeSemanticModelForNode()`
+
+* SyntaxExtensions
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.ParenthesizedExpressionSyntaxExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `CanRemoveParentheses()`
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.MemberDeclarationSyntaxExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `GetLocalDeclarationMap()`
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.MemberDeclarationSyntaxExtensions+LocalDeclarationMap` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `GetLocalDeclarationMap()`
+ * `Microsoft.CodeAnalysis.Shared.Extensions.SyntaxTokenExtensions` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `GetAncestors()`
+ * `CanReplaceWithReducedName()` (2x)
+
+* SyntaxNodeExtensions
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.SyntaxNodeExtensions`(*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `ContainsInterleavedDirective()`
+
+* TypeExtensions
+ * `Microsoft.CodeAnalysis.CSharp.Extensions.ITypeSymbolExtensions` (*Microsoft.CodeAnalysis.CSharp.Workspaces*)
+ * `GenerateTypeSyntax()`
+ * `Microsoft.CodeAnalysis.FindSymbols.DependentTypeFinder` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `FindDerivedClassesAsync()`
+
+* TypeGenerator
+ * `Microsoft.CodeAnalysis.CodeGeneration.TypeGenerator` (*Microsoft.CodeAnalysis.Workspaces*)
+ * `CreateArrayTypeSymbol()`
+ * `CreatePointerTypeSymbol()`
+ * `Construct()`
diff --git a/Tests/CSharp/CodeRefactorings/InlineMethodActionTests.cs b/Tests/CSharp/CodeRefactorings/InlineMethodActionTests.cs
new file mode 100644
index 00000000..038b7340
--- /dev/null
+++ b/Tests/CSharp/CodeRefactorings/InlineMethodActionTests.cs
@@ -0,0 +1,555 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Host.Mef;
+using NUnit.Framework;
+using RefactoringEssentials.CSharp.CodeRefactorings.Uncategorized;
+using RefactoringEssentials.Tests.Common;
+
+namespace RefactoringEssentials.Tests.CSharp.CodeRefactorings
+{
+ ///
+ /// Tests the .
+ ///
+ [TestFixture]
+ public class InlineMethodActionTests : CSharpCodeRefactoringTestBase
+ {
+ [Test]
+ [Description("Do not suggest refactoring unless the method signature is marked (excluding arguments).")]
+ public void DoesNotRefactorWhenNotOnMethodSignature()
+ {
+ // Ensure the inline method action is only invoked when the method's signature is marked.
+
+ TestWrongContext(
+@"
+class Animal
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int Bar(int a, int b) {
+ //// Simple math.
+ return $(a + b);
+ }
+
+}");
+
+ TestWrongContext(
+@"
+class Animal
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int Bar(int a, int b) {
+ //// Simple math.
+ $return (a + b);
+ }
+
+}");
+
+ TestWrongContext(
+@"
+class Animal
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int Bar(int a, int $b) {
+ //// Simple math.
+ return (a + b);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Do not inline a method with errors, that would just make more errors.")]
+ public void DoesNotRefactorMethodWithErrors()
+ {
+ // Do no inline a method with errors, that would only propogate the errors.
+
+ TestWrongContext(
+@"
+class Animal
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int $Bar(int a, int b) {
+ //// Simple math.
+ return (a + b) This is invalid syntax!;
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Do not inline an external public method. It may be referenced externally and refactoring may be breaking.")]
+ public void DoesNotRefactorPublicExternalMethods()
+ {
+ // Class Animal is public as is Animal.Bar. Therefore Animal.Bar is an external reference.
+ // External references may be in use by software outside the scope of the current workspace.
+ // Removing an external reference could break unknown consumers.
+
+ TestWrongContext(
+@"
+public class Animal
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int $Bar(int a, int b) {
+ //// Simple math.
+ return (a + b);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Do not inline a method without any references, that is not our interest.")]
+ public void DoesNotRefactorUnreferencedMethods()
+ {
+ // Animal.Bar is not referenced by any code and that is not the concern of the inline method action.
+
+ TestWrongContext(
+@"
+class Animal
+{
+
+ // Method without any references.
+ public int $Bar(int a, int b) {
+ //// Simple math.
+ return (a + b);
+ }
+}");
+ }
+
+ [Test]
+ [Description("Do not inline a method with more than 1 line of code. This test may be invalid as the capability of method inlining is expanded.")]
+ public void DoesNotRefactorComplexMethod()
+ {
+ // Animal.Bar is considered complex because it has more than one line of code.
+ // At this time we are not refactoring complex methods.
+
+ TestWrongContext(
+@"
+class Animal
+{
+ public void Foo() {
+ var x = Bar(1, 2);
+ }
+
+ // A complex method, one with multiple lines of code.
+ public int $Bar(int a, int b) {
+ //// Simple math.
+ var result = (a + b);
+ return result;
+ }
+}");
+ }
+
+ [Test]
+ [Description("Do not inline a method when contains internal member access.")]
+ public void DoNotInlineMethodBecauseOfInternalMemberAccess()
+ {
+ // Do not inline method Animal.AddLeg because its body references internal member Animal.leg.
+ // AddLeg references internal member legs.
+ // Class Dog cannot refer to Animal.legs.
+
+ TestWrongContext(
+@"class Dog : Animal {
+
+ public void DoLegs() {
+ AddLeg();
+ AddLeg();
+ AddLeg();
+ AddLeg();
+ }
+}
+
+class Animal
+{
+ private int legs = 0;
+
+ protected void $AddLeg() {
+ //// Simple math.
+ legs++;
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method with return type. int Bar()")]
+ public void InlineReturnMethod()
+ {
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int $Bar(int a, int b) {
+ //// Simple math.
+ return (a + b);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ var x = (5 + 1);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method without return type. void Bar()")]
+ public void InlineVoidMethod()
+ {
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ Bar(5, 1);
+ }
+
+ public void $Bar(int a, int b) {
+ //// Simple math.
+ Console.Writeline(a + b);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ Console.Writeline(5 + 1);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method and replace a member access invocation. foo.Bar(1, 2)")]
+ public void InlineMethodReplaceMemberAccess()
+ {
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ var tc = new TestClass();
+ tc.Bar(5, 1);
+ }
+
+ public void $Bar(int a, int b) {
+ //// Simple math.
+ Console.Writeline(a + b);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ var tc = new TestClass();
+ Console.Writeline(5 + 1);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method and replace a delegate reference.")]
+ public void InlineMethodReplaceDelegate()
+ {
+ // Method Foo invokes Foo3 with a delegate Bar.
+ // The delegate Bar will be replaced with inline method in the form of lambda syntax.
+
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ Foo3(Bar);
+ }
+
+ public void Foo3(Action act)
+ {
+ var x = act();
+ }
+
+ public void $Bar() {
+ Console.Writeline(""Blah"");
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ Foo3(() => Console.Writeline(""Blah""));
+ }
+
+ public void Foo3(Action act)
+ {
+ var x = act();
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method with a lambda, remove optional value.")]
+ public void InlineMethodWithParenthesizedLambdaReference()
+ {
+ // Method Foo invokes Foo3 with a delegate Bar.
+ // The delegate Bar will be replaced with inline method in the form of lambda syntax.
+ // Additionally, the lambda method will contain the parameters of the inline method Bar.
+ // Note that optional parameter b is not supported by lambda syntax and its default value is removed.
+ // See the below stackoverflow answer for more detail.
+ // http://stackoverflow.com/a/14249310/2735
+
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ Foo3(Bar);
+ }
+
+ public void Foo3(Func act)
+ {
+ var x = act(1, 2);
+ }
+
+ public int $Bar(int a, int b = 1) {
+ //// Simple math.
+ return (a + b);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ Foo3((int a, int b) => (a + b));
+ }
+
+ public void Foo3(Func act)
+ {
+ var x = act(1, 2);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method with multiple references.")]
+ public void InlineMethodWithMultipleReferences()
+ {
+ // Test multiple references and syntax changes.
+ // Modifying code will rearrange a document's syntax.
+ // If 2 locations in the same document are known, changing the code for one location may invalidate the location of the other.
+
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int $Bar(int a, int b) {
+ return (a + b);
+ }
+
+ public void Foo2() {
+ var x2 = Bar(10, 2);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ var x = (5 + 1);
+ }
+
+ public void Foo2() {
+ var x2 = (10 + 2);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method with optional parameters.")]
+ public void InlineMethodWithOptionalParam()
+ {
+ // Ensure that the optional parameter c in Bar is not lost.
+
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int $Bar(int a, int b, int c = 0) {
+ //// Simple math.
+ return (a + b + c);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ var x = (5 + 1 + 0);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method and ensure it properly traverses document.")]
+ public void InlineMethodTraversesDocument()
+ {
+ // While replacing syntax, if not handled, it is possible to undo one change with another.
+ // This is a gotcha of working with syntax trees and immutable types.
+ // In this example, Foo and Foo2 should be updated with the inline method body while method Bar is removed.
+ // If not properly handled, it is possible that Foo is updated and subsequenty undone when Foo2 is updated.
+
+ Test(
+@"class TestClass
+{
+ public void Foo() {
+ var x = Bar(5, 1);
+ }
+
+ public int $Bar(int a, int b, int c = 0) {
+ //// Simple math.
+ return (a + b + c);
+ }
+
+ public void Foo2() {
+ var x = Bar(6, 2);
+ }
+
+}",
+@"class TestClass
+{
+ public void Foo() {
+ var x = (5 + 1 + 0);
+ }
+
+ public void Foo2() {
+ var x = (6 + 2 + 0);
+ }
+
+}");
+ }
+
+ [Test]
+ [Description("Inline a method with references in another classes.")]
+ public void InlineMethodWithReferenceInAnotherClass()
+ {
+ Test(
+@"class Dog : Animal
+{
+ public void DoLegs() {
+ Bark();
+ }
+}
+
+class Animal
+{
+ protected void $Bark() {
+ //// Simple math.
+ Console.Writeline(""bark"");
+ }
+}",
+@"class Dog : Animal
+{
+ public void DoLegs() {
+ Console.Writeline(""bark"");
+ }
+}
+
+class Animal
+{
+}");
+ }
+
+ [Test]
+ [Description("Test to ensure that references withing multiple documents are properly handled.")]
+ public void MultipleDocumentTest()
+ {
+ // ClassA is in document named ClassA, ClassB is in document named ClassB.
+ // Method ClassA.Foo invokes ClassB.Bar, the invocation of ClassB.Bar will be replaced by body of ClassB.Bar.
+
+ var solutionId = SolutionId.CreateNewId();
+ var projectId = ProjectId.CreateNewId();
+ var projectName = "ProjectA";
+ var document1Id = DocumentId.CreateNewId(projectId);
+ var document1Name = "ClassA";
+ var document2Id = DocumentId.CreateNewId(projectId);
+ var document2Name = "ClassB";
+
+ // Create Test Workspace
+ var testWorkspace = new AdhocWorkspace(MefHostServices.DefaultHost, "Temp");
+ testWorkspace.AddSolution(WorkspaceHelpers.CreateSolution(
+ solutionId,
+ new[] {
+ WorkspaceHelpers.CreateCSharpProject(projectId, projectName, new []
+ {
+ WorkspaceHelpers.CreateDocument(document1Id, document1Name,
+@"class ClassA
+{
+ public void Foo() {
+ var x = ClassB.Bar(5, 1);
+ }
+}"),
+ WorkspaceHelpers.CreateDocument(document2Id, document2Name,
+@"class ClassB
+{
+ public static int $Bar(int a, int b, int c = 0) {
+ //// Simple math.
+ return (a + b + c);
+ }
+}")
+ })
+ }));
+
+ // Create expected workspace
+ var expWorkspace = new AdhocWorkspace();
+ expWorkspace.AddSolution(WorkspaceHelpers.CreateSolution(
+ solutionId,
+ new[] {
+ WorkspaceHelpers.CreateCSharpProject(
+ projectId,
+ projectName,
+ new []
+ {
+ WorkspaceHelpers.CreateDocument(document1Id,document1Name,
+@"class ClassA
+{
+ public void Foo() {
+ var x = (5 + 1 + 0);
+ }
+}"),
+ WorkspaceHelpers.CreateDocument(document2Id,document2Name ,
+@"class ClassB
+{
+}")
+ })
+ }));
+
+ // Run action
+ var alteredWorkspace = WorkspaceTestUtil.RunRefactoringProvider(testWorkspace);
+
+ // Assert expected workspace equals altered workspace.
+ WorkspaceTestUtil.AssertEqual(expWorkspace, alteredWorkspace);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Tests/Common/Utils.cs b/Tests/Common/Utils.cs
index 677c353d..1adda95b 100644
--- a/Tests/Common/Utils.cs
+++ b/Tests/Common/Utils.cs
@@ -1,4 +1,5 @@
-using System;
+using Microsoft.CodeAnalysis.Text;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -28,5 +29,77 @@ internal static string HomogenizeEol(string str)
}
return sb.ToString();
}
+
+ ///
+ /// Parse the provided text for demarcated text spans.
+ /// Single char selected text is demarcated with "$" or "…".
+ /// Range selected text starts with "<-" and ends with ">-".
+ /// Marked text starts with "-[" and ends with "]-".
+ ///
+ /// The text to find text spans in.
+ /// A of selected text.
+ /// A of marked text.
+ ///
+ /// The text without any demarcation syntax.
+ ///
+ internal static string ParseText(string input, out TextSpan selectedSpan, out TextSpan markedSpan)
+ {
+ int start = -1, end = -1;
+ int start2 = -1, end2 = -1;
+ var result = new StringBuilder(input.Length);
+ int upper = input.Length - 1;
+ for (int i = 0; i < upper; i++)
+ {
+ var ch = input[i];
+ if (ch == '$' && (i + 1 >= upper || input[i + 1] != '"') || ch == '…')
+ {
+ start = end = i;
+ continue;
+ }
+ if (ch == '<' && input[i + 1] == '-')
+ {
+ start = i;
+ i++;
+ continue;
+ }
+ if (ch == '-' && input[i + 1] == '>')
+ {
+ end = i;
+ i++;
+ continue;
+ }
+
+ if (ch == '-' && input[i + 1] == '[')
+ {
+ start2 = result.Length;
+ i++;
+ continue;
+ }
+ if (ch == ']' && input[i + 1] == '-')
+ {
+ end2 = result.Length;
+ i++;
+ continue;
+ }
+ result.Append(ch);
+ }
+
+ if (upper >= 0)
+ {
+ var lastChar = input[upper];
+ if (lastChar == '$')
+ {
+ start = end = upper;
+ }
+ else
+ {
+ result.Append(lastChar);
+ }
+ }
+
+ selectedSpan = start < 0 ? new TextSpan() : TextSpan.FromBounds(start, end);
+ markedSpan = start2 < 0 ? new TextSpan() : TextSpan.FromBounds(start2, end2);
+ return result.ToString();
+ }
}
}
diff --git a/Tests/Common/WorkspaceHelpers.cs b/Tests/Common/WorkspaceHelpers.cs
new file mode 100644
index 00000000..6e77d632
--- /dev/null
+++ b/Tests/Common/WorkspaceHelpers.cs
@@ -0,0 +1,199 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.VisualBasic;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace RefactoringEssentials.Tests.Common
+{
+ ///
+ /// Helps create workspace objects suitable for testing; solutions, projects and documents.
+ ///
+ static class WorkspaceHelpers
+ {
+ ///
+ /// Default CSharp parse options.
+ ///
+ static readonly ParseOptions DefaultCSharpParseOptions = new CSharpParseOptions(
+ Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp6,
+ DocumentationMode.Diagnose | DocumentationMode.Parse,
+ SourceCodeKind.Regular,
+ ImmutableArray.Create("DEBUG", "TEST")
+ );
+
+ ///
+ /// Default CSharp compilation options.
+ ///
+ static readonly CSharpCompilationOptions DefaultCSharpCompilationOptions = new CSharpCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary,
+ "",
+ "",
+ "Script",
+ null,
+ OptimizationLevel.Debug,
+ false,
+ true
+ );
+
+ ///
+ /// Default VB Parse Options.
+ ///
+ static readonly ParseOptions DefaultVBParseOptions = new VisualBasicParseOptions(
+ Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.VisualBasic14,
+ DocumentationMode.Diagnose | DocumentationMode.Parse,
+ SourceCodeKind.Regular
+ );
+
+ ///
+ /// Default VB compilation options.
+ ///
+ static readonly VisualBasicCompilationOptions DefaultVisualBasicCompilationOptions = new VisualBasicCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary,
+ "",
+ ""
+ );
+
+ static readonly MetadataReference mscorlib = MetadataReference.CreateFromFile(typeof(Console).Assembly.Location);
+ static readonly MetadataReference systemAssembly = MetadataReference.CreateFromFile(typeof(System.ComponentModel.BrowsableAttribute).Assembly.Location);
+ static readonly MetadataReference systemXmlLinq = MetadataReference.CreateFromFile(typeof(System.Xml.Linq.XElement).Assembly.Location);
+ static readonly MetadataReference systemCore = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
+
+ ///
+ /// Default references.
+ ///
+ static readonly MetadataReference[] DefaultMetadataReferences = {
+ mscorlib,
+ systemAssembly,
+ systemCore,
+ systemXmlLinq
+ };
+
+ ///
+ /// Create a solution.
+ ///
+ /// The ID of the solution.
+ ///
+ public static SolutionInfo CreateSolution(SolutionId solutionId)
+ {
+ return CreateSolution(solutionId, null);
+ }
+
+ ///
+ /// Create a solution with projects.
+ ///
+ /// The ID of the solution.
+ /// The solution's projects.
+ ///
+ public static SolutionInfo CreateSolution(SolutionId solutionId, IEnumerable projects)
+ {
+ return SolutionInfo.Create(
+ solutionId,
+ VersionStamp.Create(),
+ null,
+ projects);
+ }
+
+ ///
+ /// Create a suitable for CSharp code.
+ ///
+ /// The ID of the project.
+ /// The name of the project.
+ ///
+ public static ProjectInfo CreateCSharpProject(ProjectId projectId, string name)
+ {
+ return CreateCSharpProject(projectId, name, null);
+ }
+
+ ///
+ /// Create a suitable for CSharp code.
+ ///
+ /// The ID of the project.
+ /// The name of the project.
+ /// The project's documents.
+ ///
+ public static ProjectInfo CreateCSharpProject(ProjectId projectId, string name, IEnumerable documents)
+ {
+ return ProjectInfo.Create(
+ projectId,
+ VersionStamp.Create(),
+ name,
+ name,
+ LanguageNames.CSharp,
+ null,
+ null,
+ new CSharpCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary,
+ "",
+ "",
+ "Script",
+ null,
+ OptimizationLevel.Debug,
+ false,
+ true
+ ),
+ DefaultCSharpParseOptions,
+ documents,
+ null,
+ DiagnosticTestBase.DefaultMetadataReferences
+ ).WithMetadataReferences(DefaultMetadataReferences);
+ }
+
+ ///
+ /// Create a suitable for Visual Basic code.
+ ///
+ /// The ID of the project.
+ /// The name of the project.
+ ///
+ public static ProjectInfo CreateVisualBasicProject(ProjectId projectId, string name)
+ {
+ return CreateVisualBasicProject(projectId, name, null);
+ }
+
+ ///
+ /// Create a suitable for Visual Basic code.
+ ///
+ /// The ID of the project.
+ /// The name of the project.
+ /// The project's documents.
+ ///
+ public static ProjectInfo CreateVisualBasicProject(ProjectId projectId, string name, IEnumerable documents)
+ {
+ return ProjectInfo.Create(
+ projectId,
+ VersionStamp.Create(),
+ name,
+ name,
+ LanguageNames.VisualBasic,
+ null,
+ null,
+ DefaultVisualBasicCompilationOptions,
+ DefaultVBParseOptions,
+ documents,
+ null,
+ DiagnosticTestBase.DefaultMetadataReferences
+ ).WithMetadataReferences(DefaultMetadataReferences);
+ }
+
+ ///
+ /// Creates a suitable for testing.
+ ///
+ /// The ID of the document.
+ /// The filename of the document.
+ /// The test content of the file.
+ ///
+ public static DocumentInfo CreateDocument(DocumentId documentId, string name, string text)
+ {
+ return DocumentInfo.Create(
+ documentId,
+ name,
+ null,
+ SourceCodeKind.Regular,
+ TextLoader.From(TextAndVersion.Create(SourceText.From(text), VersionStamp.Create()))
+ );
+ }
+
+ }
+}
diff --git a/Tests/Common/WorkspaceTestUtil.cs b/Tests/Common/WorkspaceTestUtil.cs
new file mode 100644
index 00000000..86fb18ce
--- /dev/null
+++ b/Tests/Common/WorkspaceTestUtil.cs
@@ -0,0 +1,166 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Text;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace RefactoringEssentials.Tests.Common
+{
+ ///
+ /// Utility methods for testing actions with workspaces.
+ ///
+ static class WorkspaceTestUtil
+ {
+ ///
+ /// Refactor a workspace with a type.
+ /// The workspace will be searched for an entry point to test refactoring using well known entry point syntax.
+ ///
+ /// A type.
+ /// A workspace to refactor.
+ ///
+ /// A new workspace which is the result of the refactoring.
+ ///
+ public static Workspace RunRefactoringProvider(Workspace workspace) where T : CodeRefactoringProvider, new()
+ {
+ var sln = workspace.CurrentSolution;
+
+ Document doc = null;
+ var emptySpan = new TextSpan();
+ var selectedSpan = new TextSpan();
+ var markedSpan = new TextSpan();
+
+ // Search each project and document for selected and marked text.
+ foreach (var project in sln.Projects)
+ {
+ foreach (var document in project.Documents)
+ {
+ TextSpan newSelectedSpan;
+ TextSpan newMarkedSpan;
+ var txt = Utils.ParseText(document.GetTextAsync().Result.ToString(), out newSelectedSpan, out newMarkedSpan);
+ if (newSelectedSpan != emptySpan || markedSpan != emptySpan)
+ {
+ // Fail if multiple documents are demarcated.
+ if (doc != null)
+ Assert.Fail("Multiple text spans must not be chosen.");
+
+ sln = sln.WithDocumentText(document.Id, SourceText.From(txt));
+ workspace.TryApplyChanges(sln);
+
+ doc = workspace.CurrentSolution.GetDocument(document.Id);
+ selectedSpan = newSelectedSpan;
+ markedSpan = newMarkedSpan;
+ }
+ }
+ }
+
+ // Fail if no documents are demarcated.
+ if (doc == null)
+ Assert.Fail("Text spans must be chosen.");
+
+ // Prepare refactoring context and action.
+ var actions = new List();
+ var context = new CodeRefactoringContext(
+ doc,
+ selectedSpan,
+ actions.Add,
+ default(CancellationToken));
+
+ var action = new T();
+ action.ComputeRefactoringsAsync(context).Wait();
+
+ if (markedSpan.Start > 0)
+ {
+ foreach (var nra in actions.OfType())
+ {
+ Assert.AreEqual(markedSpan, nra.TextSpan, "Activation span does not match.");
+ }
+ }
+
+ // Apply code actions to workspace.
+ var a = actions[0];
+ foreach (var op in a.GetOperationsAsync(default(CancellationToken)).Result)
+ {
+ op.Apply(workspace, default(CancellationToken));
+ }
+
+ return workspace;
+ }
+
+ ///
+ /// Assert workspaces are equivalent.
+ ///
+ /// The expected workspace.
+ /// The actual workspace.
+ public static void AssertEqual(Workspace expected, Workspace actual)
+ {
+ var expectedSln = expected.CurrentSolution;
+ var actualSln = actual.CurrentSolution;
+
+ AssertEqual(expectedSln, actualSln);
+ }
+
+ ///
+ /// Assert solutions are equivalent.
+ ///
+ /// The expected .
+ /// The actual .
+ public static void AssertEqual(Solution expected, Solution actual)
+ {
+ var expectedSln = expected;
+ var actualSln = actual;
+
+ // Assert project counts match.
+ if (expectedSln.Projects.Count() != actualSln.Projects.Count())
+ Assert.Fail($"Project counts do not match. Expected; {expectedSln.Projects.Count()}, Actual: {actualSln.Projects.Count()}.");
+
+ // Loop through projects, ensure it exists in both solutions and test equivalence.
+ foreach (var expProject in expectedSln.Projects)
+ {
+ var actProject = actualSln.GetProject(expProject.Id);
+ if (actProject == null)
+ Assert.Fail($"Project with ID \"{ expProject.Id}\" and name\"{expProject.Name}\" does not exist.");
+
+ AssertEqual(expProject, actProject);
+ }
+ }
+
+ ///
+ /// Assert projects are equivalent.
+ ///
+ /// The expected .
+ /// The actual .
+ public static void AssertEqual(Project expected, Project actual)
+ {
+ // Assert document counts match.
+ if (expected.Documents.Count() != actual.Documents.Count())
+ Assert.Fail($"Document counts do not match for project named \"{expected.Name}\". Expected; {expected.Documents.Count()}, Actual: {actual.Documents.Count()}.");
+
+ // Loop through documents, ensure it exists in both projects and test equivalence.
+ foreach (var expDoc in expected.Documents)
+ {
+ var actDoc = actual.GetDocument(expDoc.Id);
+ if (actDoc == null)
+ Assert.Fail($"Document with ID \"{ expDoc.Id}\" and name\"{expDoc.Name}\" does not exist.");
+
+ AssertEqual(expDoc, actDoc);
+ }
+ }
+
+ ///
+ /// Assert documents are equivalent.
+ ///
+ /// The expected .
+ /// The actual .
+ public static void AssertEqual(Document expected, Document actual)
+ {
+ var expText = Utils.HomogenizeEol(expected.GetTextAsync().Result.ToString());
+ var actText = Utils.HomogenizeEol(actual.GetTextAsync().Result.ToString());
+
+ if (!string.Equals(expText, actText))
+ Assert.Fail($"Document with name\"{expected.Name}\" does not match. Expected:\n{expText}\nActual:\n{actText}\n");
+ }
+ }
+}
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index f5345c2a..20b6d3da 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -125,7 +125,9 @@
+
+
@@ -142,6 +144,7 @@
+
diff --git a/Vsix/source.extension.vsixmanifest b/Vsix/source.extension.vsixmanifest
index 9f67454e..a9c6b296 100644
--- a/Vsix/source.extension.vsixmanifest
+++ b/Vsix/source.extension.vsixmanifest
@@ -1,26 +1,26 @@
-
-
-
-
-
- Refactoring Essentials for Visual Studio
- Free refactorings for C# and Visual Basic (VB) - plus more!
- license.txt
- https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2
- https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2
- Images\refactoringessentials-logo90.png
- Images\refactoringessentials-preview.png
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Refactoring Essentials for Visual Studio
+ Free refactorings for C# and Visual Basic (VB) - plus more!
+ license.txt
+ https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2
+ https://github.com/icsharpcode/RefactoringEssentials/wiki/Release-3.2
+ Images\refactoringessentials-logo90.png
+ Images\refactoringessentials-preview.png
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file