28 changed files with 1495 additions and 116 deletions
@ -0,0 +1,156 @@
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2013 Daniel Grunwald
|
||||
//
|
||||
// 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 System.Linq; |
||||
|
||||
using ICSharpCode.NRefactory.CSharp.Resolver; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
/// <summary>
|
||||
/// 1) When a type cannot be resolved, offers to add a using declaration
|
||||
/// or to replace it with the fully qualified type name.
|
||||
/// 2) When an extension method cannot be resolved, offers to add a using declaration.
|
||||
/// 3) When the caret is on a namespace name, offers to add a using declaration
|
||||
/// and simplify the type references to use the new shorter option.
|
||||
/// </summary>
|
||||
[ContextAction ("Add using", Description = "Add missing using declaration.")] |
||||
public class AddUsingAction : ICodeActionProvider |
||||
{ |
||||
public IEnumerable<CodeAction> GetActions(RefactoringContext context) |
||||
{ |
||||
AstNode node = context.GetNode(); |
||||
if (node is Identifier) |
||||
node = node.Parent; |
||||
if (node is SimpleType || node is IdentifierExpression) { |
||||
return GetActionsForType(context, node) |
||||
.Concat(GetActionsForAddNamespaceUsing(context, node)); |
||||
} else if (node is MemberReferenceExpression && node.Parent is InvocationExpression) { |
||||
return GetActionsForExtensionMethodInvocation(context, (InvocationExpression)node.Parent); |
||||
} else if (node is MemberReferenceExpression) { |
||||
return GetActionsForAddNamespaceUsing(context, node); |
||||
} else { |
||||
return EmptyList<CodeAction>.Instance; |
||||
} |
||||
} |
||||
|
||||
IEnumerable<CodeAction> GetActionsForType(RefactoringContext context, AstNode node) |
||||
{ |
||||
var rr = context.Resolve(node) as UnknownIdentifierResolveResult; |
||||
if (rr == null) |
||||
return EmptyList<CodeAction>.Instance; |
||||
|
||||
string identifier = rr.Identifier; |
||||
int tc = rr.TypeArgumentCount; |
||||
string attributeIdentifier = null; |
||||
if (node.Parent is Attribute) |
||||
attributeIdentifier = identifier + "Attribute"; |
||||
|
||||
var lookup = new MemberLookup(null, context.Compilation.MainAssembly); |
||||
List<CodeAction> actions = new List<CodeAction>(); |
||||
foreach (var typeDefinition in context.Compilation.GetAllTypeDefinitions()) { |
||||
if ((typeDefinition.Name == identifier || typeDefinition.Name == attributeIdentifier) |
||||
&& typeDefinition.TypeParameterCount == tc |
||||
&& lookup.IsAccessible(typeDefinition, false)) |
||||
{ |
||||
if (typeDefinition.DeclaringTypeDefinition == null) { |
||||
actions.Add(NewUsingAction(context, node, typeDefinition.Namespace)); |
||||
} |
||||
actions.Add(ReplaceWithFullTypeNameAction(context, node, typeDefinition)); |
||||
} |
||||
} |
||||
return actions; |
||||
} |
||||
|
||||
CodeAction NewUsingAction(RefactoringContext context, AstNode node, string ns) |
||||
{ |
||||
return new CodeAction("using " + ns + ";", s => UsingHelper.InsertUsingAndRemoveRedundantNamespaceUsage(context, s, ns)); |
||||
} |
||||
|
||||
CodeAction ReplaceWithFullTypeNameAction(RefactoringContext context, AstNode node, ITypeDefinition typeDefinition) |
||||
{ |
||||
AstType astType = context.CreateShortType(typeDefinition); |
||||
string textWithoutGenerics = astType.GetText(); |
||||
foreach (var typeArg in node.GetChildrenByRole(Roles.TypeArgument)) { |
||||
astType.AddChild(typeArg.Clone(), Roles.TypeArgument); |
||||
} |
||||
return new CodeAction(textWithoutGenerics, s => s.Replace(node, astType)); |
||||
} |
||||
|
||||
IEnumerable<CodeAction> GetActionsForExtensionMethodInvocation(RefactoringContext context, InvocationExpression invocation) |
||||
{ |
||||
var rr = context.Resolve(invocation) as UnknownMethodResolveResult; |
||||
if (rr == null) |
||||
return EmptyList<CodeAction>.Instance; |
||||
|
||||
var lookup = new MemberLookup(null, context.Compilation.MainAssembly); |
||||
HashSet<string> namespaces = new HashSet<string>(); |
||||
List<CodeAction> result = new List<CodeAction>(); |
||||
foreach (var typeDefinition in context.Compilation.GetAllTypeDefinitions()) { |
||||
if (!(typeDefinition.HasExtensionMethods && lookup.IsAccessible(typeDefinition, false))) { |
||||
continue; |
||||
} |
||||
foreach (var method in typeDefinition.Methods.Where(m => m.IsExtensionMethod && m.Name == rr.MemberName)) { |
||||
IType[] inferredTypes; |
||||
if (CSharpResolver.IsEligibleExtensionMethod(rr.TargetType, method, true, out inferredTypes)) { |
||||
// avoid offering the same namespace twice
|
||||
if (namespaces.Add(typeDefinition.Namespace)) { |
||||
result.Add(NewUsingAction(context, invocation, typeDefinition.Namespace)); |
||||
} |
||||
break; // continue with the next type
|
||||
} |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
IEnumerable<CodeAction> GetActionsForAddNamespaceUsing(RefactoringContext context, AstNode node) |
||||
{ |
||||
var nrr = context.Resolve(node) as NamespaceResolveResult; |
||||
if (nrr == null) |
||||
return EmptyList<CodeAction>.Instance; |
||||
|
||||
var trr = context.Resolve(node.Parent) as TypeResolveResult; |
||||
if (trr == null) |
||||
return EmptyList<CodeAction>.Instance; |
||||
ITypeDefinition typeDef = trr.Type.GetDefinition(); |
||||
if (typeDef == null) |
||||
return EmptyList<CodeAction>.Instance; |
||||
|
||||
IList<IType> typeArguments; |
||||
ParameterizedType parameterizedType = trr.Type as ParameterizedType; |
||||
if (parameterizedType != null) |
||||
typeArguments = parameterizedType.TypeArguments; |
||||
else |
||||
typeArguments = EmptyList<IType>.Instance; |
||||
|
||||
var resolver = context.GetResolverStateBefore(node.Parent); |
||||
if (resolver.ResolveSimpleName(typeDef.Name, typeArguments) is UnknownIdentifierResolveResult) { |
||||
// It's possible to remove the explicit namespace usage and introduce a using instead
|
||||
return new[] { NewUsingAction(context, node, typeDef.Namespace) }; |
||||
} |
||||
return EmptyList<CodeAction>.Instance; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// 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 System.Diagnostics; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp.Resolver; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
/// <summary>
|
||||
/// Helper methods for managing using declarations.
|
||||
/// </summary>
|
||||
public class UsingHelper |
||||
{ |
||||
/// <summary>
|
||||
/// Inserts 'using ns;' in the current scope, and then removes all explicit
|
||||
/// usages of ns that were made redundant by the new using.
|
||||
/// </summary>
|
||||
public static void InsertUsingAndRemoveRedundantNamespaceUsage(RefactoringContext context, Script script, string ns) |
||||
{ |
||||
InsertUsing(context, script, new UsingDeclaration(ns)); |
||||
// TODO: remove the usages that were made redundant
|
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Inserts 'newUsing' in the current scope.
|
||||
/// This method will try to insert new usings in the correct position (depending on
|
||||
/// where the existing usings are; and maintaining the sort order).
|
||||
/// </summary>
|
||||
public static void InsertUsing(RefactoringContext context, Script script, AstNode newUsing) |
||||
{ |
||||
UsingInfo newUsingInfo = new UsingInfo(newUsing, context); |
||||
AstNode enclosingNamespace = context.GetNode<NamespaceDeclaration>() ?? context.RootNode; |
||||
// Find nearest enclosing parent that has usings:
|
||||
AstNode usingParent = enclosingNamespace; |
||||
while (usingParent != null && !usingParent.Children.OfType<UsingDeclaration>().Any()) |
||||
usingParent = usingParent.Parent; |
||||
if (usingParent == null) { |
||||
// No existing usings at all -> use the default location
|
||||
if (script.FormattingOptions.UsingPlacement == UsingPlacement.TopOfFile) { |
||||
usingParent = context.RootNode; |
||||
} else { |
||||
usingParent = enclosingNamespace; |
||||
} |
||||
} |
||||
// Find the main block of using declarations in the chosen scope:
|
||||
AstNode blockStart = usingParent.Children.FirstOrDefault(IsUsingDeclaration); |
||||
AstNode insertionPoint; |
||||
if (blockStart == null) { |
||||
// no using declarations in the file
|
||||
Debug.Assert(SyntaxTree.MemberRole == NamespaceDeclaration.MemberRole); |
||||
insertionPoint = usingParent.GetChildrenByRole(SyntaxTree.MemberRole).SkipWhile(CanAppearBeforeUsings).FirstOrDefault(); |
||||
} else { |
||||
insertionPoint = blockStart; |
||||
while (IsUsingDeclaration(insertionPoint) && newUsingInfo.CompareTo(new UsingInfo(insertionPoint, context)) > 0) |
||||
insertionPoint = insertionPoint.NextSibling; |
||||
} |
||||
if (insertionPoint != null) { |
||||
script.InsertBefore(insertionPoint, newUsing); |
||||
} |
||||
} |
||||
|
||||
static bool IsUsingDeclaration(AstNode node) |
||||
{ |
||||
return node is UsingDeclaration || node is UsingAliasDeclaration; |
||||
} |
||||
|
||||
static bool CanAppearBeforeUsings(AstNode node) |
||||
{ |
||||
if (node is ExternAliasDeclaration) |
||||
return true; |
||||
if (node is PreProcessorDirective) |
||||
return true; |
||||
Comment c = node as Comment; |
||||
if (c != null) |
||||
return !c.IsDocumentation; |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Sorts the specified usings.
|
||||
/// </summary>
|
||||
public static IEnumerable<AstNode> SortUsingBlock(IEnumerable<AstNode> nodes, BaseRefactoringContext context) |
||||
{ |
||||
var infos = nodes.Select(_ => new UsingInfo(_, context)); |
||||
var orderedInfos = infos.OrderBy(_ => _); |
||||
var orderedNodes = orderedInfos.Select(_ => _.Node); |
||||
|
||||
return orderedNodes; |
||||
} |
||||
|
||||
|
||||
private sealed class UsingInfo : IComparable<UsingInfo> |
||||
{ |
||||
public AstNode Node; |
||||
|
||||
public string Alias; |
||||
public string Name; |
||||
|
||||
public bool IsAlias; |
||||
public bool HasTypesFromOtherAssemblies; |
||||
public bool IsSystem; |
||||
|
||||
public UsingInfo(AstNode node, BaseRefactoringContext context) |
||||
{ |
||||
var importAndAlias = GetImportAndAlias(node); |
||||
|
||||
Node = node; |
||||
|
||||
Alias = importAndAlias.Item2; |
||||
Name = importAndAlias.Item1.ToString(); |
||||
|
||||
IsAlias = Alias != null; |
||||
|
||||
ResolveResult rr; |
||||
if (node.Ancestors.Contains(context.RootNode)) { |
||||
rr = context.Resolve(importAndAlias.Item1); |
||||
} else { |
||||
// It's possible that we're looking at a new using that
|
||||
// isn't part of the AST.
|
||||
var resolver = new CSharpAstResolver(new CSharpResolver(context.Compilation), node); |
||||
rr = resolver.Resolve(importAndAlias.Item1); |
||||
} |
||||
|
||||
var nrr = rr as NamespaceResolveResult; |
||||
HasTypesFromOtherAssemblies = nrr != null && nrr.Namespace.ContributingAssemblies.Any(a => !a.IsMainAssembly); |
||||
|
||||
IsSystem = HasTypesFromOtherAssemblies && (Name == "System" || Name.StartsWith("System.", StringComparison.Ordinal)); |
||||
} |
||||
|
||||
private static Tuple<AstType, string> GetImportAndAlias(AstNode node) |
||||
{ |
||||
var plainUsing = node as UsingDeclaration; |
||||
if (plainUsing != null) |
||||
return Tuple.Create(plainUsing.Import, (string)null); |
||||
|
||||
var aliasUsing = node as UsingAliasDeclaration; |
||||
if (aliasUsing != null) |
||||
return Tuple.Create(aliasUsing.Import, aliasUsing.Alias); |
||||
|
||||
throw new InvalidOperationException(string.Format("Invalid using node: {0}", node)); |
||||
} |
||||
|
||||
public int CompareTo(UsingInfo y) |
||||
{ |
||||
UsingInfo x = this; |
||||
if (x.IsAlias != y.IsAlias) |
||||
return x.IsAlias ? 1 : -1; |
||||
else if (x.HasTypesFromOtherAssemblies != y.HasTypesFromOtherAssemblies) |
||||
return x.HasTypesFromOtherAssemblies ? -1 : 1; |
||||
else if (x.IsSystem != y.IsSystem) |
||||
return x.IsSystem ? -1 : 1; |
||||
else if (x.Alias != y.Alias) |
||||
return StringComparer.Ordinal.Compare(x.Alias, y.Alias); |
||||
else if (x.Name != y.Name) |
||||
return StringComparer.Ordinal.Compare(x.Name, y.Name); |
||||
else |
||||
return 0; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,229 @@
@@ -0,0 +1,229 @@
|
||||
using NUnit.Framework; |
||||
using ICSharpCode.NRefactory.CSharp.CodeIssues; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using ICSharpCode.NRefactory.CSharp.CodeActions; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeIssues.UnresolvedType |
||||
{ |
||||
[TestFixture] |
||||
public class UnresolvedTypeActionAlphabeticalTests : ContextActionTestBase |
||||
{ |
||||
[Test] |
||||
public void ShouldAddUsingAtStartIfItIsTheFirstAlphabetically() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System.IO; |
||||
|
||||
class TestClass |
||||
{ |
||||
private $List<TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldInsertUsingBetweenExistingUsings() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.IO; |
||||
|
||||
class TestClass |
||||
{ |
||||
private $List<TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldInsertUsingAfterExistingUsings() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<$TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddBlankLinesAfterUsingsWhenAddingAtEnd() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
class TestClass |
||||
{ |
||||
private List<$TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<TextWriter> writerList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
#region System Namespaces
|
||||
|
||||
[Test] |
||||
public void ShouldBeAbleToPlaceSystemNamespacesFirst() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using ANamespace; |
||||
|
||||
class TestClass |
||||
{ |
||||
private $TextWriter writer; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System.IO; |
||||
using ANamespace; |
||||
|
||||
class TestClass |
||||
{ |
||||
private TextWriter writer; |
||||
} |
||||
}";
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotPlaceNonSystemNamespacesStartingWithSystemFirst() |
||||
{ |
||||
string testCode = |
||||
@"namespace A { class B { } }
|
||||
namespace OuterNamespace |
||||
{ |
||||
using SystemA; |
||||
|
||||
class TestClass |
||||
{ |
||||
private $B b; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace A { class B { } }
|
||||
namespace OuterNamespace |
||||
{ |
||||
using A; |
||||
using SystemA; |
||||
|
||||
class TestClass |
||||
{ |
||||
private B b; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldPlaceSystemBeforeOtherNamespaces() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System.Collections.Generic; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<$DateTime> dates; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<DateTime> dates; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
#endregion
|
||||
} |
||||
} |
||||
|
@ -0,0 +1,203 @@
@@ -0,0 +1,203 @@
|
||||
using NUnit.Framework; |
||||
using ICSharpCode.NRefactory.CSharp.CodeIssues; |
||||
using ICSharpCode.NRefactory.CSharp.CodeActions; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeIssues.UnresolvedType |
||||
{ |
||||
[TestFixture] |
||||
public class UnresolvedTypeActionInsideNamespaceTests : ContextActionTestBase |
||||
{ |
||||
[Test] |
||||
public void ShouldInsertUsingStatement() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
using System.Collections.Generic; |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.UsingPlacement = UsingPlacement.InsideNamespace; |
||||
formattingOptions.BlankLinesAfterUsings = 0; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddBlankLinesBeforeUsingStatement() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
|
||||
|
||||
using System.Collections.Generic; |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.UsingPlacement = UsingPlacement.InsideNamespace; |
||||
formattingOptions.BlankLinesBeforeUsings = 2; |
||||
formattingOptions.BlankLinesAfterUsings = 0; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddBlankLinesAfterUsingStatements() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
using System.Collections.Generic; |
||||
|
||||
|
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.UsingPlacement = UsingPlacement.InsideNamespace; |
||||
formattingOptions.BlankLinesAfterUsings = 2; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Something is wrong with the blank lines")] |
||||
public void ShouldAddUsingAfterExistingUsings() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
using System; |
||||
|
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.UsingPlacement = UsingPlacement.InsideNamespace; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddUsingInMostNestedNamespace() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
namespace InnerNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
namespace InnerNamespace |
||||
{ |
||||
using System.Collections.Generic; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.UsingPlacement = UsingPlacement.InsideNamespace; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Something is wrong with the blank lines")] |
||||
public void ShouldAddUsingAfterExistingUsingsInMostNestedNamespace() |
||||
{ |
||||
string testCode = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
namespace InnerNamespace |
||||
{ |
||||
using System; |
||||
|
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"namespace OuterNamespace
|
||||
{ |
||||
namespace InnerNamespace |
||||
{ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.UsingPlacement = UsingPlacement.InsideNamespace; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,320 @@
@@ -0,0 +1,320 @@
|
||||
using ICSharpCode.NRefactory.CSharp.CodeIssues; |
||||
using NUnit.Framework; |
||||
using ICSharpCode.NRefactory.CSharp.CodeActions; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeIssues.UnresolvedType |
||||
{ |
||||
[TestFixture] |
||||
public class UnresolvedTypeIssueTests : ContextActionTestBase |
||||
{ |
||||
void UnresolvedTypeName(string code, string typeName, params string[] namespaces) |
||||
{ |
||||
TestActionDescriptions( |
||||
new AddUsingAction(), code, |
||||
namespaces.SelectMany(ns => new[] { |
||||
"using " + ns + ";", |
||||
ns + "." + typeName |
||||
}).ToArray()); |
||||
} |
||||
|
||||
#region Field Declarations
|
||||
[Test] |
||||
public void ShouldReturnAnIssueForUnresolvedFieldDeclarations() |
||||
{ |
||||
UnresolvedTypeName(@"class Foo {
|
||||
private $TextWriter textWriter; |
||||
}", "TextWriter", "System.IO");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnAnyIssuesIfFieldTypeIsResolved() |
||||
{ |
||||
TestWrongContext<AddUsingAction>(@"using System.IO;
|
||||
class Foo { |
||||
private $TextWriter textWriter; |
||||
}");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldReturnAnIssueIfFieldTypeArgumentIsNotResolvable() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"using System.Collections.Generic;
|
||||
|
||||
class Foo |
||||
{ |
||||
private List<$AttributeTargets> targets; |
||||
}", "AttributeTargets", "System");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnAnIssueIfFieldTypeArgumentIsResolvable() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System;
|
||||
using System.Collections.Generic; |
||||
|
||||
class Foo |
||||
{ |
||||
private List<$AttributeTargets> notifiers; |
||||
}");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnAnIssueIfFieldTypeArgumentIsPrimitiveType() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System.Collections.Generic;
|
||||
|
||||
class Foo |
||||
{ |
||||
private List<$string> notifiers; |
||||
}");
|
||||
} |
||||
#endregion
|
||||
|
||||
#region Method Return Types
|
||||
[Test] |
||||
public void ShouldReturnIssueForUnresolvedReturnType() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"class Foo
|
||||
{ |
||||
$TextWriter Bar () |
||||
{ |
||||
return null; |
||||
} |
||||
}", "TextWriter", "System.IO");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnIssueForResolvedReturnType() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System.IO;
|
||||
|
||||
class Foo |
||||
{ |
||||
$TextWriter Bar () |
||||
{ |
||||
return null; |
||||
} |
||||
}");
|
||||
} |
||||
#endregion
|
||||
|
||||
#region Local Variables
|
||||
[Test] |
||||
public void ShouldReturnIssueForUnresolvedLocalVariableDeclaration() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"class Foo
|
||||
{ |
||||
void Bar () |
||||
{ |
||||
$TextWriter writer; |
||||
} |
||||
}", "TextWriter", "System.IO");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnIssueForResolvedLocalVariableDeclaration() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System.IO;
|
||||
|
||||
class Foo |
||||
{ |
||||
void Bar () |
||||
{ |
||||
$TextWriter writer; |
||||
} |
||||
}");
|
||||
} |
||||
#endregion
|
||||
|
||||
#region Method Parameters
|
||||
[Test] |
||||
public void ShouldReturnIssueIfMethodParameterIsNotResolvable() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"class Foo
|
||||
{ |
||||
void Bar ($TextWriter writer) |
||||
{ |
||||
} |
||||
}", "TextWriter", "System.IO");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnAnIssueIfMethodParameterIsResolvable() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System.IO;
|
||||
|
||||
class Foo |
||||
{ |
||||
void Bar ($TextWriter writer) |
||||
{ |
||||
} |
||||
}");
|
||||
} |
||||
#endregion
|
||||
|
||||
#region Base Types
|
||||
[Test] |
||||
public void ShouldReturnIssueIfBaseClassIsNotResolvable() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"class Foo : $List<string>
|
||||
{ |
||||
}", "List<>", "System.Collections.Generic");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnIssueIfBaseClassIsResolvable() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System.Collections.Generic;
|
||||
|
||||
class Foo : $List<string> |
||||
{ |
||||
}");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldReturnIssueIfGenericInterfaceIsMissingButNonGenericIsPresent() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"using System.Collections;
|
||||
class Foo : $IEnumerable<string> |
||||
{ |
||||
}", "IEnumerable<>", "System.Collections.Generic");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldReturnIssueIfNonGenericInterfaceIsMissingButGenericIsPresent() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"using System.Collections.Generic;
|
||||
class Foo : $IEnumerable |
||||
{ |
||||
}", "IEnumerable", "System.Collections");
|
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region Member Access
|
||||
[Test] |
||||
public void ShouldReturnIssueIfEnumValueIsNotResolvable() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"class Foo
|
||||
{ |
||||
void Bar () |
||||
{ |
||||
var support = $AttributeTargets.Assembly; |
||||
} |
||||
}", "AttributeTargets", "System");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnIssueIfEnumValueIsResolvable() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System;
|
||||
class Foo |
||||
{ |
||||
void Bar () |
||||
{ |
||||
var support = $AttributeTargets.Assembly; |
||||
} |
||||
}");
|
||||
} |
||||
#endregion
|
||||
|
||||
[Test] |
||||
public void ShouldReturnIssueIfAttributeIsNotResolvable() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"[$Serializable]
|
||||
class Foo |
||||
{ |
||||
}", "SerializableAttribute", "System");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldNotReturnIssueIfAttributeIsResolvable() |
||||
{ |
||||
TestWrongContext<AddUsingAction>( |
||||
@"using System;
|
||||
|
||||
[$Serializable] |
||||
class Foo |
||||
{ |
||||
}");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldReturnIssueIfTypeArgumentIsNotResolvable() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"using System.Collections.Generic;
|
||||
|
||||
class Test |
||||
{ |
||||
void TestMethod() |
||||
{ |
||||
var list = new List<$Stream>(); |
||||
} |
||||
}", "Stream", "System.IO");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldReturnIssueForUnresolvedExtensionMethod() |
||||
{ |
||||
TestActionDescriptions( |
||||
new AddUsingAction(), |
||||
@"using System.Collections.Generic;
|
||||
|
||||
class Test |
||||
{ |
||||
void TestMethod() |
||||
{ |
||||
var list = new List<string>(); |
||||
var first = list.$First(); |
||||
} |
||||
}", "using System.Linq;");
|
||||
} |
||||
|
||||
[Test] |
||||
public void ShouldReturnMultipleNamespaceSuggestions() |
||||
{ |
||||
UnresolvedTypeName( |
||||
@"namespace A { public class TestClass { } }
|
||||
namespace B { public class TestClass { } } |
||||
namespace C |
||||
{ |
||||
public class Test |
||||
{ |
||||
private $TestClass testClass; |
||||
} |
||||
}", "TestClass", "A", "B");
|
||||
} |
||||
|
||||
[Test] |
||||
public void InnerTypeCanOnlyBeReferredToByFullName() |
||||
{ |
||||
TestActionDescriptions( |
||||
new AddUsingAction(), |
||||
@"class Outer { public class Inner {} }
|
||||
public class Test |
||||
{ |
||||
private $Inner t; |
||||
} |
||||
", "Outer.Inner");
|
||||
} |
||||
} |
||||
} |
@ -0,0 +1,248 @@
@@ -0,0 +1,248 @@
|
||||
using NUnit.Framework; |
||||
using ICSharpCode.NRefactory.CSharp.CodeIssues; |
||||
using ICSharpCode.NRefactory.CSharp.CodeActions; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeIssues.UnresolvedType |
||||
{ |
||||
[TestFixture] |
||||
public class UnresolvedTypeActionTests : ContextActionTestBase |
||||
{ |
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldInsertUsingStatement() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"using System.Collections.Generic;
|
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddBlankLinesAfterUsings() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.BlankLinesAfterUsings = 2; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddBlankLinesBeforeUsing() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"
|
||||
|
||||
using System.Collections.Generic; |
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
formattingOptions.BlankLinesBeforeUsings = 2; |
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddAfterExistingUsingStatements() |
||||
{ |
||||
string testCode = |
||||
@"using System;
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"using System;
|
||||
using System.Collections.Generic; |
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Something else is broken regarding blank lines as well")] |
||||
public void ShouldNotAddBlankLinesAfterIfTheyAreAlreadyThere() |
||||
{ |
||||
string testCode = |
||||
@"using System;
|
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"using System;
|
||||
using System.Collections.Generic; |
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Something else is broken regarding blank lines as well")] |
||||
public void ShouldLeaveAdditionalBlankLinesThatAlreadyExist() |
||||
{ |
||||
string testCode = |
||||
@"using System;
|
||||
|
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"using System;
|
||||
using System.Collections.Generic; |
||||
|
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldAddFirstUsingAfterComments() |
||||
{ |
||||
string testCode = |
||||
@"// This is the file header.
|
||||
// It contains any copyright information.
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private $List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"// This is the file header.
|
||||
// It contains any copyright information.
|
||||
using System.Collections.Generic; |
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
class TestClass |
||||
{ |
||||
private List<string> stringList; |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
|
||||
[Test] |
||||
[Ignore("Add using does not honor the blank line setting yet")] |
||||
public void ShouldBeAbleToFixAttributeWithShortName() |
||||
{ |
||||
string testCode = |
||||
@"namespace TestNamespace
|
||||
{ |
||||
[$Serializable] |
||||
class TestClass |
||||
{ |
||||
} |
||||
}";
|
||||
|
||||
string expectedOutput = |
||||
@"using System;
|
||||
|
||||
namespace TestNamespace |
||||
{ |
||||
[Serializable] |
||||
class TestClass |
||||
{ |
||||
} |
||||
}";
|
||||
|
||||
Test(new AddUsingAction(), testCode, expectedOutput); |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue