Annotate DebuggerVisualizerAttribute to be linker safe (#57082)
authorEric Erhardt <eric.erhardt@microsoft.com>
Wed, 11 Aug 2021 20:36:46 +0000 (15:36 -0500)
committerGitHub <noreply@github.com>
Wed, 11 Aug 2021 20:36:46 +0000 (15:36 -0500)
* Annotate DebuggerVisualizerAttribute to be linker safe

Fix #49055

src/libraries/System.Private.CoreLib/src/System/Diagnostics/DebuggerVisualizerAttribute.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs [new file with mode: 0644]
src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj

index 0ad6bd8..425156d 100644 (file)
@@ -1,6 +1,8 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Diagnostics.CodeAnalysis;
+
 namespace System.Diagnostics
 {
     /// <summary>
@@ -12,18 +14,23 @@ namespace System.Diagnostics
     {
         private Type? _target;
 
-        public DebuggerVisualizerAttribute(string visualizerTypeName)
+        public DebuggerVisualizerAttribute(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string visualizerTypeName)
         {
             VisualizerTypeName = visualizerTypeName;
         }
 
-        public DebuggerVisualizerAttribute(string visualizerTypeName, string? visualizerObjectSourceTypeName)
+        public DebuggerVisualizerAttribute(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string visualizerTypeName,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName)
         {
             VisualizerTypeName = visualizerTypeName;
             VisualizerObjectSourceTypeName = visualizerObjectSourceTypeName;
         }
 
-        public DebuggerVisualizerAttribute(string visualizerTypeName, Type visualizerObjectSource)
+        public DebuggerVisualizerAttribute(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string visualizerTypeName,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizerObjectSource)
         {
             if (visualizerObjectSource == null)
             {
@@ -34,7 +41,8 @@ namespace System.Diagnostics
             VisualizerObjectSourceTypeName = visualizerObjectSource.AssemblyQualifiedName;
         }
 
-        public DebuggerVisualizerAttribute(Type visualizer)
+        public DebuggerVisualizerAttribute(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizer)
         {
             if (visualizer == null)
             {
@@ -44,7 +52,9 @@ namespace System.Diagnostics
             VisualizerTypeName = visualizer.AssemblyQualifiedName!;
         }
 
-        public DebuggerVisualizerAttribute(Type visualizer, Type visualizerObjectSource)
+        public DebuggerVisualizerAttribute(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizer,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizerObjectSource)
         {
             if (visualizer == null)
             {
@@ -59,7 +69,9 @@ namespace System.Diagnostics
             VisualizerObjectSourceTypeName = visualizerObjectSource.AssemblyQualifiedName;
         }
 
-        public DebuggerVisualizerAttribute(Type visualizer, string? visualizerObjectSourceTypeName)
+        public DebuggerVisualizerAttribute(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type visualizer,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName)
         {
             if (visualizer == null)
             {
@@ -70,8 +82,10 @@ namespace System.Diagnostics
             VisualizerObjectSourceTypeName = visualizerObjectSourceTypeName;
         }
 
+        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
         public string? VisualizerObjectSourceTypeName { get; }
 
+        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
         public string VisualizerTypeName { get; }
 
         public string? Description { get; set; }
index 3016a1e..fd7ba1e 100644 (file)
@@ -8860,16 +8860,18 @@ namespace System.Diagnostics
     [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple=true)]
     public sealed partial class DebuggerVisualizerAttribute : System.Attribute
     {
-        public DebuggerVisualizerAttribute(string visualizerTypeName) { }
-        public DebuggerVisualizerAttribute(string visualizerTypeName, string? visualizerObjectSourceTypeName) { }
-        public DebuggerVisualizerAttribute(string visualizerTypeName, System.Type visualizerObjectSource) { }
-        public DebuggerVisualizerAttribute(System.Type visualizer) { }
-        public DebuggerVisualizerAttribute(System.Type visualizer, string? visualizerObjectSourceTypeName) { }
-        public DebuggerVisualizerAttribute(System.Type visualizer, System.Type visualizerObjectSource) { }
+        public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string visualizerTypeName) { }
+        public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string visualizerTypeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName) { }
+        public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string visualizerTypeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizerObjectSource) { }
+        public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizer) { }
+        public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizer, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] string? visualizerObjectSourceTypeName) { }
+        public DebuggerVisualizerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizer, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type visualizerObjectSource) { }
         public string? Description { get { throw null; } set { } }
         public System.Type? Target { get { throw null; } set { } }
         public string? TargetTypeName { get { throw null; } set { } }
