diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
index de7d8470bc..7c25be98f4 100644
--- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
+++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
@@ -286,6 +286,8 @@
+
+
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
index f634f266c8..20255c6e05 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
@@ -119,6 +119,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
///
public CSharpResolver WithCheckForOverflow(bool checkForOverflow)
{
+ if (checkForOverflow == this.checkForOverflow)
+ return this;
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
}
@@ -431,7 +433,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// evaluate as (E)(~(U)x);
var U = compilation.FindType(expression.ConstantValue.GetType());
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
- return CheckErrorAndResolveCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum));
+ return CheckErrorAndResolveUncheckedCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum));
} else {
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
}
@@ -911,7 +913,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
rhs = ResolveCast(elementType, rhs);
if (rhs.IsError)
return rhs;
- return CheckErrorAndResolveCast(elementType, ResolveBinaryOperator(BinaryOperatorType.Subtract, lhs, rhs));
+ return CheckErrorAndResolveUncheckedCast(elementType, ResolveBinaryOperator(BinaryOperatorType.Subtract, lhs, rhs));
}
IType resultType = MakeNullable(elementType, isNullable);
return BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs, isNullable);
@@ -937,7 +939,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
rhs = ResolveCast(elementType, rhs);
if (rhs.IsError)
return rhs;
- return CheckErrorAndResolveCast(enumType, ResolveBinaryOperator(op, lhs, rhs));
+ return CheckErrorAndResolveUncheckedCast(enumType, ResolveBinaryOperator(op, lhs, rhs));
}
IType resultType = MakeNullable(enumType, isNullable);
return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
@@ -1260,7 +1262,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
if (c == Conversion.IdentityConversion)
return rr;
- else if (rr.IsCompileTimeConstant && c != Conversion.None)
+ else if (rr.IsCompileTimeConstant && c != Conversion.None && !c.IsUserDefined)
return ResolveCast(targetType, rr);
else
return new ConversionResolveResult(targetType, rr, c, checkForOverflow);
@@ -1269,7 +1271,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public ResolveResult ResolveCast(IType targetType, ResolveResult expression)
{
// C# 4.0 spec: §7.7.6 Cast expressions
- if (expression.IsCompileTimeConstant) {
+ Conversion c = conversions.ExplicitConversion(expression, targetType);
+ if (expression.IsCompileTimeConstant && !c.IsUserDefined) {
TypeCode code = ReflectionHelper.GetTypeCode(targetType);
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) {
try {
@@ -1293,7 +1296,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
}
- Conversion c = conversions.ExplicitConversion(expression, targetType);
return new ConversionResolveResult(targetType, expression, c, checkForOverflow);
}
@@ -1302,12 +1304,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return Utils.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow);
}
- ResolveResult CheckErrorAndResolveCast(IType targetType, ResolveResult expression)
+ ResolveResult CheckErrorAndResolveUncheckedCast(IType targetType, ResolveResult expression)
{
if (expression.IsError)
return expression;
else
- return ResolveCast(targetType, expression);
+ return WithCheckForOverflow(false).ResolveCast(targetType, expression);
}
#endregion
@@ -1596,7 +1598,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
if (target.Type.Kind == TypeKind.Dynamic)
- return new ResolveResult(SpecialType.Dynamic);
+ return new DynamicMemberResolveResult(target, identifier);
MemberLookup lookup = CreateMemberLookup(lookupMode);
ResolveResult result;
@@ -1893,8 +1895,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// C# 4.0 spec: §7.6.5
- if (target.Type.Kind == TypeKind.Dynamic)
- return new ResolveResult(SpecialType.Dynamic);
+ if (target.Type.Kind == TypeKind.Dynamic) {
+ return new DynamicInvocationResolveResult(target, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly());
+ }
MethodGroupResolveResult mgrr = target as MethodGroupResolveResult;
if (mgrr != null) {
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs
new file mode 100644
index 0000000000..6139be02c0
--- /dev/null
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs
@@ -0,0 +1,73 @@
+// 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.Globalization;
+using System.Linq;
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.TypeSystem;
+
+namespace ICSharpCode.NRefactory.CSharp.Resolver
+{
+ ///
+ /// Represents a single argument in a dynamic invocation.
+ ///
+ public class DynamicInvocationArgument {
+ ///
+ /// Parameter name, if the argument is named. Null otherwise.
+ ///
+ public readonly string Name;
+
+ ///
+ /// Value of the argument.
+ ///
+ public readonly ResolveResult Value;
+
+ public DynamicInvocationArgument(string name, ResolveResult value) {
+ Name = name;
+ Value = value;
+ }
+ }
+
+ ///
+ /// Represents the result of an invocation of a member of a dynamic object.
+ ///
+ public class DynamicInvocationResolveResult : ResolveResult
+ {
+ ///
+ /// Target of the invocation (a dynamic object).
+ ///
+ public readonly ResolveResult Target;
+
+ ///
+ /// Arguments for the call.
+ ///
+ public readonly IList Arguments;
+
+ public DynamicInvocationResolveResult(ResolveResult target, IList arguments) : base(SpecialType.Dynamic) {
+ this.Target = target;
+ this.Arguments = arguments ?? EmptyList.Instance;
+ }
+
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[Dynamic invocation ]");
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/DynamicMemberResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicMemberResolveResult.cs
new file mode 100644
index 0000000000..13e2ba1de6
--- /dev/null
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicMemberResolveResult.cs
@@ -0,0 +1,57 @@
+// 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.Globalization;
+using System.Linq;
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.TypeSystem;
+
+namespace ICSharpCode.NRefactory.CSharp.Resolver
+{
+ ///
+ /// Represents the result of an access to a member of a dynamic object.
+ ///
+ public class DynamicMemberResolveResult : ResolveResult
+ {
+ ///
+ /// Target of the member access (a dynamic object).
+ ///
+ public readonly ResolveResult Target;
+
+ ///
+ /// Name of the accessed member.
+ ///
+ public readonly string Member;
+
+ public DynamicMemberResolveResult(ResolveResult target, string member) : base(SpecialType.Dynamic) {
+ this.Target = target;
+ this.Member = member;
+ }
+
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[Dynamic member '{0}']", Member);
+ }
+
+ public override IEnumerable GetChildResults() {
+ return new[] { Target };
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
index 84e8f74526..f160fcbc60 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
@@ -854,21 +854,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
resolver.CurrentTypeResolveContext, propertyOrIndexerDeclaration.EntityType, name,
explicitInterfaceType, parameterTypeReferences: parameterTypeReferences);
}
+ // We need to use the property as current member so that indexer parameters can be resolved correctly.
+ resolver = resolver.WithCurrentMember(member);
+ var resolverWithPropertyAsMember = resolver;
for (AstNode node = propertyOrIndexerDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == PropertyDeclaration.GetterRole && member is IProperty) {
- resolver = resolver.PushBlock();
resolver = resolver.WithCurrentMember(((IProperty)member).Getter);
Scan(node);
- resolver = resolver.PopBlock();
- }
- else if (node.Role == PropertyDeclaration.SetterRole && member is IProperty) {
- resolver = resolver.PushBlock();
+ resolver = resolverWithPropertyAsMember;
+ } else if (node.Role == PropertyDeclaration.SetterRole && member is IProperty) {
resolver = resolver.WithCurrentMember(((IProperty)member).Setter);
Scan(node);
- resolver = resolver.PopBlock();
- }
- else {
+ resolver = resolverWithPropertyAsMember;
+ } else {
Scan(node);
}
}
@@ -908,21 +907,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
explicitInterfaceAstType.ToTypeReference());
}
}
-
+ resolver = resolver.WithCurrentMember(member);
+ var resolverWithEventAsMember = resolver;
+
for (AstNode node = eventDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == CustomEventDeclaration.AddAccessorRole && member is IEvent) {
- resolver = resolver.PushBlock();
resolver = resolver.WithCurrentMember(((IEvent)member).AddAccessor);
Scan(node);
- resolver = resolver.PopBlock();
- }
- else if (node.Role == CustomEventDeclaration.RemoveAccessorRole && member is IEvent) {
- resolver = resolver.PushBlock();
+ resolver = resolverWithEventAsMember;
+ } else if (node.Role == CustomEventDeclaration.RemoveAccessorRole && member is IEvent) {
resolver = resolver.WithCurrentMember(((IEvent)member).RemoveAccessor);
Scan(node);
- resolver = resolver.PopBlock();
- }
- else {
+ resolver = resolverWithEventAsMember;
+ } else {
Scan(node);
}
}
diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs
index b562f03a9e..97a7c67849 100644
--- a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs
+++ b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs
@@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck
solution.AllFiles.Count(),
solution.Projects.Count);
- //using (new Timer("ID String test... ")) TypeSystemTests.IDStringConsistencyCheck(solution);
+ using (new Timer("ID String test... ")) TypeSystemTests.IDStringConsistencyCheck(solution);
using (new Timer("Resolve unresolved members... ")) TypeSystemTests.ResolvedUnresolvedMembers(solution);
//RunTestOnAllFiles("Roundtripping test", RoundtripTest.RunTest);
RunTestOnAllFiles("Resolver test", ResolverTest.RunTest);
diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs b/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs
index a5b191edbb..ee367f8a68 100644
--- a/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs
+++ b/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs
@@ -71,7 +71,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck
if (CSharpAstResolver.IsUnresolvableNode(node))
throw new InvalidOperationException("Resolved unresolvable node");
if (!ParenthesizedExpression.ActsAsParenthesizedExpression(node))
- if (!resolveResults.Add(result))
+ if (!resolveResults.Add(result) && result != ErrorResolveResult.UnknownError)
throw new InvalidOperationException("Duplicate resolve result");
if (result.IsError && !allowErrors) {
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs
new file mode 100644
index 0000000000..088064320c
--- /dev/null
+++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.TypeSystem;
+using NUnit.Framework;
+
+namespace ICSharpCode.NRefactory.CSharp.Resolver {
+ [TestFixture]
+ public class DynamicTests : ResolverTestBase {
+ [Test]
+ public void AccessToDynamicMember() {
+ string program = @"using System;
+class TestClass {
+ void F() {
+ dynamic obj = null;
+ $obj.SomeProperty$ = 10;
+ }
+}";
+ var rr = Resolve(program);
+ Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic));
+ Assert.That(rr.Target is LocalResolveResult && ((LocalResolveResult)rr.Target).Variable.Name == "obj");
+ Assert.That(rr.Member, Is.EqualTo("SomeProperty"));
+ }
+
+ [Test]
+ public void DynamicInvocation() {
+ string program = @"using System;
+class TestClass {
+ void F() {
+ dynamic obj = null;
+ int a = 0;
+ string b = null;
+ $obj.SomeMethod(a, b)$;
+ }
+}";
+ var rr = Resolve(program);
+ Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic));
+ Assert.That(rr.Target, Is.InstanceOf());
+ var dynamicMember = (DynamicMemberResolveResult)rr.Target;
+ Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj");
+ Assert.That(dynamicMember.Member, Is.EqualTo("SomeMethod"));
+ Assert.That(rr.Arguments.Count, Is.EqualTo(2));
+ Assert.That(rr.Arguments[0].Name, Is.Null);
+ Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "a");
+ Assert.That(rr.Arguments[1].Name, Is.Null);
+ Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "b");
+ }
+
+ [Test]
+ public void DynamicInvocationWithNamedArguments() {
+ string program = @"using System;
+class TestClass {
+ void F() {
+ dynamic obj = null;
+ int a = 0, x = 0;
+ string b = null;
+ $obj.SomeMethod(x, param1: a, param2: b)$;
+ }
+}";
+ var rr = Resolve(program);
+ Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic));
+ Assert.That(rr.Target, Is.InstanceOf());
+ var dynamicMember = (DynamicMemberResolveResult)rr.Target;
+ Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj");
+ Assert.That(dynamicMember.Member, Is.EqualTo("SomeMethod"));
+ Assert.That(rr.Arguments.Count, Is.EqualTo(3));
+ Assert.That(rr.Arguments[0].Name, Is.Null);
+ Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "x");
+ Assert.That(rr.Arguments[1].Name, Is.EqualTo("param1"));
+ Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "a");
+ Assert.That(rr.Arguments[2].Name, Is.EqualTo("param2"));
+ Assert.That(rr.Arguments[2].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[2].Value).Variable.Name == "b");
+ }
+
+ [Test]
+ public void TwoDynamicInvocationsInARow() {
+ string program = @"using System;
+class TestClass {
+ void F() {
+ dynamic obj = null;
+ int a = 0, b = 0;
+ $obj.SomeMethod(a)(b)$;
+ }
+}";
+ var rr = Resolve(program);
+ Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic));
+ Assert.That(rr.Target, Is.InstanceOf());
+ var innerInvocation = (DynamicInvocationResolveResult)rr.Target;
+ Assert.That(innerInvocation.Target, Is.InstanceOf());
+ var dynamicMember = (DynamicMemberResolveResult)innerInvocation.Target;
+ Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj");
+ Assert.That(dynamicMember.Member, Is.EqualTo("SomeMethod"));
+ Assert.That(innerInvocation.Arguments.Count, Is.EqualTo(1));
+ Assert.That(innerInvocation.Arguments[0].Name, Is.Null);
+ Assert.That(innerInvocation.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)innerInvocation.Arguments[0].Value).Variable.Name == "a");
+ Assert.That(rr.Arguments.Count, Is.EqualTo(1));
+ Assert.That(rr.Arguments[0].Name, Is.Null);
+ Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "b");
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MethodTests.cs
index 185faa7410..eb997bcac5 100644
--- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MethodTests.cs
+++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MethodTests.cs
@@ -125,6 +125,21 @@ class TestClass {
Assert.IsTrue(ReferenceEquals(j1, j2));
Assert.IsTrue(ReferenceEquals(value1, value2));
}
+
+ [Test]
+ public void ResolveParameterDeclarationInIndexer()
+ {
+ string code = @"using System;
+class TestClass {
+ int[,] myField;
+ int this[$int i$, int j] {
+ get { return myField[i, j]; }
+ set { myField[i, j] = value; }
+ }
+}";
+ var rr = Resolve(code);
+ Assert.IsTrue(rr.IsParameter);
+ }
[Test]
public void ParameterIdentityInEventAdder()
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs
index 3635a4db1b..a87aa6d0f6 100644
--- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs
+++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs
@@ -257,5 +257,22 @@ class Test {
Assert.IsFalse(irr.IsError);
Assert.IsTrue(irr.IsLiftedOperator);
}
+
+ [Test]
+ public void UShortEnumNegation()
+ {
+ string program = @"
+class Test {
+ enum UShortEnum : ushort { Three = 3 }
+ static void Inc() {
+ checked { // even in checked context, the implicit cast back to enum is unchecked
+ var a = $~UShortEnum.Three$;
+ }
+ }
+}";
+ var rr = Resolve(program);
+ Assert.IsFalse(rr.IsError);
+ Assert.AreEqual(unchecked( (ushort)~3 ), rr.ConstantValue);
+ }
}
}
diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
index 471fb98b03..e44c23b565 100644
--- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
+++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
@@ -190,6 +190,7 @@
+
@@ -221,6 +222,7 @@
+
diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs
new file mode 100644
index 0000000000..2000207509
--- /dev/null
+++ b/ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs
@@ -0,0 +1,36 @@
+// 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 ICSharpCode.NRefactory.TypeSystem.Implementation;
+using NUnit.Framework;
+
+namespace ICSharpCode.NRefactory.TypeSystem
+{
+ [TestFixture]
+ public class LazyLoadedCecilLoaderTests : TypeSystemTests
+ {
+ [TestFixtureSetUp]
+ public void FixtureSetUp()
+ {
+ CecilLoader loader = new CecilLoader() { IncludeInternalMembers = true, LazyLoad = true };
+ IUnresolvedAssembly pc = loader.LoadAssemblyFile(typeof(TestCase.SimplePublicClass).Assembly.Location);
+ base.compilation = new SimpleCompilation(pc, CecilLoaderTests.Mscorlib);
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
index aa60bd995c..8b97a43897 100644
--- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
@@ -47,6 +47,16 @@ namespace ICSharpCode.NRefactory.TypeSystem
///
public bool IncludeInternalMembers { get; set; }
+ ///
+ /// Specifies whether to use lazy loading. The default is false.
+ /// If this property is set to true, the CecilLoader will not copy all the relevant information
+ /// out of the Cecil object model, but will maintain references to the Cecil objects.
+ /// This speeds up the loading process and avoids loading unnecessary information, but it causes
+ /// the Cecil objects to stay in memory (which can significantly increase memory usage).
+ /// It also prevents serialization of the Cecil-loaded type system.
+ ///
+ public bool LazyLoad { get; set; }
+
///
/// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members.
///
@@ -137,7 +147,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.CancellationToken.ThrowIfCancellationRequested();
if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) {
string name = td.Name;
- if (name.Length == 0 || name[0] == '<')
+ if (name.Length == 0)
continue;
var t = CreateTopLevelTypeDefinition(td);
@@ -166,6 +176,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// This causes ReadTypeReference() to use for references
/// in that module.
///
+ [CLSCompliant(false)]
public void SetCurrentModule(ModuleDefinition module)
{
this.currentModule = module;
@@ -1449,65 +1460,76 @@ namespace ICSharpCode.NRefactory.TypeSystem
{
string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name);
var td = new DefaultUnresolvedTypeDefinition(typeDefinition.Namespace, name);
- InitTypeParameters(typeDefinition, td);
+ if (typeDefinition.HasGenericParameters)
+ InitTypeParameters(typeDefinition, td.TypeParameters);
return td;
}
- static void InitTypeParameters(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
+ static void InitTypeParameters(TypeDefinition typeDefinition, IList typeParameters)
{
// Type parameters are initialized within the constructor so that the class can be put into the type storage
// before the rest of the initialization runs - this allows it to be available for early binding as soon as possible.
for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) {
if (typeDefinition.GenericParameters[i].Position != i)
throw new InvalidOperationException("g.Position != i");
- td.TypeParameters.Add(new DefaultUnresolvedTypeParameter(
+ typeParameters.Add(new DefaultUnresolvedTypeParameter(
EntityType.TypeDefinition, i, typeDefinition.GenericParameters[i].Name));
}
}
+ void InitTypeParameterConstraints(TypeDefinition typeDefinition, IList typeParameters)
+ {
+ for (int i = 0; i < typeParameters.Count; i++) {
+ AddConstraints((DefaultUnresolvedTypeParameter)typeParameters[i], typeDefinition.GenericParameters[i]);
+ }
+ }
+
void InitTypeDefinition(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
{
+ td.Kind = GetTypeKind(typeDefinition);
InitTypeModifiers(typeDefinition, td);
+ InitTypeParameterConstraints(typeDefinition, td.TypeParameters);
- if (typeDefinition.HasGenericParameters) {
- for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) {
- AddConstraints((DefaultUnresolvedTypeParameter)td.TypeParameters[i], typeDefinition.GenericParameters[i]);
- }
- }
-
- InitNestedTypes(typeDefinition, td); // nested types can be initialized only after generic parameters were created
+ // nested types can be initialized only after generic parameters were created
+ InitNestedTypes(typeDefinition, td, td.NestedTypes);
AddAttributes(typeDefinition, td);
td.HasExtensionMethods = HasExtensionAttribute(typeDefinition);
+ InitBaseTypes(typeDefinition, td.BaseTypes);
+
+ td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum);
+ InitMembers(typeDefinition, td, td.Members);
+ if (HasCecilReferences)
+ typeSystemTranslationTable[td] = typeDefinition;
+ if (this.InterningProvider != null) {
+ td.ApplyInterningProvider(this.InterningProvider);
+ }
+ td.Freeze();
+ }
+
+ void InitBaseTypes(TypeDefinition typeDefinition, IList baseTypes)
+ {
// set base classes
if (typeDefinition.IsEnum) {
foreach (FieldDefinition enumField in typeDefinition.Fields) {
if (!enumField.IsStatic) {
- td.BaseTypes.Add(ReadTypeReference(enumField.FieldType));
+ baseTypes.Add(ReadTypeReference(enumField.FieldType));
break;
}
}
} else {
if (typeDefinition.BaseType != null) {
- td.BaseTypes.Add(ReadTypeReference(typeDefinition.BaseType));
+ baseTypes.Add(ReadTypeReference(typeDefinition.BaseType));
}
if (typeDefinition.HasInterfaces) {
foreach (TypeReference iface in typeDefinition.Interfaces) {
- td.BaseTypes.Add(ReadTypeReference(iface));
+ baseTypes.Add(ReadTypeReference(iface));
}
}
}
-
- InitMembers(typeDefinition, td);
- if (HasCecilReferences)
- typeSystemTranslationTable[td] = typeDefinition;
- if (this.InterningProvider != null) {
- td.ApplyInterningProvider(this.InterningProvider);
- }
- td.Freeze();
}
- void InitNestedTypes(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
+ void InitNestedTypes(TypeDefinition typeDefinition, IUnresolvedTypeDefinition declaringTypeDefinition, IList nestedTypes)
{
if (!typeDefinition.HasNestedTypes)
return;
@@ -1522,33 +1544,35 @@ namespace ICSharpCode.NRefactory.TypeSystem
int pos = name.LastIndexOf('/');
if (pos > 0)
name = name.Substring(pos + 1);
- if (name.Length == 0 || name[0] == '<')
- continue;
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
- var nestedType = new DefaultUnresolvedTypeDefinition(td, name);
- InitTypeParameters(nestedTypeDef, nestedType);
- td.NestedTypes.Add(nestedType);
+ var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name);
+ InitTypeParameters(nestedTypeDef, nestedType.TypeParameters);
+ nestedTypes.Add(nestedType);
InitTypeDefinition(nestedTypeDef, nestedType);
}
}
}
- static void InitTypeModifiers(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
+ static TypeKind GetTypeKind(TypeDefinition typeDefinition)
{
// set classtype
if (typeDefinition.IsInterface) {
- td.Kind = TypeKind.Interface;
+ return TypeKind.Interface;
} else if (typeDefinition.IsEnum) {
- td.Kind = TypeKind.Enum;
+ return TypeKind.Enum;
} else if (typeDefinition.IsValueType) {
- td.Kind = TypeKind.Struct;
+ return TypeKind.Struct;
} else if (IsDelegate(typeDefinition)) {
- td.Kind = TypeKind.Delegate;
+ return TypeKind.Delegate;
} else if (IsModule(typeDefinition)) {
- td.Kind = TypeKind.Module;
+ return TypeKind.Module;
} else {
- td.Kind = TypeKind.Class;
+ return TypeKind.Class;
}
+ }
+
+ static void InitTypeModifiers(TypeDefinition typeDefinition, AbstractUnresolvedEntity td)
+ {
td.IsSealed = typeDefinition.IsSealed;
td.IsAbstract = typeDefinition.IsAbstract;
switch (typeDefinition.Attributes & TypeAttributes.VisibilityMask) {
@@ -1598,9 +1622,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
return false;
}
- void InitMembers(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
+ void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList members)
{
- td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum);
if (typeDefinition.HasMethods) {
foreach (MethodDefinition method in typeDefinition.Methods) {
if (IsVisible(method.Attributes) && !IsAccessor(method.SemanticsAttributes)) {
@@ -1611,14 +1634,14 @@ namespace ICSharpCode.NRefactory.TypeSystem
else if (method.Name.StartsWith("op_", StringComparison.Ordinal))
type = EntityType.Operator;
}
- td.Members.Add(ReadMethod(method, td, type));
+ members.Add(ReadMethod(method, td, type));
}
}
}
if (typeDefinition.HasFields) {
foreach (FieldDefinition field in typeDefinition.Fields) {
if (IsVisible(field.Attributes) && !field.IsSpecialName) {
- td.Members.Add(ReadField(field, td));
+ members.Add(ReadField(field, td));
}
}
}
@@ -1634,14 +1657,14 @@ namespace ICSharpCode.NRefactory.TypeSystem
bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes);
if (getterVisible || setterVisible) {
EntityType type = property.Name == defaultMemberName ? EntityType.Indexer : EntityType.Property;
- td.Members.Add(ReadProperty(property, td, type));
+ members.Add(ReadProperty(property, td, type));
}
}
}
if (typeDefinition.HasEvents) {
foreach (EventDefinition ev in typeDefinition.Events) {
if (ev.AddMethod != null && IsVisible(ev.AddMethod.Attributes)) {
- td.Members.Add(ReadEvent(ev, td));
+ members.Add(ReadEvent(ev, td));
}
}
}
@@ -1653,6 +1676,140 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
#endregion
+ #region Lazy-Loaded Type Definition
+ sealed class LazyCecilTypeDefinition : AbstractUnresolvedEntity, IUnresolvedTypeDefinition
+ {
+ readonly CecilLoader loader;
+ readonly string namespaceName;
+ readonly TypeDefinition cecilTypeDef;
+ readonly TypeKind kind;
+ readonly IList typeParameters;
+
+ IList baseTypes;
+ IList nestedTypes;
+ IList members;
+
+ public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition)
+ {
+ this.loader = loader;
+ this.cecilTypeDef = typeDefinition;
+ this.EntityType = EntityType.TypeDefinition;
+ this.namespaceName = cecilTypeDef.Namespace;
+ this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(cecilTypeDef.Name);
+ var tps = new List();
+ InitTypeParameters(cecilTypeDef, tps);
+ this.typeParameters = FreezableHelper.FreezeList(tps);
+
+ this.kind = GetTypeKind(typeDefinition);
+ InitTypeModifiers(typeDefinition, this);
+ loader.InitTypeParameterConstraints(typeDefinition, typeParameters);
+
+ loader.AddAttributes(typeDefinition, this);
+ flags[FlagHasExtensionMethods] = HasExtensionAttribute(typeDefinition);
+
+ if (loader.HasCecilReferences)
+ loader.typeSystemTranslationTable[this] = typeDefinition;
+ if (loader.InterningProvider != null) {
+ this.ApplyInterningProvider(loader.InterningProvider);
+ }
+ this.Freeze();
+ }
+
+ public override string Namespace {
+ get { return namespaceName; }
+ set { throw new NotSupportedException(); }
+ }
+
+ public TypeKind Kind {
+ get { return kind; }
+ }
+
+ public IList TypeParameters {
+ get { return typeParameters; }
+ }
+
+ public IList BaseTypes {
+ get {
+ var result = LazyInit.VolatileRead(ref this.baseTypes);
+ if (result != null) {
+ return result;
+ } else {
+ result = new List();
+ loader.InitBaseTypes(cecilTypeDef, result);
+ return LazyInit.GetOrSet(ref this.baseTypes, FreezableHelper.FreezeList(result));
+ }
+ }
+ }
+
+ public IList NestedTypes {
+ get {
+ var result = LazyInit.VolatileRead(ref this.nestedTypes);
+ if (result != null) {
+ return result;
+ } else {
+ result = new List();
+ loader.InitNestedTypes(cecilTypeDef, this, result);
+ return LazyInit.GetOrSet(ref this.nestedTypes, FreezableHelper.FreezeList(result));
+ }
+ }
+ }
+
+ public IList Members {
+ get {
+ var result = LazyInit.VolatileRead(ref this.members);
+ if (result != null) {
+ return result;
+ } else {
+ result = new List();
+ loader.InitMembers(cecilTypeDef, this, result);
+ return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result));
+ }
+ }
+ }
+
+ public IEnumerable Methods {
+ get { return Members.OfType(); }
+ }
+
+ public IEnumerable Properties {
+ get { return Members.OfType(); }
+ }
+
+ public IEnumerable Fields {
+ get { return Members.OfType(); }
+ }
+
+ public IEnumerable Events {
+ get { return Members.OfType(); }
+ }
+
+ public bool AddDefaultConstructorIfRequired {
+ get { return kind == TypeKind.Struct || kind == TypeKind.Enum; }
+ }
+
+ public bool? HasExtensionMethods {
+ get { return flags[FlagHasExtensionMethods]; }
+ // we always return true or false, never null.
+ // FlagHasNoExtensionMethods is unused in LazyCecilTypeDefinition
+ }
+
+ public IType Resolve(ITypeResolveContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+ if (context.CurrentAssembly == null)
+ throw new ArgumentException("An ITypeDefinition cannot be resolved in a context without a current assembly.");
+ return context.CurrentAssembly.GetTypeDefinition(this)
+ ?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count);
+ }
+
+ public ITypeResolveContext CreateResolveContext(ITypeResolveContext parentContext)
+ {
+ return parentContext;
+ }
+ }
+ #endregion
+
#region Read Method
[CLSCompliant(false)]
public IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, EntityType methodType = EntityType.Method)
diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
index 508f9ce810..1438505d8b 100644
--- a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
@@ -48,6 +48,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
///
bool? HasExtensionMethods { get; }
+ ///
+ /// Gets whether this unresolved type definition causes the addition of a default constructor
+ /// if no other constructor is present.
+ ///
+ bool AddDefaultConstructorIfRequired { get; }
+
///
/// Looks up the resolved type definition from the corresponding to this unresolved
/// type definition.
diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
index d0a040e454..66d29b8c0c 100644
--- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
@@ -50,7 +50,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
internal const ushort FlagShadowing = 0x0008;
internal const ushort FlagSynthetic = 0x0010;
internal const ushort FlagStatic = 0x0020;
- // flags for DefaultUnresolvedTypeDefinition
+ // flags for DefaultUnresolvedTypeDefinition/LazyCecilTypeDefinition
internal const ushort FlagAddDefaultConstructorIfRequired = 0x0040;
internal const ushort FlagHasExtensionMethods = 0x0080;
internal const ushort FlagHasNoExtensionMethods = 0x0100;
diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
index c4fb589e9b..4ea28dbe69 100644
--- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
@@ -322,10 +322,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
}
}
- DefaultUnresolvedTypeDefinition dutd = part as DefaultUnresolvedTypeDefinition;
- if (dutd != null) {
- addDefaultConstructorIfRequired |= dutd.AddDefaultConstructorIfRequired;
- }
+ addDefaultConstructorIfRequired |= part.AddDefaultConstructorIfRequired;
}
if (addDefaultConstructorIfRequired) {
TypeKind kind = this.Kind;