From 4f70f161495229457001b7a75666cc2b001cc35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Thu, 19 Jul 2012 08:01:21 +0200 Subject: [PATCH 01/17] [CodeActions] Implemented abstract members action/Improved implement interface action & fixed unit tests. --- .../ImplementAbstractMembersAction.cs | 65 +++++++++- .../CodeActions/ImplementInterfaceAction.cs | 111 ++++++++++++++++-- .../ImplementAbstractMembersTest.cs | 10 +- .../CodeActions/ImplementInterfaceTests.cs | 8 +- .../TypeMembers/EventDeclarationTests.cs | 6 +- 5 files changed, 173 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs index 8de75ab63e..97a4b9b00e 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs @@ -31,7 +31,7 @@ using System.Linq; namespace ICSharpCode.NRefactory.CSharp.Refactoring { -// [ContextAction("Implement abstract members", Description = "Implements abstract members from an abstract class.")] + [ContextAction("Implement abstract members", Description = "Implements abstract members from an abstract class.")] public class ImplementAbstractMembersAction : ICodeActionProvider { public IEnumerable GetActions(RefactoringContext context) @@ -47,15 +47,70 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring if (resolveResult.Type.Kind != TypeKind.Class || resolveResult.Type.GetDefinition() == null || !resolveResult.Type.GetDefinition().IsAbstract) yield break; - yield break; - /* + var toImplement = CollectMembersToImplement(state.CurrentTypeDefinition, resolveResult.Type); + if (toImplement.Count == 0) + yield break; + yield return new CodeAction(context.TranslateString("Implement abstract members"), script => { script.InsertWithCursor( context.TranslateString("Implement abstract members"), state.CurrentTypeDefinition, - ImplementInterfaceAction.GenerateImplementation (context, toImplement) + ImplementInterfaceAction.GenerateImplementation (context, toImplement.Select (m => Tuple.Create (m, false))).Select (entity => { + var decl = entity as EntityDeclaration; + if (decl != null) + decl.Modifiers |= Modifiers.Override; + return entity; + }) ); - });*/ + }); + } + + public static List CollectMembersToImplement(ITypeDefinition implementingType, IType abstractType) + { + var def = abstractType.GetDefinition(); + var toImplement = new List(); + bool alreadyImplemented; + + // Stub out non-implemented events defined by @iface + foreach (var ev in abstractType.GetEvents (e => !e.IsSynthetic && e.IsAbstract)) { + alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( + x => x.Kind != TypeKind.Interface && x.Events.Any (y => y.Name == ev.Name) + ); + + if (!alreadyImplemented) + toImplement.Add(ev); + } + + // Stub out non-implemented methods defined by @iface + foreach (var method in abstractType.GetMethods (d => !d.IsSynthetic && d.IsAbstract)) { + alreadyImplemented = false; + + foreach (var cmet in implementingType.GetMethods ()) { + if (!cmet.IsAbstract && ImplementInterfaceAction.CompareMethods(method, cmet)) { + alreadyImplemented = true; + } + } + if (!alreadyImplemented) + toImplement.Add(method); + } + + // Stub out non-implemented properties defined by @iface + foreach (var prop in abstractType.GetProperties (p => !p.IsSynthetic && p.IsAbstract)) { + alreadyImplemented = false; + foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { + if (t.Kind == TypeKind.Interface) + continue; + foreach (IProperty cprop in t.Properties) { + if (!cprop.IsAbstract && cprop.Name == prop.Name) { + alreadyImplemented = true; + } + } + } + if (!alreadyImplemented) + toImplement.Add(prop); + } + return toImplement; } + } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs index 7233ea818b..f4b60dc58b 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs @@ -73,9 +73,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } foreach (var kv in nodes) { - yield return new PreProcessorDirective( - PreProcessorDirectiveType.Region, - string.Format("{0} implementation", kv.Key.Name)); + if (kv.Key.Kind == TypeKind.Interface) { + yield return new PreProcessorDirective( + PreProcessorDirectiveType.Region, + string.Format("{0} implementation", kv.Key.Name)); + } else { + yield return new PreProcessorDirective( + PreProcessorDirectiveType.Region, + string.Format("implemented abstract members of {0}", kv.Key.Name)); + } foreach (var member in kv.Value) yield return member; yield return new PreProcessorDirective( @@ -88,11 +94,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring { switch (member.Item1.EntityType) { case EntityType.Property: - return null; + return GenerateProperty(context, (IProperty)member.Item1, member.Item2); case EntityType.Indexer: - return null; + return GenerateIndexer(context, (IProperty)member.Item1, member.Item2); case EntityType.Event: - return null; + return GenerateEvent(context, (IEvent)member.Item1, member.Item2); case EntityType.Method: return GenerateMethod(context, (IMethod)member.Item1, member.Item2); default: @@ -100,6 +106,87 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } } + static AstNode GenerateEvent(RefactoringContext context, IEvent evt, bool explicitImplementation) + { + if (!explicitImplementation) { + return new EventDeclaration() { + Modifiers = Modifiers.Public, + Name = evt.Name, + ReturnType = context.CreateShortType (evt.ReturnType) + }; + } + return new CustomEventDeclaration () { + Name = evt.Name, + ReturnType = context.CreateShortType (evt.ReturnType), + PrivateImplementationType = context.CreateShortType(evt.DeclaringType), + AddAccessor = new Accessor { + Body = new BlockStatement() { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }, + RemoveAccessor = new Accessor { + Body = new BlockStatement() { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + } + }; + } + + static AstNode GenerateProperty(RefactoringContext context, IProperty property, bool explicitImplementation) + { + var result = new PropertyDeclaration() { + Name = property.Name, + ReturnType = context.CreateShortType (property.ReturnType) + }; + + if (!explicitImplementation) { + result.Modifiers = Modifiers.Public; + } else { + result.PrivateImplementationType = context.CreateShortType(property.DeclaringType); + } + + if (property.CanGet) + result.Getter = new Accessor (); + if (property.CanSet) + result.Getter = new Accessor (); + + return result; + } + + static AstNode GenerateIndexer(RefactoringContext context, IProperty indexer, bool explicitImplementation) + { + var result = new IndexerDeclaration() { + ReturnType = context.CreateShortType (indexer.ReturnType) + }; + + if (!explicitImplementation) { + result.Modifiers = Modifiers.Public; + } else { + result.PrivateImplementationType = context.CreateShortType(indexer.DeclaringType); + } + + foreach (var p in indexer.Parameters) { + ParameterModifier modifier; + if (p.IsOut) { + modifier = ParameterModifier.Out; + } else if (p.IsRef) { + modifier = ParameterModifier.Ref; + } else if (p.IsParams) { + modifier = ParameterModifier.Params; + } else { + modifier = ParameterModifier.None; + } + result.Parameters.Add(new ParameterDeclaration(context.CreateShortType(p.Type), p.Name, modifier)); + } + + if (indexer.CanGet) + result.Getter = new Accessor (); + if (indexer.CanSet) + result.Getter = new Accessor (); + + return result; + } + static AstNode GenerateMethod(RefactoringContext context, IMethod method, bool explicitImplementation) { var result = new MethodDeclaration() { @@ -168,7 +255,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring bool alreadyImplemented; // Stub out non-implemented events defined by @iface - foreach (var ev in interfaceType.GetEvents (e => !e.IsSynthetic && e.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).Reverse ()) { + foreach (var evGroup in interfaceType.GetEvents (e => !e.IsSynthetic && e.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).GroupBy (m => m.DeclaringType).Reverse ()) + foreach (var ev in evGroup) { bool needsExplicitly = explicitly; alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( x => x.Kind != TypeKind.Interface && x.Events.Any (y => y.Name == ev.Name) @@ -179,7 +267,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } // Stub out non-implemented methods defined by @iface - foreach (var method in interfaceType.GetMethods (d => !d.IsSynthetic /* && d.DeclaringTypeDefinition.ReflectionName == def.ReflectionName*/).Reverse ()) { + foreach (var methodGroup in interfaceType.GetMethods (d => !d.IsSynthetic).GroupBy (m => m.DeclaringType).Reverse ()) + foreach (var method in methodGroup) { + bool needsExplicitly = explicitly; alreadyImplemented = false; @@ -198,7 +288,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } // Stub out non-implemented properties defined by @iface - foreach (var prop in interfaceType.GetProperties (p => !p.IsSynthetic && p.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).Reverse ()) { + foreach (var propGroup in interfaceType.GetProperties (p => !p.IsSynthetic && p.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).GroupBy (m => m.DeclaringType).Reverse ()) + foreach (var prop in propGroup) { bool needsExplicitly = explicitly; alreadyImplemented = false; foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { @@ -219,7 +310,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring return toImplement; } - static bool CompareMethods (IMethod interfaceMethod, IMethod typeMethod) + internal static bool CompareMethods (IMethod interfaceMethod, IMethod typeMethod) { if (typeMethod.IsExplicitInterfaceImplementation) return typeMethod.ImplementedInterfaceMembers.Any (m => m.Equals (interfaceMethod)); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs index adc2a57e2a..9bf6d38732 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs @@ -29,12 +29,11 @@ using ICSharpCode.NRefactory.CSharp.Refactoring; namespace ICSharpCode.NRefactory.CSharp.CodeActions { - [Ignore("TODO")] [TestFixture] public class ImplementAbstractMembersTest : ContextActionTestBase { [Test()] - public void TestSimpleInterface() + public void TestSimpleBaseType() { Test(@"abstract class Simple { public abstract void FooBar (string foo, int bar); @@ -50,12 +49,13 @@ class Foo : $Simple class Foo : Simple { #region implemented abstract members of Simple - public override void FooBar(string foo, int bar) + public override void FooBar (string foo, int bar) { - throw new System.NotImplementedException(); + throw new System.NotImplementedException (); } #endregion -}"); +} +"); } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs index a204e5f676..d0e18a42b4 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs @@ -236,14 +236,14 @@ public interface ITest : IA, IEnumerable class Foo : ITest { - #region IA implementation - public bool GetEnumerator () + #region IEnumerable implementation + public IEnumerator GetEnumerator () { throw new NotImplementedException (); } #endregion - #region IEnumerable implementation - IEnumerator IEnumerable.GetEnumerator () + #region IA implementation + bool IA.GetEnumerator () { throw new NotImplementedException (); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs index 6ef545b313..d4ead13bff 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs @@ -70,9 +70,9 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers MemberName = "EventHandler" }, Name = "MyEvent", - AddAccessor = new Accessor { Body = new BlockStatement() }, - RemoveAccessor = new Accessor { Body = new BlockStatement() } - }); + AddAccessor = new Accessor { Body = new BlockStatement() }, + RemoveAccessor = new Accessor { Body = new BlockStatement() } + }); } [Test] From c7de3dabcb207759acd6027ba9b40f766f5d5cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Thu, 19 Jul 2012 08:08:07 +0200 Subject: [PATCH 02/17] [CodeAction] Fixed getter/setters of the implemented interface/abstract class properties & indexers. --- .../CodeActions/ImplementInterfaceAction.cs | 193 ++++++++++-------- 1 file changed, 109 insertions(+), 84 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs index f4b60dc58b..6c107a618b 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs @@ -23,7 +23,6 @@ // 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; using System.Threading; @@ -40,38 +39,38 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var type = context.GetNode(); if (type == null || type.Role != Roles.BaseType) yield break; - + var state = context.GetResolverStateBefore(type); if (state.CurrentTypeDefinition == null) yield break; - + var resolveResult = context.Resolve(type); if (resolveResult.Type.Kind != TypeKind.Interface) yield break; - + var toImplement = CollectMembersToImplement(state.CurrentTypeDefinition, resolveResult.Type, false); if (toImplement.Count == 0) yield break; - + yield return new CodeAction(context.TranslateString("Implement interface"), script => { script.InsertWithCursor( - context.TranslateString ("Implement Interface"), + context.TranslateString("Implement Interface"), state.CurrentTypeDefinition, - GenerateImplementation (context, toImplement) + GenerateImplementation(context, toImplement) ); }); } - + public static IEnumerable GenerateImplementation(RefactoringContext context, IEnumerable> toImplement) { var nodes = new Dictionary>(); - + foreach (var member in toImplement) { if (!nodes.ContainsKey(member.Item1.DeclaringType)) nodes [member.Item1.DeclaringType] = new List(); nodes [member.Item1.DeclaringType].Add(GenerateMemberImplementation(context, member)); } - + foreach (var kv in nodes) { if (kv.Key.Kind == TypeKind.Interface) { yield return new PreProcessorDirective( @@ -89,7 +88,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring ); } } - + static AstNode GenerateMemberImplementation(RefactoringContext context, Tuple member) { switch (member.Item1.EntityType) { @@ -105,7 +104,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring throw new ArgumentOutOfRangeException(); } } - + static AstNode GenerateEvent(RefactoringContext context, IEvent evt, bool explicitImplementation) { if (!explicitImplementation) { @@ -115,7 +114,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring ReturnType = context.CreateShortType (evt.ReturnType) }; } - return new CustomEventDeclaration () { + return new CustomEventDeclaration() { Name = evt.Name, ReturnType = context.CreateShortType (evt.ReturnType), PrivateImplementationType = context.CreateShortType(evt.DeclaringType), @@ -131,7 +130,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } }; } - + static AstNode GenerateProperty(RefactoringContext context, IProperty property, bool explicitImplementation) { var result = new PropertyDeclaration() { @@ -145,10 +144,28 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring result.PrivateImplementationType = context.CreateShortType(property.DeclaringType); } - if (property.CanGet) - result.Getter = new Accessor (); - if (property.CanSet) - result.Getter = new Accessor (); + if (property.CanGet) { + if (property.DeclaringType.Kind != TypeKind.Interface) { + result.Getter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } else { + result.Getter = new Accessor(); + } + } + if (property.CanSet) { + if (property.DeclaringType.Kind != TypeKind.Interface) { + result.Setter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } else { + result.Setter = new Accessor(); + } + } return result; } @@ -164,7 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } else { result.PrivateImplementationType = context.CreateShortType(indexer.DeclaringType); } - + foreach (var p in indexer.Parameters) { ParameterModifier modifier; if (p.IsOut) { @@ -178,12 +195,21 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } result.Parameters.Add(new ParameterDeclaration(context.CreateShortType(p.Type), p.Name, modifier)); } - - if (indexer.CanGet) - result.Getter = new Accessor (); - if (indexer.CanSet) - result.Getter = new Accessor (); + if (indexer.CanGet) { + result.Getter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } + if (indexer.CanSet) { + result.Setter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } return result; } @@ -196,40 +222,40 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) } }; - + if (!explicitImplementation) { result.Modifiers = Modifiers.Public; } else { result.PrivateImplementationType = context.CreateShortType(method.DeclaringType); } - + foreach (var typeParam in method.TypeParameters) { result.TypeParameters.Add(new TypeParameterDeclaration(typeParam.Name)); - + var constraint = new Constraint() { TypeParameter = new SimpleType(typeParam.Name) }; - + if (typeParam.HasDefaultConstructorConstraint) { - constraint.BaseTypes.Add (new PrimitiveType("new")); + constraint.BaseTypes.Add(new PrimitiveType("new")); } else if (typeParam.HasReferenceTypeConstraint) { - constraint.BaseTypes.Add (new PrimitiveType("class")); + constraint.BaseTypes.Add(new PrimitiveType("class")); } else if (typeParam.HasValueTypeConstraint) { - constraint.BaseTypes.Add (new PrimitiveType("struct")); + constraint.BaseTypes.Add(new PrimitiveType("struct")); } - + foreach (var type in typeParam.DirectBaseTypes) { if (type.FullName == "System.Object") continue; if (type.FullName == "System.ValueType") continue; - constraint.BaseTypes.Add (context.CreateShortType (type)); + constraint.BaseTypes.Add(context.CreateShortType(type)); } if (constraint.BaseTypes.Count == 0) continue; - result.Constraints.Add (constraint); + result.Constraints.Add(constraint); } - + foreach (var p in method.Parameters) { ParameterModifier modifier; if (p.IsOut) { @@ -243,78 +269,77 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } result.Parameters.Add(new ParameterDeclaration(context.CreateShortType(p.Type), p.Name, modifier)); } - + return result; } - - + public static List> CollectMembersToImplement(ITypeDefinition implementingType, IType interfaceType, bool explicitly) { var def = interfaceType.GetDefinition(); List> toImplement = new List>(); bool alreadyImplemented; - + // Stub out non-implemented events defined by @iface foreach (var evGroup in interfaceType.GetEvents (e => !e.IsSynthetic && e.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).GroupBy (m => m.DeclaringType).Reverse ()) - foreach (var ev in evGroup) { - bool needsExplicitly = explicitly; - alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( - x => x.Kind != TypeKind.Interface && x.Events.Any (y => y.Name == ev.Name) - ); - - if (!alreadyImplemented) - toImplement.Add(new Tuple(ev, needsExplicitly)); - } - + foreach (var ev in evGroup) { + bool needsExplicitly = explicitly; + alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( + x => x.Kind != TypeKind.Interface && x.Events.Any(y => y.Name == ev.Name) + ); + + if (!alreadyImplemented) + toImplement.Add(new Tuple(ev, needsExplicitly)); + } + // Stub out non-implemented methods defined by @iface foreach (var methodGroup in interfaceType.GetMethods (d => !d.IsSynthetic).GroupBy (m => m.DeclaringType).Reverse ()) - foreach (var method in methodGroup) { - - bool needsExplicitly = explicitly; - alreadyImplemented = false; + foreach (var method in methodGroup) { + + bool needsExplicitly = explicitly; + alreadyImplemented = false; - foreach (var cmet in implementingType.GetMethods ()) { - if (CompareMethods(method, cmet)) { - if (!needsExplicitly && !cmet.ReturnType.Equals(method.ReturnType)) - needsExplicitly = true; - else - alreadyImplemented |= !needsExplicitly /*|| cmet.InterfaceImplementations.Any (impl => impl.InterfaceType.Equals (interfaceType))*/; + foreach (var cmet in implementingType.GetMethods ()) { + if (CompareMethods(method, cmet)) { + if (!needsExplicitly && !cmet.ReturnType.Equals(method.ReturnType)) + needsExplicitly = true; + else + alreadyImplemented |= !needsExplicitly /*|| cmet.InterfaceImplementations.Any (impl => impl.InterfaceType.Equals (interfaceType))*/; + } } + if (toImplement.Where(t => t.Item1 is IMethod).Any(t => CompareMethods(method, (IMethod)t.Item1))) + needsExplicitly = true; + if (!alreadyImplemented) + toImplement.Add(new Tuple(method, needsExplicitly)); } - if (toImplement.Where (t => t.Item1 is IMethod).Any (t => CompareMethods (method, (IMethod)t.Item1))) - needsExplicitly = true; - if (!alreadyImplemented) - toImplement.Add(new Tuple(method, needsExplicitly)); - } - + // Stub out non-implemented properties defined by @iface foreach (var propGroup in interfaceType.GetProperties (p => !p.IsSynthetic && p.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).GroupBy (m => m.DeclaringType).Reverse ()) - foreach (var prop in propGroup) { - bool needsExplicitly = explicitly; - alreadyImplemented = false; - foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { - if (t.Kind == TypeKind.Interface) - continue; - foreach (IProperty cprop in t.Properties) { - if (cprop.Name == prop.Name) { - if (!needsExplicitly && !cprop.ReturnType.Equals(prop.ReturnType)) - needsExplicitly = true; - else - alreadyImplemented |= !needsExplicitly/* || cprop.InterfaceImplementations.Any (impl => impl.InterfaceType.Resolve (ctx).Equals (interfaceType))*/; + foreach (var prop in propGroup) { + bool needsExplicitly = explicitly; + alreadyImplemented = false; + foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { + if (t.Kind == TypeKind.Interface) + continue; + foreach (IProperty cprop in t.Properties) { + if (cprop.Name == prop.Name) { + if (!needsExplicitly && !cprop.ReturnType.Equals(prop.ReturnType)) + needsExplicitly = true; + else + alreadyImplemented |= !needsExplicitly/* || cprop.InterfaceImplementations.Any (impl => impl.InterfaceType.Resolve (ctx).Equals (interfaceType))*/; + } } } + if (!alreadyImplemented) + toImplement.Add(new Tuple(prop, needsExplicitly)); } - if (!alreadyImplemented) - toImplement.Add(new Tuple(prop, needsExplicitly)); - } return toImplement; } - - internal static bool CompareMethods (IMethod interfaceMethod, IMethod typeMethod) + + internal static bool CompareMethods(IMethod interfaceMethod, IMethod typeMethod) { if (typeMethod.IsExplicitInterfaceImplementation) - return typeMethod.ImplementedInterfaceMembers.Any (m => m.Equals (interfaceMethod)); - return SignatureComparer.Ordinal.Equals (interfaceMethod, typeMethod); + return typeMethod.ImplementedInterfaceMembers.Any(m => m.Equals(interfaceMethod)); + return SignatureComparer.Ordinal.Equals(interfaceMethod, typeMethod); } } } From 58c4ec8010cbac4246be4e2f8bed2f5fadbd264c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Thu, 19 Jul 2012 09:47:39 +0200 Subject: [PATCH 03/17] [Resolver] Fixed type inference use case. Daniel: The fix may not be correct - it's hard to tell which side effects that change will cause. --- .../Resolver/CSharpResolver.cs | 2 +- .../Resolver/LocalTypeInferenceTests.cs | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index 423ad11a96..3b707f000f 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1755,7 +1755,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (method.TypeParameters.Count != typeArguments.Count) continue; SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments)); - if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, false, out inferredTypes)) + if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) outputGroup.Add(sm); } else { if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs index 66ec024aea..b1145458d3 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs @@ -210,5 +210,35 @@ static class ExtMethods { var rr = Resolve(program); Assert.AreEqual("System.Int32", rr.Type.ReflectionName); } + + [Test] + public void InfertypeFromOverwrittenMethodArguments() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A + { + } + + public class B : A + { + } + + class Program + { + static void Main(string[] args) + { + IEnumerable list = new List(); + var arr = list.ToArray(); + $arr$.ToString(); + } + } +"; + var lrr = Resolve(program); + Assert.AreEqual("A[]", lrr.Type.FullName); + } + + } } From 0a7fdcd833ec2bc0f9334053c6c22167cbc31502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Thu, 19 Jul 2012 17:38:50 +0200 Subject: [PATCH 04/17] [CodeCompletion] No longer show static enum members on enum types (even if the call is theoretical valid). --- .../Completion/CSharpCompletionEngine.cs | 10 ---------- .../CSharp/CodeCompletion/CodeCompletionBugTests.cs | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 980b4349ca..1da2d02f67 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -2323,11 +2323,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion continue; result.AddMember(field); } - foreach (var m in type.GetMethods ()) { - if (m.IsStatic && m.IsPublic) { - result.AddMember(m); - } - } return result.Result; } @@ -2351,11 +2346,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion foreach (var field in trr.Type.GetFields ()) { result.AddMember(field); } - foreach (var m in trr.Type.GetMethods ()) { - if (m.Name == "TryParse" && m.IsStatic) { - result.AddMember(m); - } - } return result.Result; } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs index a7bd3f474e..2c07ee42f7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs @@ -4572,7 +4572,7 @@ class Test } } "); - Assert.AreEqual (4, provider.Count); // 2xTryParse + 2 fields + Assert.AreEqual (2, provider.Count); // 2 fields Assert.IsNotNull (provider.Find ("Value1"), "field 'Value1' not found."); Assert.IsNotNull (provider.Find ("Value2"), "field 'Value2' not found."); } From cc35fcc606a542c1c1ac4c03d00df22c1ddc9eac Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Jul 2012 00:35:43 +0200 Subject: [PATCH 05/17] Revert f01a4b2 and 58c4ec8. Fixed small issue in CSharpResolver.GetExtensionMethods: when type parameters are provided explicitly, use the specialized method for the eligibility check. --- .../Resolver/CSharpResolver.cs | 8 +- .../Resolver/MethodGroupResolveResult.cs | 19 +++++ .../CSharp/Resolver/ExtensionMethodTests.cs | 80 +++++++++++++++++++ .../Resolver/LocalTypeInferenceTests.cs | 30 ------- 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index 3b707f000f..cc0ba36f0d 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1618,7 +1618,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new NotSupportedException("Invalid value for NameLookupMode"); } if (result is UnknownMemberResolveResult) { - var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments, true); + // We intentionally use all extension methods here, not just the eligible ones. + // Proper eligibility checking is only possible for the full invocation + // (after we know the remaining arguments). + // The eligibility check in GetExtensionMethods is only intended for code completion. + var extensionMethods = GetExtensionMethods(identifier, typeArguments); if (extensionMethods.Count > 0) { return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { extensionMethods = extensionMethods @@ -1755,7 +1759,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (method.TypeParameters.Count != typeArguments.Count) continue; SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments)); - if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) + if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes)) outputGroup.Add(sm); } else { if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) { diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 037119dd21..11fd0e6519 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -132,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets all candidate extension methods. /// Note: this includes candidates that are not eligible due to an inapplicable /// this argument. + /// The candidates will only be specialized if the type arguments were provided explicitly. /// /// /// The results are stored in nested lists because they are grouped by using scope. @@ -155,6 +156,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return extensionMethods ?? Enumerable.Empty>(); } + /// + /// Gets the eligible extension methods. + /// + /// + /// Specifies whether to produce a + /// when type arguments could be inferred from . This parameter + /// is only used for inferred types and has no effect if the type parameters are + /// specified explicitly. + /// + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// public IEnumerable> GetEligibleExtensionMethods(bool substituteInferredTypes) { var result = new List>(); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs index 098deaa421..e81a2c5866 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs @@ -17,8 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Linq; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -118,5 +120,83 @@ public static class XC { Assert.AreEqual(1, inferredTypes.Length); Assert.AreEqual("System.String", inferredTypes[0].ReflectionName); } + + + [Test] + public void InferTypeFromOverwrittenMethodArguments() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + + public class B : A { } + + class Program + { + static void Main(string[] args) + { + IEnumerable list = new List(); + var arr = $list.ToArray()$; + } + } +"; + var rr = Resolve(program); + Assert.AreEqual("A[]", rr.Type.ReflectionName); + Assert.AreEqual("System.Linq.Enumerable.ToArray", rr.Member.FullName); + Assert.AreEqual("A", ((SpecializedMethod)rr.Member).TypeArguments.Single().ReflectionName); + } + + [Test] + public void TypeInferenceBasedOnTargetTypeAndArgumentType() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + public class B : A { } + + static class Program + { + static void Main(A a, B b) + { + var x = $b.Choose(a)$; + } + + public static T Choose(this T a, T b) { } + } +"; + var rr = Resolve(program); + Assert.AreEqual("A", rr.Type.ReflectionName); + } + + [Test] + public void PartiallySpecializedMethod() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + public class B : A { } + + static class Program + { + static void Main(A a, B b) + { + var x = $b.Choose$(a); + } + + public static T Choose(this T a, T b) { } + } +"; + var rr = Resolve(program); + Assert.IsFalse(rr.Methods.Any()); + // We deliberately do not specialize the method unless partial specialization is requested explicitly. + // This is because the actual type (when considering the whole invocation, not just the method group) + // is actually A. + Assert.AreEqual("``0", rr.GetExtensionMethods().Single().Single().ReturnType.ReflectionName); + Assert.AreEqual("``0", rr.GetEligibleExtensionMethods(false).Single().Single().ReturnType.ReflectionName); + Assert.AreEqual("B", rr.GetEligibleExtensionMethods(true).Single().Single().ReturnType.ReflectionName); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs index b1145458d3..66ec024aea 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs @@ -210,35 +210,5 @@ static class ExtMethods { var rr = Resolve(program); Assert.AreEqual("System.Int32", rr.Type.ReflectionName); } - - [Test] - public void InfertypeFromOverwrittenMethodArguments() - { - string program = @"using System.Collections.Generic; - using System.Linq; - - public class A - { - } - - public class B : A - { - } - - class Program - { - static void Main(string[] args) - { - IEnumerable list = new List(); - var arr = list.ToArray(); - $arr$.ToString(); - } - } -"; - var lrr = Resolve(program); - Assert.AreEqual("A[]", lrr.Type.FullName); - } - - } } From 5592e889abe2a543b4dc909ccf4f20404df3bbe1 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Jul 2012 01:32:01 +0200 Subject: [PATCH 06/17] Fixed bug when passing an extension method as a method group to a generic function. --- .../Resolver/TypeInference.cs | 1 - .../CSharp/Resolver/ExtensionMethodTests.cs | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs b/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs index d176e9b7c6..e082f98d8e 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs @@ -510,7 +510,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } var or = mgrr.PerformOverloadResolution(compilation, args, - allowExtensionMethods: false, allowExpandingParams: false); if (or.FoundApplicableCandidate && or.BestCandidateAmbiguousWith == null) { IType returnType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs index e81a2c5866..cb759789c7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs @@ -198,5 +198,42 @@ public static class XC { Assert.AreEqual("``0", rr.GetEligibleExtensionMethods(false).Single().Single().ReturnType.ReflectionName); Assert.AreEqual("B", rr.GetEligibleExtensionMethods(true).Single().Single().ReturnType.ReflectionName); } + + [Test] + public void CreateDelegateFromExtensionMethod() + { + string program = @"using System; + static class Program + { + static void Main() { + Func f = $"""".id$; + } + static string id(this string x) { return x; } + } +"; + Conversion c = GetConversion(program); + Assert.IsTrue(c.IsValid); + Assert.IsTrue(c.IsMethodGroupConversion); + Assert.AreEqual("Program.id", c.Method.FullName); + } + + [Test] + public void InferDelegateTypeFromExtensionMethod() + { + string program = @"using System; + static class Program + { + static void Main() { + $call("""".id)$; + } + + static string id(this string x) { return x; } + static T call(Func f) { } + } +"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + Assert.AreEqual("System.String", rr.Type.FullName); + } } } From 58d7ac0ee6eb6b6959a410ee78d8ea2f973bd4be Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Jul 2012 01:32:20 +0200 Subject: [PATCH 07/17] Improved ToString() for methods. --- .../Implementation/DefaultResolvedMethod.cs | 25 +++++++++++++++++++ .../Implementation/SpecializedMethod.cs | 3 +++ 2 files changed, 28 insertions(+) diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs index 6a20f62c21..bb8744fc5c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs @@ -19,6 +19,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; + using ICSharpCode.NRefactory.Semantics; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -217,6 +219,29 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public override string ToString() + { + StringBuilder b = new StringBuilder("["); + b.Append(this.EntityType); + b.Append(' '); + b.Append(this.DeclaringType.ReflectionName); + b.Append('.'); + b.Append(this.Name); + if (this.TypeParameters.Count > 0) { + b.Append("``"); + b.Append(this.TypeParameters.Count); + } + b.Append('('); + for (int i = 0; i < this.Parameters.Count; i++) { + if (i > 0) b.Append(", "); + b.Append(this.Parameters[i].ToString()); + } + b.Append("):"); + b.Append(this.ReturnType.ReflectionName); + b.Append(']'); + return b.ToString(); + } + /// /// Gets a dummy constructor for the specified compilation. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs index 79b5d10fa7..a84201047d 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs @@ -195,6 +195,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append(this.TypeArguments[i].ReflectionName); } b.Append(']'); + } else if (this.TypeParameters.Count > 0) { + b.Append("``"); + b.Append(this.TypeParameters.Count); } b.Append('('); for (int i = 0; i < this.Parameters.Count; i++) { From 1146033e6400c06d22c139c82da2ec92ccc2fcac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 13:19:22 +0200 Subject: [PATCH 08/17] Fixed get extension method call. --- .../Completion/CSharpCompletionEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 1da2d02f67..02b70bafc7 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -767,7 +767,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } idx++; - foreach (var list in mgr.GetExtensionMethods ()) { + foreach (var list in mgr.GetEligibleExtensionMethods (true)) { foreach (var method in list) { if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { AutoSelect = false; From c0323828554f67d851ad1bbf1c23cea1f429ab98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 13:26:40 +0200 Subject: [PATCH 09/17] [TypeSystem] Added location property to IAssembly. --- .../CSharpProjectContent.cs | 15 ++++++++++++++- ICSharpCode.NRefactory/TypeSystem/IAssembly.cs | 8 +++++++- .../Implementation/DefaultUnresolvedAssembly.cs | 15 ++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs index 44c6e22969..a890c1ea41 100644 --- a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs +++ b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs @@ -31,6 +31,7 @@ namespace ICSharpCode.NRefactory.CSharp public class CSharpProjectContent : IProjectContent { string assemblyName; + string location; Dictionary parsedFiles; List assemblyReferences; CompilerSettings compilerSettings; @@ -47,6 +48,7 @@ namespace ICSharpCode.NRefactory.CSharp protected CSharpProjectContent(CSharpProjectContent pc) { this.assemblyName = pc.assemblyName; + this.location = pc.location; this.parsedFiles = new Dictionary(pc.parsedFiles, Platform.FileNameComparer); this.assemblyReferences = new List(pc.assemblyReferences); this.compilerSettings = pc.compilerSettings; @@ -63,7 +65,11 @@ namespace ICSharpCode.NRefactory.CSharp public string AssemblyName { get { return assemblyName; } } - + + public string Location { + get { return location; } + } + public CompilerSettings CompilerSettings { get { return compilerSettings; } } @@ -123,6 +129,13 @@ namespace ICSharpCode.NRefactory.CSharp pc.assemblyName = newAssemblyName; return pc; } + + public IProjectContent SetLocation(string location) + { + CSharpProjectContent pc = Clone(); + pc.location = location; + return pc; + } public IProjectContent SetCompilerSettings(object compilerSettings) { diff --git a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs index 9d5d5c2dfd..1c98a0498e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs @@ -30,7 +30,13 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets the assembly name (short name). /// string AssemblyName { get; } - + + /// + /// Gets the path to the assembly location. + /// For projects it is the same as the output path. + /// + string Location { get; } + /// /// Gets the list of all assembly attributes in the project. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index e53cb1422e..81866619ec 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -69,7 +69,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation assemblyName = value; } } - + + string location; + public string Location { + get { + return location; + } + set { + if (value == null) + throw new ArgumentNullException("value"); + FreezableHelper.ThrowIfFrozen(this); + location = value; + } + } + public IList AssemblyAttributes { get { return assemblyAttributes; } } From be9a3eda3fc2c7b7360c1d811c6a617301ea34ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 13:27:45 +0200 Subject: [PATCH 10/17] [Completion] Fixed get extension method call in parameter completion. --- .../Completion/CSharpParameterCompletionEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs index 229016533c..6dc68605e6 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -121,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion yield return method; } - foreach (var extMethods in resolveResult.GetExtensionMethods ()) { + foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var method in extMethods) { yield return method; } From 14addf402d194665419dab122af4be37f541b118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 13:35:16 +0200 Subject: [PATCH 11/17] [TypeSystem] Added SetLocation to IProjectContent. --- ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs b/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs index 7acfb81de2..4a731c42ce 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs @@ -69,7 +69,12 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Changes the assembly name of this project content. /// IProjectContent SetAssemblyName(string newAssemblyName); - + + /// + /// Changes the location of this project content. + /// + IProjectContent SetLocation(string newLocation); + /// /// Add assembly references to this project content. /// From 5f60958a7ea10d281f098ae5688c08ed8ec0fc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 13:42:02 +0200 Subject: [PATCH 12/17] [TypeSystem] Cecil loader can now take the loaded assembly location. --- ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 52f4800b91..d39cfdd9a9 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -105,7 +105,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// IProjectContent that represents the assembly [CLSCompliant(false)] - public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition) + public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition, string location = null) { if (assemblyDefinition == null) throw new ArgumentNullException("assemblyDefinition"); @@ -166,6 +166,7 @@ namespace ICSharpCode.NRefactory.TypeSystem typeSystemTranslationTable[this.currentAssembly] = assemblyDefinition; var result = this.currentAssembly; + result.Location = location; this.currentAssembly = null; this.currentModule = null; return result; From 8090455130cb2331fd15a7597f004c606962105f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 15:48:45 +0200 Subject: [PATCH 13/17] [Parser] Added conditional symbols to the compilation unit. --- .../Ast/CompilationUnit.cs | 21 +++++++++++++++++-- .../Parser/CSharpParser.cs | 7 +++++-- .../Parser/mcs/cs-tokenizer.cs | 7 +++++++ .../Parser/mcs/driver.cs | 1 + .../Parser/mcs/namespace.cs | 6 ++++++ 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs index 2897d7fab2..b141ba0626 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs @@ -61,13 +61,30 @@ namespace ICSharpCode.NRefactory.CSharp public AstNodeCollection Members { get { return GetChildrenByRole(MemberRole); } } - + + string[] conditionals = null; + List errors = new List (); public List Errors { get { return errors; } } - + + + /// + /// Gets the conditional symbols used to parse the source file. Note that this list contains + /// the conditional symbols at the start of the first token in the file - including the ones defined + /// in the source file. + /// + public string[] Conditionals { + get { + return conditionals ?? new string[0]; + } + internal set { + conditionals = value; + } + } + /// /// Gets the expression that was on top of the parse stack. /// This is the only way to get an expression that isn't part of a statment. diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index de60fd29e6..9c72e7cf4b 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -3669,7 +3669,9 @@ namespace ICSharpCode.NRefactory.CSharp if (top.LastYYValue is Mono.CSharp.Expression) { conversionVisitor.Unit.TopExpression = ((Mono.CSharp.Expression)top.LastYYValue).Accept(conversionVisitor) as AstNode; } + conversionVisitor.Unit.FileName = fileName; + conversionVisitor.Unit.Conditionals = top.Conditionals.ToArray (); return conversionVisitor.Unit; } @@ -3710,11 +3712,12 @@ namespace ICSharpCode.NRefactory.CSharp Location.Initialize (new List (new [] { file })); var module = new ModuleContainer (ctx); var parser = Driver.Parse (reader, file, module, lineModifier); - + var top = new CompilerCompilationUnit () { ModuleCompiled = module, LocationsBag = parser.LocationsBag, - SpecialsBag = parser.Lexer.sbag + SpecialsBag = parser.Lexer.sbag, + Conditionals = parser.Lexer.SourceFile.Conditionals }; var unit = Parse (top, fileName, lineModifier); unit.Errors.AddRange (errorReportPrinter.Errors); diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs index 6ca0a80b90..600569edc5 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs @@ -174,6 +174,13 @@ namespace Mono.CSharp readonly SeekableStreamReader reader; readonly CompilationSourceFile source_file; + + public CompilationSourceFile SourceFile { + get { + return source_file; + } + } + readonly CompilerContext context; SourceFile current_source; diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs index e86263da6c..0baa4833bc 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs @@ -369,6 +369,7 @@ namespace Mono.CSharp public ModuleContainer ModuleCompiled { get; set; } public LocationsBag LocationsBag { get; set; } public SpecialsBag SpecialsBag { get; set; } + public IEnumerable Conditionals { get; set; } public object LastYYValue { get; set; } } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs index a33a3a14ae..5b16501490 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs @@ -591,6 +591,12 @@ namespace Mono.CSharp { } } + public IEnumerable Conditionals { + get { + return conditionals.Where (kv => kv.Value).Select (kv => kv.Key); + } + } + public string FileName { get { return file.Name; From ed0e4c51972c597c993f8ee5ad0e54226540ce14 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Jul 2012 18:06:55 +0200 Subject: [PATCH 14/17] Fixed conversions from "dynamic" - these only exist from expressions, not from the type (this was causing subtle issues with type inference). --- .../Resolver/CSharpConversions.cs | 4 ++-- .../Resolver/MethodGroupResolveResult.cs | 4 ++-- .../CSharp/Resolver/ConversionsTest.cs | 22 +++++++++++++---- .../CSharp/Resolver/TypeInferenceTests.cs | 24 +++++++++++++++++++ .../Semantics/MemberResolveResult.cs | 2 +- 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index 359338129f..a22011fb7f 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -112,6 +112,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } c = ImplicitConversion(resolveResult.Type, toType); if (c.IsValid) return c; + if (resolveResult.Type.Kind == TypeKind.Dynamic) + return Conversion.ImplicitDynamicConversion; c = AnonymousFunctionConversion(resolveResult, toType); if (c.IsValid) return c; c = MethodGroupConversion(resolveResult, toType); @@ -159,8 +161,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return Conversion.ImplicitReferenceConversion; if (IsBoxingConversion(fromType, toType)) return Conversion.BoxingConversion; - if (fromType.Kind == TypeKind.Dynamic) - return Conversion.ImplicitDynamicConversion; if (ImplicitTypeParameterConversion(fromType, toType)) { // Implicit type parameter conversions that aren't also // reference conversions are considered to be boxing conversions diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 11fd0e6519..57c748f3c8 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -161,8 +161,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// /// /// Specifies whether to produce a - /// when type arguments could be inferred from . This parameter - /// is only used for inferred types and has no effect if the type parameters are + /// when type arguments could be inferred from . + /// This setting is only used for inferred types and has no effect if the type parameters are /// specified explicitly. /// /// diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index e5275bca18..e422edd811 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -73,8 +73,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void DynamicIdentityConversions() { - Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(object), typeof(ReflectionHelper.Dynamic))); - Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(ReflectionHelper.Dynamic), typeof(object))); + Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(object), typeof(dynamic))); + Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(dynamic), typeof(object))); } [Test] @@ -155,12 +155,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } [Test] - public void SimpleDynamicConversions() + public void ConversionToDynamic() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string), typeof(dynamic))); - Assert.AreEqual(C.ImplicitDynamicConversion, ImplicitConversion(typeof(dynamic), typeof(string))); Assert.AreEqual(C.BoxingConversion, ImplicitConversion(typeof(int), typeof(dynamic))); - Assert.AreEqual(C.ImplicitDynamicConversion, ImplicitConversion(typeof(dynamic), typeof(int))); + } + + [Test] + public void ConversionFromDynamic() + { + // There is no conversion from the type 'dynamic' to other types (except object). + // Such conversions only exists from dynamic expression. + // This is an important distinction for type inference (see TypeInferenceTests.IEnumerableCovarianceWithDynamic) + Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(string))); + Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(int))); + + var dynamicRR = new ResolveResult(SpecialType.Dynamic); + Assert.AreEqual(C.ImplicitDynamicConversion, conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(string)))); + Assert.AreEqual(C.ImplicitDynamicConversion, conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(int)))); } [Test] diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs index 287cfc9bd5..4addf8548e 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs @@ -148,6 +148,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver out success)); Assert.IsTrue(success); } + + [Test] + public void IEnumerableCovarianceWithDynamic() + { + ITypeParameter tp = new DefaultTypeParameter(compilation, EntityType.Method, 0, "T"); + var ienumerableOfT = new ParameterizedType(compilation.FindType(typeof(IEnumerable<>)).GetDefinition(), new[] { tp }); + var ienumerableOfString = compilation.FindType(typeof(IEnumerable)); + var ienumerableOfDynamic = compilation.FindType(typeof(IEnumerable)); + + // static T M(IEnumerable x, IEnumerable y) {} + // M(IEnumerable, IEnumerable); -> should infer T=dynamic, no ambiguity + // See http://blogs.msdn.com/b/cburrows/archive/2010/04/01/errata-dynamic-conversions-and-overload-resolution.aspx + // for details. + + bool success; + Assert.AreEqual( + new [] { SpecialType.Dynamic }, + ti.InferTypeArguments( + new [] { tp }, + new [] { new ResolveResult(ienumerableOfDynamic), new ResolveResult(ienumerableOfString) }, + new [] { ienumerableOfT, ienumerableOfT }, + out success)); + Assert.IsTrue(success); + } #endregion #region Inference with Method Groups diff --git a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs index f861920b45..3a2430856f 100644 --- a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.NRefactory.Semantics /// /// Represents the result of a member invocation. /// Used for field/property/event access. - /// Also, derives from MemberResolveResult. + /// Also, derives from MemberResolveResult. /// public class MemberResolveResult : ResolveResult { From 5a1f7d70f0c4ce68845ce6741dcd32394061969e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Jul 2012 18:36:51 +0200 Subject: [PATCH 15/17] Fixed issues with CecilLoader.LazyLoad=true. --- .../TypeSystem/CecilLoader.cs | 92 +++++++++++++------ 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index d39cfdd9a9..c6626d135b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -98,6 +98,21 @@ namespace ICSharpCode.NRefactory.TypeSystem // Enable interning by default. this.InterningProvider = new SimpleInterningProvider(); } + + /// + /// Creates a nested CecilLoader for lazy-loading. + /// + private CecilLoader(CecilLoader loader) + { + // use a shared typeSystemTranslationTable + this.typeSystemTranslationTable = loader.typeSystemTranslationTable; + this.IncludeInternalMembers = loader.IncludeInternalMembers; + this.LazyLoad = loader.LazyLoad; + this.currentModule = loader.currentModule; + this.currentAssembly = loader.currentAssembly; + // don't use interning - the interning provider is most likely not thread-safe + // don't use cancellation for delay-loaded members + } #region Load From AssemblyDefinition /// @@ -133,13 +148,15 @@ namespace ICSharpCode.NRefactory.TypeSystem int typeParameterCount; string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); var typeRef = new GetClassTypeReference(GetAssemblyReference(type.Scope), type.Namespace, name, typeParameterCount); - typeRef = this.InterningProvider.Intern(typeRef); + if (this.InterningProvider != null) + typeRef = this.InterningProvider.Intern(typeRef); var key = new FullNameAndTypeParameterCount(type.Namespace, name, typeParameterCount); currentAssembly.AddTypeForwarder(key, typeRef); } } // Create and register all types: + CecilLoader cecilLoaderCloneForLazyLoading = LazyLoad ? new CecilLoader(this) : null; List cecilTypeDefs = new List(); List typeDefs = new List(); foreach (ModuleDefinition module in assemblyDefinition.Modules) { @@ -150,10 +167,14 @@ namespace ICSharpCode.NRefactory.TypeSystem if (name.Length == 0) continue; - var t = CreateTopLevelTypeDefinition(td); - cecilTypeDefs.Add(td); - typeDefs.Add(t); - currentAssembly.AddTypeDefinition(t); + if (this.LazyLoad) { + currentAssembly.AddTypeDefinition(new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td)); + } else { + var t = CreateTopLevelTypeDefinition(td); + cecilTypeDefs.Add(td); + typeDefs.Add(t); + currentAssembly.AddTypeDefinition(t); + } } } } @@ -162,8 +183,7 @@ namespace ICSharpCode.NRefactory.TypeSystem InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]); } - if (HasCecilReferences) - typeSystemTranslationTable[this.currentAssembly] = assemblyDefinition; + RegisterCecilObject(this.currentAssembly, assemblyDefinition); var result = this.currentAssembly; result.Location = location; @@ -230,8 +250,7 @@ namespace ICSharpCode.NRefactory.TypeSystem var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }; AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param); var result = LoadAssembly(asm); - if (HasCecilReferences) - typeSystemTranslationTable[result] = asm; + RegisterCecilObject(result, asm); return result; } @@ -1178,14 +1197,14 @@ namespace ICSharpCode.NRefactory.TypeSystem EntityType memberType; var b = ReadByte(); switch (b) { - case 0x53: - memberType = EntityType.Field; - break; - case 0x54: - memberType = EntityType.Property; - break; - default: - throw new NotSupportedException(string.Format("Custom member type 0x{0:x} is not supported.", b)); + case 0x53: + memberType = EntityType.Field; + break; + case 0x54: + memberType = EntityType.Property; + break; + default: + throw new NotSupportedException(string.Format("Custom member type 0x{0:x} is not supported.", b)); } IType type = ReadCustomAttributeFieldOrPropType(); string name = ReadSerString(); @@ -1501,12 +1520,11 @@ namespace ICSharpCode.NRefactory.TypeSystem 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(); + RegisterCecilObject(td, typeDefinition); } void InitBaseTypes(TypeDefinition typeDefinition, IList baseTypes) @@ -1709,12 +1727,11 @@ namespace ICSharpCode.NRefactory.TypeSystem 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(); + loader.RegisterCecilObject(this, typeDefinition); } public override string Namespace { @@ -1722,6 +1739,15 @@ namespace ICSharpCode.NRefactory.TypeSystem set { throw new NotSupportedException(); } } + public override string FullName { + // This works because LazyCecilTypeDefinition is only used for top-level types + get { return cecilTypeDef.FullName; } + } + + public override string ReflectionName { + get { return cecilTypeDef.FullName; } + } + public TypeKind Kind { get { return kind; } } @@ -2125,14 +2151,24 @@ namespace ICSharpCode.NRefactory.TypeSystem void FinishReadMember(AbstractUnresolvedMember member, object cecilDefinition) { - member.ApplyInterningProvider(this.InterningProvider); + if (this.InterningProvider != null) + member.ApplyInterningProvider(this.InterningProvider); member.Freeze(); - if (HasCecilReferences) - typeSystemTranslationTable[member] = cecilDefinition; + RegisterCecilObject(member, cecilDefinition); } #region Type system translation table - Dictionary typeSystemTranslationTable; + readonly Dictionary typeSystemTranslationTable; + + void RegisterCecilObject(object typeSystemObject, object cecilObject) + { + if (typeSystemTranslationTable != null) { + // When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently + lock (typeSystemTranslationTable) { + typeSystemTranslationTable[typeSystemObject] = cecilObject; + } + } + } T InternalGetCecilObject (object typeSystemObject) where T : class { @@ -2141,8 +2177,10 @@ namespace ICSharpCode.NRefactory.TypeSystem if (!HasCecilReferences) throw new NotSupportedException ("This instance contains no cecil references."); object result; - if (!typeSystemTranslationTable.TryGetValue (typeSystemObject, out result)) - return null; + lock (typeSystemTranslationTable) { + if (!typeSystemTranslationTable.TryGetValue (typeSystemObject, out result)) + return null; + } return result as T; } From d1f08074580b2adb777efffade54bf7dab467b57 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Jul 2012 22:49:52 +0200 Subject: [PATCH 16/17] Use location from Cecil instead of providing it as an extra parameter to LoadAssembly(). --- ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs | 4 ++-- .../TypeSystem/Implementation/DefaultUnresolvedAssembly.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index c6626d135b..4d4fd9c5f2 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -120,7 +120,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// IProjectContent that represents the assembly [CLSCompliant(false)] - public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition, string location = null) + public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition) { if (assemblyDefinition == null) throw new ArgumentNullException("assemblyDefinition"); @@ -139,6 +139,7 @@ namespace ICSharpCode.NRefactory.TypeSystem } this.currentAssembly = new CecilUnresolvedAssembly(assemblyDefinition.Name.Name, this.DocumentationProvider); + currentAssembly.Location = assemblyDefinition.MainModule.FullyQualifiedName; currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes); currentAssembly.ModuleAttributes.AddRange(assemblyAttributes); @@ -186,7 +187,6 @@ namespace ICSharpCode.NRefactory.TypeSystem RegisterCecilObject(this.currentAssembly, assemblyDefinition); var result = this.currentAssembly; - result.Location = location; this.currentAssembly = null; this.currentModule = null; return result; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 81866619ec..b3aba10d88 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -76,8 +76,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return location; } set { - if (value == null) - throw new ArgumentNullException("value"); FreezableHelper.ThrowIfFrozen(this); location = value; } From e71a8fd8b2ca57720e848eb104812a92c8b71a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 20 Jul 2012 22:58:04 +0200 Subject: [PATCH 17/17] Fixed conditional symbol bug. --- ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs index 5b16501490..315aed9808 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs @@ -593,6 +593,8 @@ namespace Mono.CSharp { public IEnumerable Conditionals { get { + if (conditionals == null) + return Enumerable.Empty (); return conditionals.Where (kv => kv.Value).Select (kv => kv.Key); } }