You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
11 KiB
273 lines
11 KiB
// 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.IO; |
|
using System.Linq; |
|
using System.Threading; |
|
using ICSharpCode.NRefactory.CSharp.TypeSystem; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using NUnit.Framework; |
|
|
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
|
{ |
|
[TestFixture] |
|
public class FindReferencesTest |
|
{ |
|
SyntaxTree syntaxTree; |
|
CSharpUnresolvedFile unresolvedFile; |
|
ICompilation compilation; |
|
FindReferences findReferences; |
|
|
|
void Init(string code) |
|
{ |
|
syntaxTree = SyntaxTree.Parse(code, "test.cs"); |
|
unresolvedFile = syntaxTree.ToTypeSystem(); |
|
compilation = TypeSystemHelper.CreateCompilation(unresolvedFile); |
|
findReferences = new FindReferences(); |
|
} |
|
|
|
AstNode[] FindReferences(IEntity entity) |
|
{ |
|
var result = new List<AstNode>(); |
|
var searchScopes = findReferences.GetSearchScopes(entity); |
|
findReferences.FindReferencesInFile(searchScopes, unresolvedFile, syntaxTree, compilation, |
|
(node, rr) => result.Add(node), CancellationToken.None); |
|
return result.OrderBy(n => n.StartLocation).ToArray(); |
|
} |
|
|
|
#region Method Group |
|
[Test] |
|
public void FindMethodGroupReference() |
|
{ |
|
Init(@"using System; |
|
class Test { |
|
Action<int> x = M; |
|
Action<string> y = M; |
|
static void M(int a) {} |
|
static void M(string a) {} |
|
}"); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(); |
|
var m_int = test.Methods.Single(m => m.Name == "M" && m.Parameters.Single().Type.Name == "Int32"); |
|
var m_string = test.Methods.Single(m => m.Name == "M" && m.Parameters.Single().Type.Name == "String"); |
|
Assert.AreEqual(new int[] { 3, 5 }, FindReferences(m_int).Select(n => n.StartLocation.Line).ToArray()); |
|
Assert.AreEqual(new int[] { 4, 6 }, FindReferences(m_string).Select(n => n.StartLocation.Line).ToArray()); |
|
} |
|
|
|
[Test] |
|
public void FindMethodGroupReferenceInOtherMethodCall() |
|
{ |
|
Init(@"using System; |
|
class Test { |
|
static void T(Action<int> a, Action<string> b) { |
|
this.T(M, M); |
|
} |
|
static void M(int a) {} |
|
static void M(string a) {} |
|
}"); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(); |
|
var m_int = test.Methods.Single(m => m.Name == "M" && m.Parameters.Single().Type.Name == "Int32"); |
|
var m_string = test.Methods.Single(m => m.Name == "M" && m.Parameters.Single().Type.Name == "String"); |
|
Assert.AreEqual(new [] { new TextLocation(4, 10), new TextLocation(6, 2) }, |
|
FindReferences(m_int).Select(n => n.StartLocation).ToArray()); |
|
Assert.AreEqual(new [] { new TextLocation(4, 13), new TextLocation(7, 2) }, |
|
FindReferences(m_string).Select(n => n.StartLocation).ToArray()); |
|
} |
|
|
|
[Test] |
|
public void FindMethodGroupReferenceInExplicitDelegateCreation() |
|
{ |
|
Init(@"using System; |
|
class Test { |
|
static void T(Action<int> a, Action<string> b) { |
|
this.T(new Action<int>(M), new Action<string>(M)); |
|
} |
|
static void M(int a) {} |
|
static void M(string a) {} |
|
}"); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(); |
|
var m_int = test.Methods.Single(m => m.Name == "M" && m.Parameters.Single().Type.Name == "Int32"); |
|
var m_string = test.Methods.Single(m => m.Name == "M" && m.Parameters.Single().Type.Name == "String"); |
|
Assert.AreEqual(new [] { new TextLocation(4, 26), new TextLocation(6, 2) }, |
|
FindReferences(m_int).Select(n => n.StartLocation).ToArray()); |
|
Assert.AreEqual(new [] { new TextLocation(4, 49), new TextLocation(7, 2) }, |
|
FindReferences(m_string).Select(n => n.StartLocation).ToArray()); |
|
} |
|
#endregion |
|
|
|
#region GetEnumerator |
|
[Test] |
|
public void FindReferenceToGetEnumeratorUsedImplicitlyInForeach() |
|
{ |
|
Init(@"using System; |
|
class MyEnumerable { |
|
public System.Collections.IEnumerator GetEnumerator(); |
|
} |
|
class Test { |
|
static void T() { |
|
var x = new MyEnumerable(); |
|
foreach (var y in x) { |
|
} |
|
} |
|
}"); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "MyEnumerable"); |
|
var getEnumerator = test.Methods.Single(m => m.Name == "GetEnumerator"); |
|
var actual = FindReferences(getEnumerator).ToList(); |
|
Assert.AreEqual(2, actual.Count); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is ForeachStatement)); |
|
} |
|
#endregion |
|
|
|
#region Op_Implicit |
|
[Test] |
|
public void FindReferencesForOpImplicitInLocalVariableInitialization() |
|
{ |
|
Init(@"using System; |
|
class Test { |
|
static void T() { |
|
int x = new Test(); |
|
} |
|
public static implicit operator int(Test x) { return 0; } |
|
}"); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "Test"); |
|
var opImplicit = test.Methods.Single(m => m.Name == "op_Implicit"); |
|
var actual = FindReferences(opImplicit).ToList(); |
|
Assert.AreEqual(2, actual.Count); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is ObjectCreateExpression)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 6 && r is OperatorDeclaration)); |
|
} |
|
#endregion |
|
|
|
#region Inheritance |
|
const string inheritanceTest = @"using System; |
|
class A { public virtual void M() {} } |
|
class B : A { public override void M() {} } |
|
class C : A { public override void M() {} } |
|
class Calls { |
|
void Test(A a, B b, C c) { |
|
a.M(); |
|
b.M(); |
|
c.M(); |
|
} |
|
}"; |
|
|
|
[Test] |
|
public void InheritanceTest1() |
|
{ |
|
Init(inheritanceTest); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B"); |
|
var BM = test.Methods.Single(m => m.Name == "M"); |
|
var actual = FindReferences(BM).ToList(); |
|
Assert.AreEqual(2, actual.Count); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression)); |
|
} |
|
|
|
[Test] |
|
public void InheritanceTest2() |
|
{ |
|
Init(inheritanceTest); |
|
findReferences.FindCallsThroughVirtualBaseMethod = true; |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B"); |
|
var BM = test.Methods.Single(m => m.Name == "M"); |
|
var actual = FindReferences(BM).ToList(); |
|
Assert.AreEqual(3, actual.Count); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 7 && r is InvocationExpression)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression)); |
|
} |
|
|
|
[Test] |
|
public void InheritanceTest3() |
|
{ |
|
Init(inheritanceTest); |
|
findReferences.WholeVirtualSlot = true; |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B"); |
|
var BM = test.Methods.Single(m => m.Name == "M"); |
|
var actual = FindReferences(BM).ToList(); |
|
Assert.AreEqual(6, actual.Count); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 2 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 7 && r is InvocationExpression)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 9 && r is InvocationExpression)); |
|
} |
|
#endregion |
|
|
|
#region Await |
|
const string awaitTest = @"using System; |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
public MyAwaiter GetAwaiter() { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = await x; |
|
} |
|
}"; |
|
|
|
[Test] |
|
public void GetAwaiterReferenceInAwaitExpressionIsFound() { |
|
Init(awaitTest); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "MyAwaitable"); |
|
var method = test.Methods.Single(m => m.Name == "GetAwaiter"); |
|
var actual = FindReferences(method).ToList(); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 13 && r is UnaryOperatorExpression)); |
|
} |
|
|
|
[Test] |
|
public void GetResultReferenceInAwaitExpressionIsFound() { |
|
Init(awaitTest); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "MyAwaiter"); |
|
var method = test.Methods.Single(m => m.Name == "GetResult"); |
|
var actual = FindReferences(method).ToList(); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 5 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 13 && r is UnaryOperatorExpression)); |
|
} |
|
|
|
[Test] |
|
public void OnCompletedReferenceInAwaitExpressionIsFound() { |
|
Init(awaitTest); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "MyAwaiter"); |
|
var method = test.Methods.Single(m => m.Name == "OnCompleted"); |
|
var actual = FindReferences(method).ToList(); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is MethodDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 13 && r is UnaryOperatorExpression)); |
|
} |
|
|
|
[Test] |
|
public void IsCompletedReferenceInAwaitExpressionIsFound() { |
|
Init(awaitTest); |
|
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "MyAwaiter"); |
|
var property = test.Properties.Single(m => m.Name == "IsCompleted"); |
|
var actual = FindReferences(property).ToList(); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is PropertyDeclaration)); |
|
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 13 && r is UnaryOperatorExpression)); |
|
} |
|
#endregion |
|
} |
|
}
|
|
|