diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 53a5a4812..474c1e952 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -50,10 +50,15 @@ namespace ICSharpCode.Decompiler.Ast return true; } FieldDefinition field = member as FieldDefinition; - if (field != null) { - if (settings.AnonymousMethods && field.Name.StartsWith("CS$<>", StringComparison.Ordinal) && field.IsCompilerGenerated()) + if (field != null && field.IsCompilerGenerated()) { + if (settings.AnonymousMethods && field.Name.StartsWith("CS$<>", StringComparison.Ordinal)) + return true; + if (settings.AutomaticProperties && field.Name.StartsWith("<", StringComparison.Ordinal) && field.Name.EndsWith("BackingField", StringComparison.Ordinal)) return true; } + // event-fields are not [CompilerGenerated] + if (field != null && settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name)) + return true; return false; } @@ -161,6 +166,7 @@ namespace ICSharpCode.Decompiler.Ast TypeDefinition oldCurrentType = context.CurrentType; context.CurrentType = typeDef; TypeDeclaration astType = new TypeDeclaration(); + ConvertAttributes(astType, typeDef); astType.AddAnnotation(typeDef); astType.Modifiers = ConvertModifiers(typeDef); astType.Name = CleanName(typeDef.Name); @@ -224,7 +230,6 @@ namespace ICSharpCode.Decompiler.Ast AddTypeMembers(astType, typeDef); } - ConvertAttributes(astType, typeDef); context.CurrentType = oldCurrentType; return astType; } @@ -239,9 +244,12 @@ namespace ICSharpCode.Decompiler.Ast int pos = name.LastIndexOf('`'); if (pos >= 0) name = name.Substring(0, pos); + pos = name.LastIndexOf('.'); + if (pos >= 0) + name = name.Substring(pos + 1); return name; } - + #region Convert Type Reference /// /// Converts a type reference. @@ -497,6 +505,7 @@ namespace ICSharpCode.Decompiler.Ast return modifiers; } + #endregion void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef) @@ -511,11 +520,9 @@ namespace ICSharpCode.Decompiler.Ast foreach(EventDefinition eventDef in typeDef.Events) { astType.AddChild(CreateEvent(eventDef), TypeDeclaration.MemberRole); } - + // Add properties - foreach(PropertyDefinition propDef in typeDef.Properties) { - astType.AddChild(CreateProperty(propDef), TypeDeclaration.MemberRole); - } + CreateProperties(astType, typeDef); // Add constructors foreach(MethodDefinition methodDef in typeDef.Methods) { @@ -532,6 +539,35 @@ namespace ICSharpCode.Decompiler.Ast } } + private void CreateProperties(TypeDeclaration astType, TypeDefinition typeDef) + { + CustomAttribute attributeToRemove = null; + foreach (PropertyDefinition propDef in typeDef.Properties) { + MemberDeclaration astProp = CreateProperty(propDef); + + if (astProp.Name == "Item" && propDef.HasParameters) { + var defaultMember = GetDefaultMember(astType.Annotation()); + if (defaultMember.Item1 == "Item") { + astProp = ConvertPropertyToIndexer((PropertyDeclaration)astProp, propDef); + attributeToRemove = defaultMember.Item2; + } else if ((propDef.GetMethod ?? propDef.SetMethod).HasOverrides) { + astProp = ConvertPropertyToIndexer((PropertyDeclaration)astProp, propDef); + } + } + + astType.AddChild(astProp, TypeDeclaration.MemberRole); + } + + if (attributeToRemove != null) { + var astAttr = astType.Attributes.SelectMany(sec => sec.Attributes).First(attr => attr.Annotation() == attributeToRemove); + var attrSection = (AttributeSection)astAttr.Parent; + if (attrSection.Attributes.Count == 1) + attrSection.Remove(); + else + astAttr.Remove(); + } + } + MethodDeclaration CreateMethod(MethodDefinition methodDef) { // Create mapping - used in debugger @@ -547,7 +583,10 @@ namespace ICSharpCode.Decompiler.Ast astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters)); astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters)); if (!methodDef.DeclaringType.IsInterface) { - astMethod.Modifiers = ConvertModifiers(methodDef); + if (!methodDef.HasOverrides) + astMethod.Modifiers = ConvertModifiers(methodDef); + else + astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); } ConvertAttributes(astMethod, methodDef); @@ -613,11 +652,48 @@ namespace ICSharpCode.Decompiler.Ast return astMethod; } + IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef) + { + var astIndexer = new IndexerDeclaration(); + astIndexer.Name = astProp.Name; + astIndexer.CopyAnnotationsFrom(astProp); + astProp.Attributes.MoveTo(astIndexer.Attributes); + astIndexer.Modifiers = astProp.Modifiers; + astIndexer.PrivateImplementationType = astProp.PrivateImplementationType.Detach(); + astIndexer.ReturnType = astProp.ReturnType.Detach(); + astIndexer.Getter = astProp.Getter.Detach(); + astIndexer.Setter = astProp.Setter.Detach(); + astIndexer.Parameters.AddRange(MakeParameters(propDef.Parameters)); + return astIndexer; + } + + Modifiers FixUpVisibility(Modifiers m) + { + Modifiers v = m & Modifiers.VisibilityMask; + // If any of the modifiers is public, use that + if ((v & Modifiers.Public) == Modifiers.Public) + return Modifiers.Public | (m & ~Modifiers.VisibilityMask); + // If both modifiers are private, no need to fix anything + if (v == Modifiers.Private) + return m; + // Otherwise, use the other modifiers (internal and/or protected) + return m & ~Modifiers.Private; + } + PropertyDeclaration CreateProperty(PropertyDefinition propDef) { PropertyDeclaration astProp = new PropertyDeclaration(); astProp.AddAnnotation(propDef); - astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod); + var accessor = propDef.GetMethod ?? propDef.SetMethod; + Modifiers getterModifiers = Modifiers.None; + Modifiers setterModifiers = Modifiers.None; + if (!propDef.DeclaringType.IsInterface && !accessor.HasOverrides) { + getterModifiers = ConvertModifiers(propDef.GetMethod); + setterModifiers = ConvertModifiers(propDef.SetMethod); + astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers); + } else if (accessor.HasOverrides) { + astProp.PrivateImplementationType = ConvertType(accessor.Overrides.First().DeclaringType); + } astProp.Name = CleanName(propDef.Name); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { @@ -627,8 +703,12 @@ namespace ICSharpCode.Decompiler.Ast astProp.Getter = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) }.WithAnnotation(propDef.GetMethod); + ConvertAttributes(astProp.Getter, propDef.GetMethod); + if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) + astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; + if (methodMapping != null) astProp.Getter.AddAnnotation(methodMapping); } @@ -639,9 +719,13 @@ namespace ICSharpCode.Decompiler.Ast astProp.Setter = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) }.WithAnnotation(propDef.SetMethod); + ConvertAttributes(astProp.Setter, propDef.SetMethod); ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), AttributeTarget.Param); + if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) + astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; + if (methodMapping != null) astProp.Setter.AddAnnotation(methodMapping); } @@ -655,7 +739,10 @@ namespace ICSharpCode.Decompiler.Ast astEvent.AddAnnotation(eventDef); astEvent.Name = CleanName(eventDef.Name); astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); - astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); + if (eventDef.AddMethod == null || !eventDef.AddMethod.HasOverrides) + astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); + else + astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); if (eventDef.AddMethod != null) { // Create mapping - used in debugger MethodMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(CSharpCodeMapping.SourceCodeMappings); @@ -882,20 +969,27 @@ namespace ICSharpCode.Decompiler.Ast } } - void ConvertAttributes(AttributedNode attributedNode, FieldDefinition fieldDefinition) + internal static void ConvertAttributes(AttributedNode attributedNode, FieldDefinition fieldDefinition, AttributeTarget target = AttributeTarget.None) { ConvertCustomAttributes(attributedNode, fieldDefinition); #region FieldOffsetAttribute if (fieldDefinition.HasLayoutInfo) { - Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute)); + Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute), fieldDefinition.Module); fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset)); - attributedNode.Attributes.Add(new AttributeSection(fieldOffset)); + attributedNode.Attributes.Add(new AttributeSection(fieldOffset) { AttributeTarget = target }); + } + #endregion + + #region NonSerializedAttribute + if (fieldDefinition.IsNotSerialized) { + Ast.Attribute nonSerialized = CreateNonCustomAttribute(typeof(NonSerializedAttribute), fieldDefinition.Module); + attributedNode.Attributes.Add(new AttributeSection(nonSerialized) { AttributeTarget = target }); } #endregion if (fieldDefinition.HasMarshalInfo) { - attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module))); + attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module)) { AttributeTarget = target }); } } @@ -936,6 +1030,7 @@ namespace ICSharpCode.Decompiler.Ast var attributes = new List(); foreach (var customAttribute in customAttributeProvider.CustomAttributes) { var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); + attribute.AddAnnotation(customAttribute); attribute.Type = ConvertType(customAttribute.AttributeType); attributes.Add(attribute); @@ -1066,5 +1161,19 @@ namespace ICSharpCode.Decompiler.Ast return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); } + + /// + /// Gets the name of the default member of the type pointed by the attribute. + /// + /// The type definition. + /// The name of the default member or null if no attribute has been found. + private static Tuple GetDefaultMember(TypeDefinition type) + { + foreach (CustomAttribute ca in type.CustomAttributes) + if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") + return Tuple.Create(ca.ConstructorArguments.Single().Value as string, ca); + return new Tuple(null, null); + } + } } diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 0f42df534..6da566fcb 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -610,6 +610,9 @@ namespace ICSharpCode.Decompiler.Ast }; } } + } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + return target.Invoke(methodArgs); } } // Default invocation diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index 0a4602084..977867036 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -65,8 +65,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation(); if (fieldDef == null) break; - FieldDeclaration fieldDecl = typeDeclaration.Members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); - if (fieldDecl == null) + AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation() == fieldDef); + if (fieldOrEventDecl == null) break; allSame = true; @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (allSame) { foreach (var ctor in instanceCtors) ctor.Body.First().Remove(); - fieldDecl.Variables.Single().Initializer = m.Get("initializer").Single().Detach(); + fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = m.Get("initializer").Single().Detach(); } } while (allSame); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index f41fe43ec..4dd384831 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -14,11 +14,27 @@ namespace ICSharpCode.Decompiler.Ast.Transforms /// public class PatternStatementTransform : IAstTransform { + DecompilerContext context; + + public PatternStatementTransform(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + } + public void Run(AstNode compilationUnit) { - TransformUsings(compilationUnit); - TransformForeach(compilationUnit); + if (context.Settings.UsingStatement) + TransformUsings(compilationUnit); + if (context.Settings.ForEachStatement) + TransformForeach(compilationUnit); TransformFor(compilationUnit); + TransformDoWhile(compilationUnit); + if (context.Settings.AutomaticProperties) + TransformAutomaticProperties(compilationUnit); + if (context.Settings.AutomaticEvents) + TransformAutomaticEvents(compilationUnit); } /// @@ -99,7 +115,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms #endregion #region foreach - UsingStatement foreachPattern = new UsingStatement { + static readonly UsingStatement foreachPattern = new UsingStatement { ResourceAcquisition = new VariableDeclarationStatement { Type = new AnyNode("enumeratorType"), Variables = { @@ -188,7 +204,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms #endregion #region for - WhileStatement forPattern = new WhileStatement { + static readonly WhileStatement forPattern = new WhileStatement { Condition = new BinaryOperatorExpression { Left = new NamedNode("ident", new IdentifierExpression()), Operator = BinaryOperatorType.Any, @@ -206,8 +222,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Right = new AnyNode() })) } - } - }; + }}; public void TransformFor(AstNode compilationUnit) { @@ -237,5 +252,207 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } #endregion + + #region doWhile + static readonly WhileStatement doWhilePattern = new WhileStatement { + Condition = new PrimitiveExpression(true), + EmbeddedStatement = new BlockStatement { + Statements = { + new Repeat(new AnyNode("statement")), + new IfElseStatement { + Condition = new AnyNode("condition"), + TrueStatement = new BlockStatement { new BreakStatement() } + } + } + }}; + + public void TransformDoWhile(AstNode compilationUnit) + { + foreach (WhileStatement whileLoop in compilationUnit.Descendants.OfType().ToArray()) { + Match m = doWhilePattern.Match(whileLoop); + if (m != null) { + DoWhileStatement doLoop = new DoWhileStatement(); + doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get("condition").Single().Detach()); + doLoop.Condition.AcceptVisitor(new PushNegation(), null); + BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement; + block.Statements.Last().Remove(); // remove if statement + doLoop.EmbeddedStatement = block.Detach(); + whileLoop.ReplaceWith(doLoop); + + // we may have to extract variable definitions out of the loop if they were used in the condition: + foreach (var varDecl in block.Statements.OfType()) { + VariableInitializer v = varDecl.Variables.Single(); + if (doLoop.Condition.DescendantsAndSelf.OfType().Any(i => i.Identifier == v.Name)) { + AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach()); + // move annotations from v to assign: + assign.CopyAnnotationsFrom(v); + v.RemoveAnnotations(); + // remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement: + varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl)); + varDecl.RemoveAnnotations(); + + // insert the varDecl above the do-while loop: + doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole); + } + } + } + } + } + #endregion + + #region Automatic Properties + static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { + Attributes = { new Repeat(new AnyNode()) }, + Modifiers = Modifiers.Any, + ReturnType = new AnyNode(), + Getter = new Accessor { + Attributes = { new Repeat(new AnyNode()) }, + Modifiers = Modifiers.Any, + Body = new BlockStatement { + new ReturnStatement { + Expression = new NamedNode("fieldReference", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) + } + } + }, + Setter = new Accessor { + Attributes = { new Repeat(new AnyNode()) }, + Modifiers = Modifiers.Any, + Body = new BlockStatement { + new AssignmentExpression { + Left = new Backreference("fieldReference"), + Right = new IdentifierExpression("value") + } + }}}; + + void TransformAutomaticProperties(AstNode compilationUnit) + { + foreach (var property in compilationUnit.Descendants.OfType()) { + PropertyDefinition cecilProperty = property.Annotation(); + if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null) + continue; + if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated())) + continue; + Match m = automaticPropertyPattern.Match(property); + if (m != null) { + FieldDefinition field = m.Get("fieldReference").Single().Annotation(); + if (field.IsCompilerGenerated()) { + RemoveCompilerGeneratedAttribute(property.Getter.Attributes); + RemoveCompilerGeneratedAttribute(property.Setter.Attributes); + property.Getter.Body = null; + property.Setter.Body = null; + } + } + } + } + + void RemoveCompilerGeneratedAttribute(AstNodeCollection attributeSections) + { + foreach (AttributeSection section in attributeSections) { + foreach (var attr in section.Attributes) { + TypeReference tr = attr.Type.Annotation(); + if (tr != null && tr.Namespace == "System.Runtime.CompilerServices" && tr.Name == "CompilerGeneratedAttribute") { + attr.Remove(); + } + } + if (section.Attributes.Count == 0) + section.Remove(); + } + } + #endregion + + #region Automatic Events + Accessor automaticEventPatternV4 = new Accessor { + Body = new BlockStatement { + new VariableDeclarationStatement { + Type = new AnyNode("type"), + Variables = { + new NamedNode( + "var1", new VariableInitializer { + Initializer = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) + })} + }, + new VariableDeclarationStatement { + Type = new Backreference("type"), + Variables = { new NamedNode("var2", new VariableInitializer()) } + }, + new DoWhileStatement { + EmbeddedStatement = new BlockStatement { + new AssignmentExpression(new IdentifierExpressionBackreference("var2"), new IdentifierExpressionBackreference("var1")), + new VariableDeclarationStatement { + Type = new Backreference("type"), + Variables = { + new NamedNode( + "var3", new VariableInitializer { + Initializer = new AnyNode("delegateCombine").ToExpression().Invoke( + new IdentifierExpressionBackreference("var2"), + new IdentifierExpression("value") + ).CastTo(new Backreference("type")) + }) + }}, + new AssignmentExpression { + Left = new IdentifierExpressionBackreference("var1"), + Right = new AnyNode("Interlocked").ToType().Invoke( + "CompareExchange", + new AstType[] { new Backreference("type") }, // type argument + new Expression[] { // arguments + new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, + new IdentifierExpressionBackreference("var3"), + new IdentifierExpressionBackreference("var2") + } + )} + }, + Condition = new BinaryOperatorExpression { + Left = new IdentifierExpressionBackreference("var1"), + Operator = BinaryOperatorType.InEquality, + Right = new IdentifierExpressionBackreference("var2") + }} + }}; + + bool CheckAutomaticEventV4Match(Match m, CustomEventDeclaration ev, bool isAddAccessor) + { + if (m == null) + return false; + if (m.Get("field").Single().MemberName != ev.Name) + return false; // field name must match event name + if (ev.ReturnType.Match(m.Get("type").Single()) == null) + return false; // variable types must match event type + var combineMethod = m.Get("delegateCombine").Single().Parent.Annotation(); + if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) + return false; + if (combineMethod.DeclaringType.FullName != "System.Delegate") + return false; + var ice = m.Get("Interlocked").Single().Annotation(); + return ice != null && ice.FullName == "System.Threading.Interlocked"; + } + + void TransformAutomaticEvents(AstNode compilationUnit) + { + foreach (var ev in compilationUnit.Descendants.OfType().ToArray()) { + Match m1 = automaticEventPatternV4.Match(ev.AddAccessor); + if (!CheckAutomaticEventV4Match(m1, ev, true)) + continue; + Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor); + if (!CheckAutomaticEventV4Match(m2, ev, false)) + continue; + EventDeclaration ed = new EventDeclaration(); + ev.Attributes.MoveTo(ed.Attributes); + ed.ReturnType = ev.ReturnType.Detach(); + ed.Modifiers = ev.Modifiers; + ed.Variables.Add(new VariableInitializer(ev.Name)); + ed.CopyAnnotationsFrom(ev); + + EventDefinition eventDef = ev.Annotation(); + if (eventDef != null) { + FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name); + if (field != null) { + ed.AddAnnotation(field); + AstBuilder.ConvertAttributes(ed, field, AttributeTarget.Field); + } + } + + ev.ReplaceWith(ed); + } + } + #endregion } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index d240c7583..b4aedf175 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -19,7 +19,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return new IAstTransform[] { new PushNegation(), new DelegateConstruction(context), - new PatternStatementTransform(), + new PatternStatementTransform(context), new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators(), }; diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index a1770d771..82e6797b6 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -41,6 +41,66 @@ namespace ICSharpCode.Decompiler } } + bool automaticProperties = true; + + /// + /// Decompile automatic properties + /// + public bool AutomaticProperties { + get { return automaticProperties; } + set { + if (automaticProperties != value) { + automaticProperties = value; + OnPropertyChanged("AutomaticProperties"); + } + } + } + + bool automaticEvents = true; + + /// + /// Decompile automatic events + /// + public bool AutomaticEvents { + get { return automaticEvents; } + set { + if (automaticEvents != value) { + automaticEvents = value; + OnPropertyChanged("AutomaticEvents"); + } + } + } + + bool usingStatement = true; + + /// + /// Decompile using statements. + /// + public bool UsingStatement { + get { return usingStatement; } + set { + if (usingStatement != value) { + usingStatement = value; + OnPropertyChanged("UsingStatement"); + } + } + } + + bool forEachStatement = true; + + /// + /// Decompile foreach statements. + /// + public bool ForEachStatement { + get { return forEachStatement; } + set { + if (forEachStatement != value) { + forEachStatement = value; + OnPropertyChanged("ForEachStatement"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index b7afed8d4..e09d14e33 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -458,11 +458,22 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Rethrow: case ILCode.LoopOrSwitchBreak: case ILCode.LoopContinue: + case ILCode.YieldBreak: return null; case ILCode.Ret: if (forceInferChildren && expr.Arguments.Count == 1) InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType); return null; + case ILCode.YieldReturn: + if (forceInferChildren) { + GenericInstanceType genericType = context.CurrentMethod.ReturnType as GenericInstanceType; + if (genericType != null) { // IEnumerable or IEnumerator + InferTypeForExpression(expr.Arguments[0], genericType.GenericArguments[0]); + } else { // non-generic IEnumerable or IEnumerator + InferTypeForExpression(expr.Arguments[0], typeSystem.Object); + } + } + return null; #endregion case ILCode.Pop: return null; diff --git a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs index 9e653d374..d7b83381c 100644 --- a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs @@ -690,18 +690,19 @@ namespace ICSharpCode.Decompiler.ILAst // Now verify that the last instruction in the body is 'ret(false)' if (returnVariable != null) { // If we don't have a return variable, we already verified that above. - if (bodyLength < 2) - throw new YieldAnalysisFailedException(); - ILExpression leave = body[bodyLength - 1] as ILExpression; - if (leave == null || leave.Operand != returnLabel || !(leave.Code == ILCode.Br || leave.Code == ILCode.Leave)) - throw new YieldAnalysisFailedException(); - ILExpression store0 = body[bodyLength - 2] as ILExpression; + // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' + + // Maybe might be a jump to the return label after the stloc: + ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; + if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) + bodyLength--; + ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) throw new YieldAnalysisFailedException(); if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) throw new YieldAnalysisFailedException(); - bodyLength -= 2; // don't conside the 'ret(false)' part of the body + bodyLength--; // don't conside the stloc instruction to be part of the body } // verify that the last element in the body is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; diff --git a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs index 866d9aa06..1a05566b4 100644 --- a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs +++ b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs @@ -134,7 +134,21 @@ namespace AppliedToPropertySet [MyAttribute] set { - return; + } + } + } +} +//$$ AppliedToIndexer +namespace AppliedToIndexer +{ + public class TestClass + { + [Obsolete("reason")] + public int this[int i] + { + get + { + return 0; } } } @@ -177,7 +191,7 @@ namespace AppliedToParameter } public class MyClass { - public void Method([MyAttribute]int val) + public void Method([MyAttribute] int val) { } } @@ -204,7 +218,6 @@ namespace NamedInitializerPropertyString } set { - return; } } } @@ -227,7 +240,6 @@ namespace NamedInitializerPropertyType } set { - return; } } } @@ -250,7 +262,6 @@ namespace NamedInitializerPropertyEnum } set { - return; } } } @@ -321,7 +332,6 @@ namespace TargetPropertySetParam [param: MyAttribute] set { - return; } } } @@ -344,13 +354,66 @@ namespace TargetPropertySetReturn [return: MyAttribute] set { - return; } } } } -//$$ TargetPropertyIndexSetParam -namespace TargetPropertyIndexSetParam +//$$ TargetPropertyIndexGetReturn +namespace TargetPropertyIndexGetReturn +{ + [AttributeUsage(AttributeTargets.All)] + public class MyAttributeAttribute : Attribute + { + } + public class MyClass + { + public int this[string s] + { + [return: MyAttribute] + get + { + return 3; + } + } + } +} +//$$ TargetPropertyIndexParamOnlySet +namespace TargetPropertyIndexParamOnlySet +{ + [AttributeUsage(AttributeTargets.All)] + public class MyAttributeAttribute : Attribute + { + } + public class MyClass + { + public int this[[MyAttribute] string s] + { + set + { + } + } + } +} +//$$ TargetPropertyIndexParamOnlyGet +namespace TargetPropertyIndexParamOnlyGet +{ + [AttributeUsage(AttributeTargets.All)] + public class MyAttributeAttribute : Attribute + { + } + public class MyClass + { + public int this[[MyAttribute] string s] + { + get + { + return 3; + } + } + } +} +//$$ TargetPropertyIndexSetReturn +namespace TargetPropertyIndexSetReturn { [AttributeUsage(AttributeTargets.All)] public class MyAttributeAttribute : Attribute @@ -364,10 +427,9 @@ namespace TargetPropertyIndexSetParam { return ""; } - [param: MyAttribute] + [return: MyAttribute] set { - return; } } } @@ -391,7 +453,6 @@ namespace TargetPropertyIndexSetMultiParam [param: MyAttribute] set { - return; } } } diff --git a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs index 223247bf5..240e6b0d1 100644 --- a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs @@ -43,7 +43,8 @@ public static class DelegateConstruction public static List> AnonymousMethodStoreWithinLoop() { List> list = new List>(); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) + { int counter; list.Add(x => counter = x); } @@ -54,7 +55,8 @@ public static class DelegateConstruction { List> list = new List>(); int counter; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) + { list.Add(x => counter = x); } return list; diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 42ee0a0cd..1f8fbd5be 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -57,6 +57,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs b/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs index c610bc7ae..0241c217a 100644 --- a/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs +++ b/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs @@ -39,6 +39,9 @@ public class PropertiesAndEvents public event EventHandler AutomaticEvent; + [field: NonSerialized] + public event EventHandler AutomaticEventWithInitializer = delegate {}; + public event EventHandler CustomEvent { add { this.AutomaticEvent += value; diff --git a/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs new file mode 100644 index 000000000..08193844a --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs @@ -0,0 +1,305 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +//$CS +using System; +//$CE + +//$$ IndexerWithGetOnly +namespace IndexerWithGetOnly +{ + public class MyClass + { + public int this[int i] + { + get + { + return i; + } + } + } +} +//$$ IndexerWithSetOnly +namespace IndexerWithSetOnly +{ + public class MyClass + { + public int this[int i] + { + set + { + } + } + } +} +//$$ IndexerWithMoreParameters +namespace IndexerWithMoreParameters +{ + public class MyClass + { + public int this[int i, string s, Type t] + { + get + { + return 0; + } + } + } +} +//$$ IndexerInGenericClass +namespace IndexerInGenericClass +{ + public class MyClass + { + public int this[T t] + { + get + { + return 0; + } + } + } +} +//$$ OverloadedIndexer +namespace OverloadedIndexer +{ + public class MyClass + { + public int this[int t] + { + get + { + return 0; + } + } + public int this[string s] + { + get + { + return 0; + } + set + { + Console.WriteLine(value + " " + s); + } + } + } +} +//$$ IndexerInInterface +namespace IndexerInInterface +{ + public interface IInterface + { + int this[string s, string s2] + { + set; + } + } +} +//$$ IndexerInterfaceExplicitImplementation +namespace IndexerInterfaceExplicitImplementation +{ + public interface IMyInterface + { + int this[string s] + { + get; + } + } + public class MyClass : IMyInterface + { + int IMyInterface.this[string s] + { + get + { + return 3; + } + } + } +} +//$$ IndexerInterfaceImplementation +namespace IndexerInterfaceImplementation +{ + public interface IMyInterface + { + int this[string s] + { + get; + } + } + public class MyClass : IMyInterface + { + public int this[string s] + { + get + { + return 3; + } + } + } +} +//$$ IndexerAbstract +namespace IndexerAbstract +{ + public abstract class MyClass + { + public abstract int this[string s, string s2] + { + set; + } + protected abstract string this[int index] + { + get; + } + } +} +//$$ MethodExplicit +namespace MethodExplicit +{ + public interface IMyInterface + { + void MyMethod(); + } + public class MyClass : IMyInterface + { + void IMyInterface.MyMethod() + { + } + } +} +//$$ MethodFromInterfaceVirtual +namespace MethodFromInterfaceVirtual +{ + public interface IMyInterface + { + void MyMethod(); + } + public class MyClass : IMyInterface + { + public virtual void MyMethod() + { + } + } +} +//$$ MethodFromInterface +namespace MethodFromInterface +{ + public interface IMyInterface + { + void MyMethod(); + } + public class MyClass : IMyInterface + { + public void MyMethod() + { + } + } +} +//$$ MethodFromInterfaceAbstract +namespace MethodFromInterfaceAbstract +{ + public interface IMyInterface + { + void MyMethod(); + } + public abstract class MyClass : IMyInterface + { + public abstract void MyMethod(); + } +} +//$$ PropertyInterface +namespace PropertyInterface +{ + public interface IMyInterface + { + int MyProperty + { + get; + set; + } + } +} +//$$ PropertyInterfaceExplicitImplementation +namespace PropertyInterfaceExplicitImplementation +{ + public interface IMyInterface + { + int MyProperty + { + get; + set; + } + } + public class MyClass : IMyInterface + { + int IMyInterface.MyProperty + { + get + { + return 0; + } + set + { + } + } + } +} +//$$ PropertyInterfaceImplementation +namespace PropertyInterfaceImplementation +{ + public interface IMyInterface + { + int MyProperty + { + get; + set; + } + } + public class MyClass : IMyInterface + { + public int MyProperty + { + get + { + return 0; + } + set + { + } + } + } +} +//$$ PropertyPrivateGetPublicSet +namespace PropertyPrivateGetPublicSet +{ + public class MyClass + { + public int MyProperty + { + private get + { + return 3; + } + set + { + } + } + } +} +//$$ PropertyPublicGetProtectedSet +namespace PropertyPublicGetProtectedSet +{ + public class MyClass + { + public int MyProperty + { + get + { + return 3; + } + protected set + { + } + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs b/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs index 56d7db39f..398144b2e 100644 --- a/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs +++ b/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs @@ -8,5 +8,10 @@ namespace ICSharpCode.Decompiler.Tests.Types { public class TypeTests : DecompilerTestBase { + [Test] + public void TypeMemberDeclarations() + { + ValidateFileRoundtrip(@"Types\S_TypeMemberDeclarations.cs"); + } } } diff --git a/ICSharpCode.Decompiler/Tests/YieldReturn.cs b/ICSharpCode.Decompiler/Tests/YieldReturn.cs index de9bd1efb..f7c7f73f6 100644 --- a/ICSharpCode.Decompiler/Tests/YieldReturn.cs +++ b/ICSharpCode.Decompiler/Tests/YieldReturn.cs @@ -100,4 +100,19 @@ public static class YieldReturn yield return () => copy; } } + + public static IEnumerable GetEvenNumbers(int n) + { + for (int i = 0; i < n; i++) { + if (i % 2 == 0) + yield return i; + } + } + + public static IEnumerable YieldChars() + { + yield return 'a'; + yield return 'b'; + yield return 'c'; + } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index f1bbc05ce..538dbdc44 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -122,6 +122,7 @@ Code + README.txt diff --git a/ILSpy/XamlResourceNode.cs b/ILSpy/XamlResourceNode.cs new file mode 100644 index 000000000..7610ca6b7 --- /dev/null +++ b/ILSpy/XamlResourceNode.cs @@ -0,0 +1,67 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Threading.Tasks; + +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Xaml +{ + [Export(typeof(IResourceNodeFactory))] + sealed class XamlResourceNodeFactory : IResourceNodeFactory + { + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + if (key.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + return new XamlResourceEntryNode(key, data); + else + return null; + } + } + + sealed class XamlResourceEntryNode : ResourceEntryNode + { + string xaml; + + public XamlResourceEntryNode(string key, Stream data) : base(key, data) + { + } + + internal override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + IHighlightingDefinition highlighting = null; + + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + // cache read XAML because stream will be closed after first read + if (xaml == null) { + using (var reader = new StreamReader(data)) { + xaml = reader.ReadToEnd(); + } + } + output.Write(xaml); + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.Show(t.Result, highlighting) + ); + return true; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs index 2dab5a5ad..dafcdc253 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs @@ -157,6 +157,15 @@ namespace ICSharpCode.NRefactory.CSharp } } + /// + /// Gets all descendants of this node (including this node itself). + /// + public IEnumerable DescendantsAndSelf { + get { + return Utils.TreeTraversal.PreOrder(this, n => n.Children); + } + } + /// /// Gets the first child with the specified role. /// Returns the role's null object if the child is not found. diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs index a35204e3c..5582b9012 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs @@ -53,6 +53,30 @@ namespace ICSharpCode.NRefactory.CSharp return new TypeReferenceExpression { Type = this }.Member(memberName); } + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, typeArguments, arguments); + } + public static AstType Create(Type type) { switch (Type.GetTypeCode(type)) { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs index ec0a85705..3fc7fa291 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs @@ -72,7 +72,10 @@ namespace ICSharpCode.NRefactory.CSharp new KeyValuePair(Modifiers.Volatile, "volatile".Length), new KeyValuePair(Modifiers.Extern, "extern".Length), new KeyValuePair(Modifiers.Partial, "partial".Length), - new KeyValuePair(Modifiers.Const, "const".Length) + new KeyValuePair(Modifiers.Const, "const".Length), + + // even though it's used for patterns only, it needs to be in this table to be usable in the AST + new KeyValuePair(Modifiers.Any, "any".Length) }; public static IEnumerable AllModifiers { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs index a05a817aa..700a3c04d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs @@ -66,4 +66,9 @@ namespace ICSharpCode.NRefactory.CSharp //Final = 0x400000, //Literal = 0x800000, VisibilityMask = Private | Internal | Protected | Public, + + /// + /// Special value used to match any modifiers during pattern matching. + /// + Any = unchecked((int)0x80000000) }} diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index 23f5c2ea5..ed2122d19 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -34,6 +34,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return p != null ? new TypePlaceholder(p) : null; } + public AstType ToType() + { + return new TypePlaceholder(this); + } + public static implicit operator Expression(Pattern p) { return p != null ? new ExpressionPlaceholder(p) : null; @@ -64,6 +69,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return p != null ? new VariablePlaceholder(p) : null; } + public static implicit operator AttributeSection(Pattern p) + { + return p != null ? new AttributeSectionPlaceholder(p) : null; + } + // Make debugging easier by giving Patterns a ToString() implementation public override string ToString() { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs index f68feb443..67987595f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs @@ -152,4 +152,33 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return child.DoMatchCollection(role, pos, match, backtrackingStack); } } + + sealed class AttributeSectionPlaceholder : AttributeSection + { + readonly AstNode child; + + public AttributeSectionPlaceholder(AstNode child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Placeholder; } + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + return child.DoMatch(other, match); + } + + internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) + { + return child.DoMatchCollection(role, pos, match, backtrackingStack); + } + } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs index c487032eb..cc5651d87 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs @@ -56,8 +56,12 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.Body); } set { SetChildByRole (Roles.Body, value); } } - - public override S AcceptVisitor (IAstVisitor visitor, T data) + + public AstNodeCollection Parameters { + get { return GetChildrenByRole(Roles.Parameter); } + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAccessor (this, data); } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs index 41eec380d..af5e584dc 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.CSharp protected bool MatchAttributesAndModifiers(AttributedNode o, PatternMatching.Match match) { - return this.Modifiers == o.Modifiers && this.Attributes.DoMatch(o.Attributes, match); + return (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) && this.Attributes.DoMatch(o.Attributes, match); } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index da02c47d5..bcab463d7 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -202,8 +202,20 @@ namespace ICSharpCode.NRefactory.CSharp { WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); } + #endif - + + void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) + { + WriteToken("[", AstNode.Roles.LBracket); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list.SafeCast()); + Space(spaceWithin); + } + WriteToken("]", AstNode.Roles.RBracket); + } + void WriteCommaSeparatedListInBrackets(IEnumerable list) { WriteToken("[", AstNode.Roles.LBracket); @@ -1815,7 +1827,7 @@ namespace ICSharpCode.NRefactory.CSharp WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); WriteKeyword("this"); Space(policy.BeforeMethodDeclarationParentheses); - WriteCommaSeparatedListInParenthesis(indexerDeclaration.Parameters, policy.WithinMethodDeclarationParentheses); + WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.WithinMethodDeclarationParentheses); OpenBrace(policy.PropertyBraceStyle); // output get/set in their original order foreach (AstNode node in indexerDeclaration.Children) {