+        [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)]
         public string? VisualizerObjectSourceTypeName { get { throw null; } }
+        [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)]
         public string VisualizerTypeName { get { throw null; } }
     }
     [System.AttributeUsage(AttributeTargets.Class | System.AttributeTargets.Method | System.AttributeTargets.Constructor | System.AttributeTargets.Struct, Inherited = false)]
diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs b/src/libraries/System.Runtime/tests/TrimmingTests/DebuggerVisualizerAttributeTests.cs
new file mode 100644 (file)
index 0000000..dccaf03
--- /dev/null
@@ -0,0 +1,158 @@
+using System;
+using System.Diagnostics;
+
+/// <summary>
+/// Tests that types used by DebuggerVisualizerAttribute are not trimmed out
+/// when Debugger.IsSupported is true (the default).
+/// </summary>
+class Program
+{
+    static int Main(string[] args)
+    {
+        MyClass myClass = new MyClass() { Name = "trimmed" };
+        MyClassWithVisualizerString myClassWithString = new MyClassWithVisualizerString() { Name = "trimmed" };
+
+        Type[] allTypes = typeof(MyClass).Assembly.GetTypes();
+        bool foundDebuggerVisualizer = false;
+        bool foundDebuggerVisualizer2 = false;
+        bool foundDebuggerVisualizerObjectSource = false;
+        bool foundStringDebuggerVisualizer = false;
+        bool foundStringDebuggerVisualizer2 = false;
+        bool foundStringDebuggerVisualizerObjectSource = false;
+        for (int i = 0; i < allTypes.Length; i++)
+        {
+            Type currentType = allTypes[i];
+            if (currentType.FullName == "Program+MyClass+DebuggerVisualizer" &&
+                currentType.GetProperties().Length == 1 &&
+                currentType.GetConstructors().Length == 1)
+            {
+                foundDebuggerVisualizer = true;
+            }
+            else if (currentType.FullName == "Program+MyClass+DebuggerVisualizer2" &&
+                currentType.GetProperties().Length == 1 &&
+                currentType.GetConstructors().Length == 1)
+            {
+                foundDebuggerVisualizer2 = true;
+            }
+            else if (currentType.FullName == "Program+MyClass+DebuggerVisualizerObjectSource" &&
+                currentType.GetProperties().Length == 1 &&
+                currentType.GetConstructors().Length == 1)
+            {
+                foundDebuggerVisualizerObjectSource = true;
+            }
+            else if (currentType.FullName == "Program+MyClassWithVisualizerStringVisualizer" &&
+                currentType.GetProperties().Length == 1 &&
+                currentType.GetConstructors().Length == 1)
+            {
+                foundStringDebuggerVisualizer = true;
+            }
+            else if (currentType.FullName == "Program+MyClassWithVisualizerStringVisualizer2" &&
+                currentType.GetProperties().Length == 1 &&
+                currentType.GetConstructors().Length == 1)
+            {
+                foundStringDebuggerVisualizer2 = true;
+            }
+            else if (currentType.FullName == "Program+MyClassWithVisualizerStringVisualizerObjectSource" &&
+                currentType.GetProperties().Length == 1 &&
+                currentType.GetConstructors().Length == 1)
+            {
+                foundStringDebuggerVisualizerObjectSource = true;
+            }
+        }
+
+        if (!foundDebuggerVisualizer) return -1;
+        if (!foundDebuggerVisualizer2) return -2;
+        if (!foundDebuggerVisualizerObjectSource) return -3;
+        if (!foundStringDebuggerVisualizer) return -4;
+        if (!foundStringDebuggerVisualizer2) return -5;
+        if (!foundStringDebuggerVisualizerObjectSource) return -6;
+
+        return 100;
+    }
+
+    [DebuggerVisualizer(typeof(DebuggerVisualizer))]
+    [DebuggerVisualizer(typeof(DebuggerVisualizer2), typeof(DebuggerVisualizerObjectSource))]
+    public class MyClass
+    {
+        public string Name { get; set; }
+
+        private class DebuggerVisualizer
+        {
+            private MyClass _instance;
+
+            public DebuggerVisualizer(MyClass instance)
+            {
+                _instance = instance;
+            }
+
+            public string DebuggerName => _instance.Name + " Visualizer";
+        }
+
+        private class DebuggerVisualizer2
+        {
+            private MyClass _instance;
+
+            public DebuggerVisualizer2(MyClass instance)
+            {
+                _instance = instance;
+            }
+
+            public string DebuggerName => _instance.Name + " Visualizer";
+        }
+
+        private class DebuggerVisualizerObjectSource
+        {
+            private MyClass _instance;
+
+            public DebuggerVisualizerObjectSource(MyClass instance)
+            {
+                _instance = instance;
+            }
+
+            public string DebuggerName => _instance.Name + " Visualizer";
+        }
+    }
+
+    [DebuggerVisualizer("Program+MyClassWithVisualizerStringVisualizer")]
+    [DebuggerVisualizer("Program+MyClassWithVisualizerStringVisualizer2", "Program+MyClassWithVisualizerStringVisualizerObjectSource")]
+    public class MyClassWithVisualizerString
+    {
+        public string Name { get; set; }
+    }
+
+    internal class MyClassWithVisualizerStringVisualizer
+    {
+        private MyClassWithVisualizerString _instance;
+
+        public MyClassWithVisualizerStringVisualizer(MyClassWithVisualizerString instance)
+        {
+            _instance = instance;
+        }
+
+        public string DebuggerName => _instance.Name + " StringVisualizer";
+    }
+
+    internal class MyClassWithVisualizerStringVisualizer2
+    {
+        private MyClassWithVisualizerString _instance;
+
+        public MyClassWithVisualizerStringVisualizer2(MyClassWithVisualizerString instance)
+        {
+            _instance = instance;
+        }
+
+        public string DebuggerName => _instance.Name + " StringVisualizer";
+    }
+
+    internal class MyClassWithVisualizerStringVisualizerObjectSource
+    {
+        private MyClassWithVisualizerString _instance;
+
+        public MyClassWithVisualizerStringVisualizerObjectSource(MyClassWithVisualizerString instance)
+        {
+            _instance = instance;
+        }
+
+        public string DebuggerName => _instance.Name + " StringVisualizer";
+    }
+}
index 844edd5..918079b 100644 (file)
@@ -7,6 +7,7 @@
       <SkipOnTestRuntimes>osx-x64;linux-x64;browser-wasm</SkipOnTestRuntimes>
     </TestConsoleAppSourceFiles>
     <TestConsoleAppSourceFiles Include="DebuggerTypeProxyAttributeTests.cs" />
+    <TestConsoleAppSourceFiles Include="DebuggerVisualizerAttributeTests.cs" />
     <TestConsoleAppSourceFiles Include="DefaultValueAttributeCtorTest.cs" />
     <TestConsoleAppSourceFiles Include="GenericArraySortHelperTest.cs" />
     <TestConsoleAppSourceFiles Include="InheritedAttributeTests.cs" />