diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs index d1432e9198..76bafc318f 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs @@ -418,16 +418,13 @@ namespace Grunwald.BooBinding.CodeCompletion if (callingClass != null) isClassInInheritanceTree = callingClass.IsTypeInInheritanceTree(trr.ResolvedClass); - foreach (IMethod m in trr.ResolvedClass.Methods) { + foreach (IMethod m in trr.ResolvedClass.DefaultReturnType.GetMethods()) { if (m.IsConstructor && !m.IsStatic && m.IsAccessible(callingClass, isClassInInheritanceTree)) { methods.Add(m); } } - if (methods.Count == 0) { - methods.Add(ICSharpCode.SharpDevelop.Dom.Constructor.CreateDefault(trr.ResolvedClass)); - } ResolveInvocation(methods, node.Arguments); if (resolveResult != null) resolveResult.ResolvedType = trr.ResolvedType; diff --git a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs index c5b76b5b81..09b3e7fc88 100644 --- a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs +++ b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands member.DeclaringType.ProjectContent.Language.CodeGenerator != null && !FindReferencesAndRenameHelper.IsReadOnly(member.DeclaringType); - if (method == null || !method.IsConstructor) { + if (method == null || !method.IsConstructor && !method.GetIsOperator()) { if (!FindReferencesAndRenameHelper.IsReadOnly(member.DeclaringType) && !(member is IProperty && ((IProperty)member).IsIndexer)) { cmd = new MenuCommand("${res:SharpDevelop.Refactoring.RenameCommand}", Rename); diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/InsightWindow/MethodInsightDataProvider.cs b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/InsightWindow/MethodInsightDataProvider.cs index 45798339a5..16f43d64ec 100644 --- a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/InsightWindow/MethodInsightDataProvider.cs +++ b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/InsightWindow/MethodInsightDataProvider.cs @@ -144,11 +144,6 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor if (trr != null || expressionResult.Context == ExpressionContext.BaseConstructorCall) { if (results.ResolvedType != null) { methods.AddRange(GetConstructorMethods(results.ResolvedType.GetMethods())); - IClass resolvedClass = (trr != null) ? trr.ResolvedClass : results.ResolvedType.GetUnderlyingClass(); - if (methods.Count == 0 && resolvedClass != null && !resolvedClass.IsStatic) { - // add default constructor - methods.Add(Constructor.CreateDefault(resolvedClass)); - } } } } else { diff --git a/src/Main/Base/Test/GenerateOverrideMethodTests.cs b/src/Main/Base/Test/GenerateOverrideMethodTests.cs index b6a990dff8..ef8e594cdb 100644 --- a/src/Main/Base/Test/GenerateOverrideMethodTests.cs +++ b/src/Main/Base/Test/GenerateOverrideMethodTests.cs @@ -6,6 +6,7 @@ // using System; +using System.Linq; using ICSharpCode.NRefactory.Ast; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom.Refactoring; @@ -28,10 +29,10 @@ namespace ICSharpCode.SharpDevelop.Tests Assert.AreEqual(2, cu.Classes.Count); Assert.AreEqual(1, cu.Classes[0].Methods.Count + cu.Classes[0].Properties.Count); IMember virtualMember; - if (cu.Classes[0].Methods.Count > 0) - virtualMember = cu.Classes[0].Methods[0]; - else //if (cu.Classes[0].Properties.Count > 0) + if (cu.Classes[0].Properties.Count > 0) virtualMember = cu.Classes[0].Properties[0]; + else + virtualMember = cu.Classes[0].Methods[0]; CSharpCodeGenerator ccg = new CSharpCodeGenerator(); AttributedNode result = ccg.GetOverridingMethod(virtualMember, new ClassFinder(cu.Classes[1], 3, 1)); Assert.IsNotNull(result); diff --git a/src/Main/Base/Test/NRefactoryResolverTests.cs b/src/Main/Base/Test/NRefactoryResolverTests.cs index 75dd5a0c7e..14b49bfbb5 100644 --- a/src/Main/Base/Test/NRefactoryResolverTests.cs +++ b/src/Main/Base/Test/NRefactoryResolverTests.cs @@ -578,11 +578,50 @@ class A { IMethod m = (IMethod)result.ResolvedMember; Assert.IsNotNull(m); Assert.AreEqual("A", result.ResolvedType.FullyQualifiedName); + Assert.AreEqual(0, m.Parameters.Count); ArrayList ar = result.GetCompletionData(result.CallingClass.ProjectContent); Assert.IsTrue(ContainsMember(ar, "A.Method")); } + [Test] + public void DefaultStructCTorOverloadLookupTest() + { + string program = @"struct A { + void Method() { + + } + + public A(int x) {} +} +"; + MemberResolveResult result = Resolve(program, "new A()", 3); + IMethod m = (IMethod)result.ResolvedMember; + Assert.IsNotNull(m); + Assert.AreEqual("A", result.ResolvedType.FullyQualifiedName); + Assert.AreEqual(0, m.Parameters.Count); + + ArrayList ar = result.GetCompletionData(result.CallingClass.ProjectContent); + Assert.IsTrue(ContainsMember(ar, "A.Method")); + } + + [Test] + public void ReflectionStructCTorOverloadLookupTest() + { + string program = @"using System; +class A { + void Method() { + + } +} +"; + MemberResolveResult result = Resolve(program, "new DateTime()", 4); + IMethod m = (IMethod)result.ResolvedMember; + Assert.IsNotNull(m); + Assert.AreEqual("System.DateTime", result.ResolvedType.FullyQualifiedName); + Assert.AreEqual(0, m.Parameters.Count); + } + [Test] public void ValueInsideSetterTest() { diff --git a/src/Main/Base/Test/ReflectionLayerTests.cs b/src/Main/Base/Test/ReflectionLayerTests.cs index 78b0322350..d3f0609b53 100644 --- a/src/Main/Base/Test/ReflectionLayerTests.cs +++ b/src/Main/Base/Test/ReflectionLayerTests.cs @@ -171,6 +171,22 @@ namespace ICSharpCode.SharpDevelop.Tests Assert.AreEqual("System.Void", prt.BaseType.FullyQualifiedName); } + [Test] + public void DateTimeDefaultConstructor() + { + IClass c = mscorlib.GetClass("System.DateTime", 0); + Assert.IsFalse(c.Methods.Any(p => p.IsConstructor && p.Parameters.Count == 0)); + Assert.IsTrue(c.GetAddDefaultConstructorIfRequired()); + } + + [Test] + public void NoEncodingInfoDefaultConstructor() + { + IClass c = mscorlib.GetClass("System.Text.EncodingInfo", 0); + Assert.IsFalse(c.Methods.Any(p => p.IsConstructor)); // EncodingInfo only has an internal constructor + Assert.IsFalse(c.GetAddDefaultConstructorIfRequired()); + } + [Test] public void ParameterComparisonTest() { diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs index 41d2e73566..01e54df0e3 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs @@ -324,6 +324,7 @@ namespace ICSharpCode.SharpDevelop.Dom foreach (MethodDefinition method in type.Constructors) { AddMethod(method); } + this.AddDefaultConstructorIfRequired = (this.ClassType == ClassType.Struct || this.ClassType == ClassType.Enum); foreach (MethodDefinition method in type.Methods) { if (!method.IsSpecialName) { AddMethod(method); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs index bb9db4fab9..5e44a5bfdb 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs @@ -107,6 +107,8 @@ namespace ICSharpCode.SharpDevelop.Dom this.Properties.AddRange(part.Properties); this.Events.AddRange(part.Events); this.Fields.AddRange(part.Fields); + + this.AddDefaultConstructorIfRequired |= part.GetAddDefaultConstructorIfRequired(); } this.CompilationUnit.FileName = shortestFileName; if ((modifier & ModifierEnum.VisibilityMask) == ModifierEnum.None) { diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs index 9ef5cd6ee5..770147537b 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs @@ -12,7 +12,7 @@ using System.Threading; namespace ICSharpCode.SharpDevelop.Dom { - public class DefaultClass : AbstractEntity, IClass, IComparable + public class DefaultClass : AbstractEntity, IClass2, IComparable { ClassType classType; DomRegion region; @@ -64,13 +64,16 @@ namespace ICSharpCode.SharpDevelop.Dom } */ - byte flags; + byte flags = addDefaultConstructorIfRequiredFlag; + const byte calculatedFlagsReady = 0x01; const byte hasPublicOrInternalStaticMembersFlag = 0x02; const byte hasExtensionMethodsFlag = 0x04; - internal byte Flags { + const byte addDefaultConstructorIfRequiredFlag = 0x08; + + internal byte CalculatedFlags { get { - if (flags == 0) { - flags = 1; + if ((flags & calculatedFlagsReady) == 0) { + flags |= calculatedFlagsReady; foreach (IMember m in this.Fields) { if (m.IsStatic && (m.IsPublic || m.IsInternal)) { flags |= hasPublicOrInternalStaticMembersFlag; @@ -112,12 +115,23 @@ namespace ICSharpCode.SharpDevelop.Dom } public bool HasPublicOrInternalStaticMembers { get { - return (Flags & hasPublicOrInternalStaticMembersFlag) == hasPublicOrInternalStaticMembersFlag; + return (CalculatedFlags & hasPublicOrInternalStaticMembersFlag) == hasPublicOrInternalStaticMembersFlag; } } public bool HasExtensionMethods { get { - return (Flags & hasExtensionMethodsFlag) == hasExtensionMethodsFlag; + return (CalculatedFlags & hasExtensionMethodsFlag) == hasExtensionMethodsFlag; + } + } + public bool AddDefaultConstructorIfRequired { + get { + return (flags & addDefaultConstructorIfRequiredFlag) == addDefaultConstructorIfRequiredFlag; + } + set { + if (value) + flags |= addDefaultConstructorIfRequiredFlag; + else + flags &= unchecked((byte)~addDefaultConstructorIfRequiredFlag); } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs index a1385815e9..e966822fa6 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace ICSharpCode.SharpDevelop.Dom @@ -36,7 +37,13 @@ namespace ICSharpCode.SharpDevelop.Dom if (c == null) throw new ArgumentNullException("c"); - Constructor con = new Constructor(ModifierEnum.Public, c.Region, c.Region, c); + ModifierEnum modifiers = ModifierEnum.Synthetic; + if (c.IsAbstract) + modifiers |= ModifierEnum.Protected; + else + modifiers |= ModifierEnum.Public; + DomRegion region = new DomRegion(c.Region.BeginLine, c.Region.BeginColumn, c.Region.BeginLine, c.Region.BeginColumn); + Constructor con = new Constructor(modifiers, region, region, c); con.Documentation = "Default constructor of " + c.Name; return con; } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs index e9c8442a79..ce8709545a 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultReturnType.cs @@ -81,6 +81,16 @@ namespace ICSharpCode.SharpDevelop.Dom using (var busyLock = busyManager.Enter(this)) { if (busyLock.Success) { l.AddRange(c.Methods); + if (c.GetAddDefaultConstructorIfRequired() && !c.IsStatic) { + // A constructor is added for classes that do not have a default constructor; + // and for all structs. + if (c.ClassType == ClassType.Class && !l.Exists(m => m.IsConstructor)) { + l.Add(Constructor.CreateDefault(c)); + } else if (c.ClassType == ClassType.Struct || c.ClassType == ClassType.Enum) { + l.Add(Constructor.CreateDefault(c)); + } + } + if (c.ClassType == ClassType.Interface) { if (c.BaseTypes.Count == 0) { AddMethodsFromBaseType(l, c.ProjectContent.SystemTypes.Object); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs index 7430d2c76b..0d6a762508 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs @@ -151,4 +151,30 @@ namespace ICSharpCode.SharpDevelop.Dom ///// //IClass Unfreeze(); } + + public interface IClass2 : IClass + { + /// + /// Gets whether a default constructor should be added to this class if it is required. + /// Such automatic default constructors will not appear in IClass.Methods, but will be present + /// in IClass.DefaultReturnType.GetMethods(). + /// + /// This way of creating the default constructor is necessary because + /// we cannot create it directly in the IClass - we need to consider partial classes. + bool AddDefaultConstructorIfRequired { + get; + } + } + + public static class Class2Compatibility + { + public static bool GetAddDefaultConstructorIfRequired(this IClass c) + { + IClass2 c2 = c as IClass2; + if (c2 != null) + return c2.AddDefaultConstructorIfRequired; + else + return false; + } + } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs index f85efb3821..a51d1eef59 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs @@ -444,11 +444,6 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver bool resultIsAcceptable; IMethod result = MemberLookupHelper.FindOverload(methods, argumentTypes, out resultIsAcceptable); - if (result == null) { - IClass c = rt.GetUnderlyingClass(); - if (c != null) - result = Constructor.CreateDefault(c); - } ResolveResult rr = CreateMemberResolveResult(result); if (rr != null) rr.ResolvedType = rt; diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs index 5ce179a497..3dcdaad875 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/DomPersistence.cs @@ -20,7 +20,7 @@ namespace ICSharpCode.SharpDevelop.Dom { public const long FileMagic = 0x11635233ED2F428C; public const long IndexFileMagic = 0x11635233ED2F427D; - public const short FileVersion = 24; + public const short FileVersion = 25; ProjectContentRegistry registry; string cacheDirectory; @@ -384,7 +384,7 @@ namespace ICSharpCode.SharpDevelop.Dom } writer.Write((int)c.Modifiers); if (c is DefaultClass) { - writer.Write(((DefaultClass)c).Flags); + writer.Write(((DefaultClass)c).CalculatedFlags); } else { writer.Write((byte)0); } @@ -453,7 +453,7 @@ namespace ICSharpCode.SharpDevelop.Dom c.BaseTypes.Add(ReadType()); } c.Modifiers = (ModifierEnum)reader.ReadInt32(); - c.Flags = reader.ReadByte(); + c.CalculatedFlags = reader.ReadByte(); c.ClassType = (ClassType)reader.ReadByte(); ReadAttributes(c); count = reader.ReadInt32(); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs index 1cd06d01d3..dff9d6ba6b 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs @@ -55,6 +55,7 @@ namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer Methods.Add(new ReflectionMethod(methodInfo, this)); } } + this.AddDefaultConstructorIfRequired = (this.ClassType == ClassType.Struct || this.ClassType == ClassType.Enum); foreach (EventInfo eventInfo in type.GetEvents(flags)) { Events.Add(new ReflectionEvent(eventInfo, this)); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs index e720318568..da9c1a5ad8 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs @@ -6,6 +6,7 @@ // using System; +using System.Collections.Generic; using System.Linq; using System.IO; using ICSharpCode.NRefactory; @@ -242,5 +243,67 @@ class Outer where T1 : IDisposable { Assert.AreSame(inner.TypeParameters[1], method.TypeParameters[0].Constraints[0].CastToGenericReturnType().TypeParameter); } + + [Test] + public void DefaultConstructorTest() + { + ICompilationUnit cu = Parse("class X { }", SupportedLanguage.CSharp, true); + + Assert.AreEqual(0, cu.Classes[0].Methods.Count); + + IMethod ctor = cu.Classes[0].DefaultReturnType.GetMethods().Single(m => m.IsConstructor); + Assert.AreEqual(ModifierEnum.Public | ModifierEnum.Synthetic, ctor.Modifiers); + Assert.AreEqual(0, ctor.Parameters.Count); + } + + [Test] + public void DefaultConstructorOnAbstractClassTest() + { + ICompilationUnit cu = Parse("abstract class X { }", SupportedLanguage.CSharp, true); + + Assert.AreEqual(0, cu.Classes[0].Methods.Count); + + IMethod ctor = cu.Classes[0].DefaultReturnType.GetMethods().Single(m => m.IsConstructor); + Assert.AreEqual(ModifierEnum.Protected | ModifierEnum.Synthetic, ctor.Modifiers); + Assert.AreEqual(0, ctor.Parameters.Count); + } + + [Test] + public void NoDefaultConstructorWithExplicitConstructorTest() + { + ICompilationUnit cu = Parse("class X { private X(int a) {} }", SupportedLanguage.CSharp, true); + + Assert.AreEqual(1, cu.Classes[0].Methods.Count); + + IMethod ctor = cu.Classes[0].DefaultReturnType.GetMethods().Single(m => m.IsConstructor); + Assert.AreEqual(ModifierEnum.Private, ctor.Modifiers); + Assert.AreEqual(1, ctor.Parameters.Count); + } + + [Test] + public void DefaultConstructorWithExplicitConstructorOnStructTest() + { + ICompilationUnit cu = Parse("struct X { private X(int a) {} }", SupportedLanguage.CSharp, true); + + Assert.AreEqual(1, cu.Classes[0].Methods.Count); + + List ctors = cu.Classes[0].DefaultReturnType.GetMethods().FindAll(m => m.IsConstructor); + Assert.AreEqual(2, ctors.Count); + + Assert.AreEqual(ModifierEnum.Private, ctors[0].Modifiers); + Assert.AreEqual(1, ctors[0].Parameters.Count); + + Assert.AreEqual(ModifierEnum.Public | ModifierEnum.Synthetic, ctors[1].Modifiers); + Assert.AreEqual(0, ctors[1].Parameters.Count); + } + + [Test] + public void NoDefaultConstructorOnStaticClassTest() + { + ICompilationUnit cu = Parse("static class X { }", SupportedLanguage.CSharp, true); + + Assert.AreEqual(0, cu.Classes[0].Methods.Count); + Assert.IsFalse(cu.Classes[0].DefaultReturnType.GetMethods().Any(m => m.IsConstructor)); + } } }