Browse Source

Fix #1990: Empty default ctor with XML comment discarded from decompilation

pull/1996/head
Siegfried Pammer 5 years ago
parent
commit
6382f8c41d
  1. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 71
      ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs
  3. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -180,7 +180,7 @@ namespace ICSharpCode.Decompiler.CSharp
new IntroduceUnsafeModifier(), new IntroduceUnsafeModifier(),
new AddCheckedBlocks(), new AddCheckedBlocks(),
new DeclareVariables(), // should run after most transforms that modify statements new DeclareVariables(), // should run after most transforms that modify statements
new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables new TransformFieldAndConstructorInitializers(), // must run after DeclareVariables
new DecimalConstantTransform(), new DecimalConstantTransform(),
new PrettifyAssignments(), // must run after DeclareVariables new PrettifyAssignments(), // must run after DeclareVariables
new IntroduceUsingDeclarations(), new IntroduceUsingDeclarations(),

71
ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs → ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs

@ -17,7 +17,6 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
@ -27,33 +26,29 @@ using SRM = System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.CSharp.Transforms namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
/// <summary> /// <summary>
/// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer. /// This transform moves field initializers at the start of constructors to their respective field declarations
/// and transforms this-/base-ctor calls in constructors to constructor initializers.
/// </summary> /// </summary>
public class ConvertConstructorCallIntoInitializer : IAstTransform public class TransformFieldAndConstructorInitializers : DepthFirstAstVisitor, IAstTransform
{ {
TransformContext context;
public void Run(AstNode node, TransformContext context) public void Run(AstNode node, TransformContext context)
{ {
var visitor = new ConvertConstructorCallIntoInitializerVisitor(context); this.context = context;
// If we're viewing some set of members (fields are direct children of SyntaxTree),
// we also need to handle those:
visitor.HandleInstanceFieldInitializers(node.Children);
visitor.HandleStaticFieldInitializers(node.Children);
node.AcceptVisitor(visitor);
visitor.RemoveSingleEmptyConstructor(node.Children, context.CurrentTypeDefinition); try {
} // If we're viewing some set of members (fields are direct children of SyntaxTree),
} // we also need to handle those:
HandleInstanceFieldInitializers(node.Children);
HandleStaticFieldInitializers(node.Children);
sealed class ConvertConstructorCallIntoInitializerVisitor : DepthFirstAstVisitor node.AcceptVisitor(this);
{
readonly TransformContext context;
public ConvertConstructorCallIntoInitializerVisitor(TransformContext context) RemoveSingleEmptyConstructor(node.Children, context.CurrentTypeDefinition);
{ } finally {
Debug.Assert(context != null); this.context = null;
this.context = context; }
} }
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
@ -141,7 +136,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
HandleStaticFieldInitializers(typeDeclaration.Members); HandleStaticFieldInitializers(typeDeclaration.Members);
} }
internal void HandleInstanceFieldInitializers(IEnumerable<AstNode> members) void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
{ {
var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
@ -200,22 +195,34 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
internal void RemoveSingleEmptyConstructor(IEnumerable<AstNode> members, ITypeDefinition contextTypeDefinition) void RemoveSingleEmptyConstructor(IEnumerable<AstNode> members, ITypeDefinition contextTypeDefinition)
{ {
// if we're outside of a type definition skip this altogether
if (contextTypeDefinition == null) return; if (contextTypeDefinition == null) return;
// first get non-static constructor declarations from the AST
var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length == 1 && (members.Skip(1).Any() || instanceCtors[0].Parent is TypeDeclaration)) { // if there's exactly one ctor and it's part of a type declaration or there's more than one member in the current selection
ConstructorDeclaration emptyCtor = new ConstructorDeclaration(); // we can remove the constructor. (We do not want to hide the constructor if the user explicitly selected it in the tree view.)
emptyCtor.Modifiers = contextTypeDefinition.IsAbstract ? Modifiers.Protected : Modifiers.Public; if (instanceCtors.Length == 1 && (instanceCtors[0].Parent is TypeDeclaration || members.Skip(1).Any())) {
if (instanceCtors[0].HasModifier(Modifiers.Unsafe)) var ctor = instanceCtors[0];
emptyCtor.Modifiers |= Modifiers.Unsafe; // dynamically create a pattern of an empty ctor
emptyCtor.Body = new BlockStatement(); ConstructorDeclaration emptyCtorPattern = new ConstructorDeclaration();
if (emptyCtor.IsMatch(instanceCtors[0])) emptyCtorPattern.Modifiers = contextTypeDefinition.IsAbstract ? Modifiers.Protected : Modifiers.Public;
instanceCtors[0].Remove(); if (ctor.HasModifier(Modifiers.Unsafe))
emptyCtorPattern.Modifiers |= Modifiers.Unsafe;
emptyCtorPattern.Body = new BlockStatement();
if (emptyCtorPattern.IsMatch(ctor)) {
bool retainBecauseOfDocumentation = ctor.GetSymbol() is IMethod ctorMethod
&& context.Settings.ShowXmlDocumentation
&& context.DecompileRun.DocumentationProvider?.GetDocumentation(ctorMethod) != null;
if (!retainBecauseOfDocumentation)
ctor.Remove();
}
} }
} }
internal void HandleStaticFieldInitializers(IEnumerable<AstNode> members) void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
{ {
// Translate static constructor into field initializers if the class is BeforeFieldInit // Translate static constructor into field initializers if the class is BeforeFieldInit
var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -271,7 +271,7 @@
<Compile Include="CSharp\ExpressionBuilder.cs" /> <Compile Include="CSharp\ExpressionBuilder.cs" />
<Compile Include="CSharp\StatementBuilder.cs" /> <Compile Include="CSharp\StatementBuilder.cs" />
<Compile Include="CSharp\Transforms\AddCheckedBlocks.cs" /> <Compile Include="CSharp\Transforms\AddCheckedBlocks.cs" />
<Compile Include="CSharp\Transforms\ConvertConstructorCallIntoInitializer.cs" /> <Compile Include="CSharp\Transforms\TransformFieldAndConstructorInitializers.cs" />
<Compile Include="CSharp\Transforms\CustomPatterns.cs" /> <Compile Include="CSharp\Transforms\CustomPatterns.cs" />
<Compile Include="CSharp\Transforms\IAstTransform.cs" /> <Compile Include="CSharp\Transforms\IAstTransform.cs" />
<Compile Include="CSharp\Transforms\IntroduceUnsafeModifier.cs" /> <Compile Include="CSharp\Transforms\IntroduceUnsafeModifier.cs" />

Loading…
Cancel
Save