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.
272 lines
10 KiB
272 lines
10 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.Diagnostics; |
|
using System.IO; |
|
using System.Linq; |
|
using ICSharpCode.NRefactory.CSharp.Parser; |
|
using ICSharpCode.NRefactory.CSharp.TypeSystem; |
|
using ICSharpCode.NRefactory.Semantics; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
|
using NUnit.Framework; |
|
|
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
|
{ |
|
/// <summary> |
|
/// Base class with helper functions for resolver unit tests. |
|
/// </summary> |
|
public abstract class ResolverTestBase |
|
{ |
|
protected readonly IUnresolvedAssembly mscorlib = CecilLoaderTests.Mscorlib; |
|
protected IProjectContent project; |
|
protected ICompilation compilation; |
|
|
|
[SetUp] |
|
public virtual void SetUp() |
|
{ |
|
project = new CSharpProjectContent().AddAssemblyReferences(new [] { mscorlib, CecilLoaderTests.SystemCore }); |
|
compilation = project.CreateCompilation(); |
|
} |
|
|
|
protected IType ResolveType(Type type) |
|
{ |
|
IType t = compilation.FindType(type); |
|
if (t.Kind == TypeKind.Unknown) |
|
throw new InvalidOperationException("Could not resolve type"); |
|
return t; |
|
} |
|
|
|
protected ConstantResolveResult MakeConstant(object value) |
|
{ |
|
if (value == null) |
|
return new ConstantResolveResult(SpecialType.NullType, null); |
|
IType type = ResolveType(value.GetType()); |
|
if (type.Kind == TypeKind.Enum) |
|
value = Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType())); |
|
return new ConstantResolveResult(type, value); |
|
} |
|
|
|
protected ResolveResult MakeResult(Type type) |
|
{ |
|
return new ResolveResult(ResolveType(type)); |
|
} |
|
|
|
protected static TypeOrNamespaceReference MakeReference(string namespaceName) |
|
{ |
|
string[] nameParts = namespaceName.Split('.'); |
|
TypeOrNamespaceReference r = new SimpleTypeOrNamespaceReference(nameParts[0], new ITypeReference[0], NameLookupMode.TypeInUsingDeclaration); |
|
for (int i = 1; i < nameParts.Length; i++) { |
|
r = new MemberTypeOrNamespaceReference(r, nameParts[i], new ITypeReference[0]); |
|
} |
|
return r; |
|
} |
|
|
|
protected void AssertConstant(object expectedValue, ResolveResult rr) |
|
{ |
|
Assert.IsFalse(rr.IsError, rr.ToString() + " is an error"); |
|
Assert.IsTrue(rr.IsCompileTimeConstant, rr.ToString() + " is not a compile-time constant"); |
|
Type expectedType = expectedValue.GetType(); |
|
Assert.AreEqual(ResolveType(expectedType), rr.Type, "ResolveResult.Type is wrong"); |
|
if (expectedType.IsEnum) { |
|
Assert.AreEqual(Enum.GetUnderlyingType(expectedType), rr.ConstantValue.GetType(), "ResolveResult.ConstantValue has wrong Type"); |
|
Assert.AreEqual(Convert.ChangeType(expectedValue, Enum.GetUnderlyingType(expectedType)), rr.ConstantValue); |
|
} else { |
|
Assert.AreEqual(expectedType, rr.ConstantValue.GetType(), "ResolveResult.ConstantValue has wrong Type"); |
|
Assert.AreEqual(expectedValue, rr.ConstantValue); |
|
} |
|
} |
|
|
|
protected void AssertType(Type expectedType, ResolveResult rr) |
|
{ |
|
Assert.IsFalse(rr.IsError, rr.ToString() + " is an error"); |
|
Assert.IsFalse(rr.IsCompileTimeConstant, rr.ToString() + " is a compile-time constant"); |
|
Assert.AreEqual(compilation.FindType(expectedType), rr.Type); |
|
} |
|
|
|
protected void AssertError(Type expectedType, ResolveResult rr) |
|
{ |
|
Assert.IsTrue(rr.IsError, rr.ToString() + " is not an error, but an error was expected"); |
|
Assert.IsFalse(rr.IsCompileTimeConstant, rr.ToString() + " is a compile-time constant"); |
|
Assert.AreEqual(compilation.FindType(expectedType), rr.Type); |
|
} |
|
|
|
protected void TestOperator(UnaryOperatorType op, ResolveResult input, |
|
Conversion expectedConversion, Type expectedResultType) |
|
{ |
|
CSharpResolver resolver = new CSharpResolver(compilation); |
|
var rr = resolver.ResolveUnaryOperator(op, input); |
|
AssertType(expectedResultType, rr); |
|
Assert.AreEqual(typeof(OperatorResolveResult), rr.GetType()); |
|
var uorr = (OperatorResolveResult)rr; |
|
AssertConversion(uorr.Operands[0], input, expectedConversion, "Conversion"); |
|
} |
|
|
|
protected void TestOperator(ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, |
|
Conversion expectedLeftConversion, Conversion expectedRightConversion, Type expectedResultType) |
|
{ |
|
CSharpResolver resolver = new CSharpResolver(compilation); |
|
var rr = resolver.ResolveBinaryOperator(op, lhs, rhs); |
|
AssertType(expectedResultType, rr); |
|
Assert.AreEqual(typeof(OperatorResolveResult), rr.GetType()); |
|
var borr = (OperatorResolveResult)rr; |
|
AssertConversion(borr.Operands[0], lhs, expectedLeftConversion, "Left conversion"); |
|
AssertConversion(borr.Operands[1], rhs, expectedRightConversion, "Right conversion"); |
|
} |
|
|
|
protected void AssertConversion(ResolveResult conversionResult, ResolveResult expectedRR, Conversion expectedConversion, string text) |
|
{ |
|
if (expectedConversion == Conversion.IdentityConversion) { |
|
Assert.AreSame(expectedRR, conversionResult, "Expected no " + text); |
|
} else { |
|
ConversionResolveResult crr = conversionResult as ConversionResolveResult; |
|
Assert.IsNotNull(crr, "Could not find ConversionResolveResult for " + text); |
|
Assert.AreEqual(expectedConversion, crr.Conversion, text); |
|
Assert.AreSame(expectedRR, crr.Input, "Input of " + text); |
|
} |
|
} |
|
|
|
protected IEnumerable<TextLocation> FindDollarSigns(string code) |
|
{ |
|
int line = 1; |
|
int col = 1; |
|
foreach (char c in code) { |
|
if (c == '$') { |
|
yield return new TextLocation(line, col); |
|
} else if (c == '\n') { |
|
line++; |
|
col = 1; |
|
} else { |
|
col++; |
|
} |
|
} |
|
} |
|
|
|
protected Tuple<CSharpAstResolver, AstNode> PrepareResolver(string code) |
|
{ |
|
SyntaxTree syntaxTree = new CSharpParser().Parse(new StringReader(code.Replace("$", "")), "code.cs"); |
|
|
|
TextLocation[] dollars = FindDollarSigns(code).ToArray(); |
|
Assert.AreEqual(2, dollars.Length, "Expected 2 dollar signs marking start+end of desired node"); |
|
|
|
SetUp(); |
|
|
|
CSharpParsedFile parsedFile = syntaxTree.ToTypeSystem(); |
|
project = project.UpdateProjectContent(null, parsedFile); |
|
compilation = project.CreateCompilation(); |
|
|
|
CSharpAstResolver resolver = new CSharpAstResolver(compilation, syntaxTree, parsedFile); |
|
return Tuple.Create(resolver, FindNode(syntaxTree, dollars[0], dollars[1])); |
|
} |
|
|
|
protected ResolveResult Resolve(string code) |
|
{ |
|
var prep = PrepareResolver(code); |
|
Debug.WriteLine(new string('=', 70)); |
|
Debug.WriteLine("Starting new resolver for " + prep.Item2); |
|
|
|
ResolveResult rr = prep.Item1.Resolve(prep.Item2); |
|
Assert.IsNotNull(rr, "ResolveResult is null - did something go wrong while navigating to the target node?"); |
|
Debug.WriteLine("ResolveResult is " + rr); |
|
return rr; |
|
} |
|
|
|
protected Conversion GetConversion(string code) |
|
{ |
|
var prep = PrepareResolver(code); |
|
return prep.Item1.GetConversion((Expression)prep.Item2); |
|
} |
|
|
|
protected IType GetExpectedType(string code) |
|
{ |
|
var prep = PrepareResolver(code); |
|
return prep.Item1.GetExpectedType((Expression)prep.Item2); |
|
} |
|
|
|
protected T Resolve<T>(string code) where T : ResolveResult |
|
{ |
|
ResolveResult rr = Resolve(code); |
|
Assert.IsNotNull(rr); |
|
if (typeof(T) == typeof(LambdaResolveResult)) { |
|
Assert.IsTrue(rr is LambdaResolveResult, "Resolve should be " + typeof(T).Name + ", but was " + rr.GetType().Name); |
|
} else { |
|
Assert.IsTrue(rr.GetType() == typeof(T), "Resolve should be " + typeof(T).Name + ", but was " + rr.GetType().Name); |
|
} |
|
return (T)rr; |
|
} |
|
|
|
protected AstNode FindNode(SyntaxTree syntaxTree, TextLocation start, TextLocation end) |
|
{ |
|
FindNodeVisitor fnv = new FindNodeVisitor(start, end); |
|
syntaxTree.AcceptVisitor(fnv); |
|
Assert.IsNotNull(fnv.ResultNode, "Did not find DOM node at the specified location"); |
|
return fnv.ResultNode; |
|
} |
|
|
|
sealed class FindNodeVisitor : DepthFirstAstVisitor |
|
{ |
|
readonly TextLocation start; |
|
readonly TextLocation end; |
|
public AstNode ResultNode; |
|
|
|
public FindNodeVisitor(TextLocation start, TextLocation end) |
|
{ |
|
this.start = start; |
|
this.end = end; |
|
} |
|
|
|
protected override void VisitChildren(AstNode node) |
|
{ |
|
if (node.StartLocation == start && node.EndLocation == end) { |
|
if (ResultNode != null) |
|
throw new InvalidOperationException("found multiple nodes with same start+end"); |
|
ResultNode = node; |
|
return; |
|
} |
|
base.VisitChildren(node); |
|
} |
|
} |
|
|
|
protected ResolveResult ResolveAtLocation(string code) |
|
{ |
|
SyntaxTree syntaxTree = new CSharpParser().Parse(new StringReader(code.Replace("$", "")), "test.cs"); |
|
|
|
TextLocation[] dollars = FindDollarSigns(code).ToArray(); |
|
Assert.AreEqual(1, dollars.Length, "Expected 1 dollar signs marking the location"); |
|
|
|
SetUp(); |
|
|
|
CSharpParsedFile parsedFile = syntaxTree.ToTypeSystem(); |
|
project = project.UpdateProjectContent(null, parsedFile); |
|
compilation = project.CreateCompilation(); |
|
|
|
ResolveResult rr = Resolver.ResolveAtLocation.Resolve(compilation, parsedFile, syntaxTree, dollars[0]); |
|
return rr; |
|
} |
|
|
|
protected T ResolveAtLocation<T>(string code) where T : ResolveResult |
|
{ |
|
ResolveResult rr = ResolveAtLocation(code); |
|
Assert.IsNotNull(rr); |
|
Assert.IsTrue(rr.GetType() == typeof(T), "Resolve should be " + typeof(T).Name + ", but was " + rr.GetType().Name); |
|
return (T)rr; |
|
} |
|
} |
|
}
|
|
|