diff --git a/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs b/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs index a527ca2e0df60..f07200ab47e4b 100644 --- a/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs +++ b/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; @@ -23,14 +24,24 @@ public class DescriptorMarker : ProcessLinkerXmlBase static readonly string[] _accessorsAll = new string[] { "all" }; static readonly char[] _accessorsSep = new char[] { ';' }; + protected readonly HashSet _preservedMembers; + public DescriptorMarker (LinkContext context, Stream documentStream, string xmlDocumentLocation) : base (context, documentStream, xmlDocumentLocation) { + _preservedMembers = new (); } public DescriptorMarker (LinkContext context, Stream documentStream, EmbeddedResource resource, AssemblyDefinition resourceAssembly, string xmlDocumentLocation = "") : base (context, documentStream, resource, resourceAssembly, xmlDocumentLocation) { + _preservedMembers = new (); + } + + protected void LogDuplicatePreserve(string memberName, XPathNavigator duplicatePosition) + { + var origin = GetMessageOriginForPosition (duplicatePosition); + _context.LogMessage (MessageContainer.CreateInfoMessage (origin, $"Duplicate preserve of '{memberName}'")); } public void Mark () @@ -147,16 +158,16 @@ protected static TypePreserve GetTypePreserve (XPathNavigator nav) protected override void ProcessField (TypeDefinition type, FieldDefinition field, XPathNavigator nav) { - if (_context.Annotations.IsMarked (field)) - LogWarning (nav, DiagnosticId.XmlDuplicatePreserveMember, field.FullName); + if (!_preservedMembers.Add (field)) + LogDuplicatePreserve (field.FullName, nav); _context.Annotations.Mark (field, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation), GetMessageOriginForPosition (nav)); } protected override void ProcessMethod (TypeDefinition type, MethodDefinition method, XPathNavigator nav, object? customData) { - if (_context.Annotations.IsMarked (method)) - LogWarning (nav, DiagnosticId.XmlDuplicatePreserveMember, method.GetDisplayName ()); + if (!_preservedMembers.Add (method)) + LogDuplicatePreserve (method.GetDisplayName (), nav); _context.Annotations.MarkIndirectlyCalledMethod (method); _context.Annotations.SetAction (method, MethodAction.Parse); @@ -212,8 +223,8 @@ public static string GetMethodSignature (MethodDefinition meth, bool includeGene protected override void ProcessEvent (TypeDefinition type, EventDefinition @event, XPathNavigator nav, object? customData) { - if (_context.Annotations.IsMarked (@event)) - LogWarning (nav, DiagnosticId.XmlDuplicatePreserveMember, @event.FullName); + if (!_preservedMembers.Add (@event)) + LogDuplicatePreserve(@event.FullName, nav); ProcessMethod (type, @event.AddMethod, nav, customData); ProcessMethod (type, @event.RemoveMethod, nav, customData); @@ -224,8 +235,8 @@ protected override void ProcessProperty (TypeDefinition type, PropertyDefinition { string[] accessors = fromSignature ? GetAccessors (nav) : _accessorsAll; - if (_context.Annotations.IsMarked (property)) - LogWarning (nav, DiagnosticId.XmlDuplicatePreserveMember, property.FullName); + if (!_preservedMembers.Add (property)) + LogDuplicatePreserve(property.FullName, nav); if (Array.IndexOf (accessors, "all") >= 0) { ProcessMethodIfNotNull (type, property.GetMethod, nav, customData); diff --git a/src/tools/illink/src/linker/Linker/MessageContainer.cs b/src/tools/illink/src/linker/Linker/MessageContainer.cs index eb1fe94a53cfd..0de844bd6e800 100644 --- a/src/tools/illink/src/linker/Linker/MessageContainer.cs +++ b/src/tools/illink/src/linker/Linker/MessageContainer.cs @@ -253,6 +253,11 @@ public static MessageContainer CreateInfoMessage (string text) return new MessageContainer (MessageCategory.Info, text, null); } + internal static MessageContainer CreateInfoMessage (MessageOrigin origin, string text) + { + return new MessageContainer (MessageCategory.Info, text, null, "", origin); + } + /// /// Create a diagnostics message. /// diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/LinkXml/LinkXmlErrorCases.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/LinkXml/LinkXmlErrorCases.cs index 467831cbb69fc..e9021db105d4e 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/LinkXml/LinkXmlErrorCases.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/LinkXml/LinkXmlErrorCases.cs @@ -6,6 +6,7 @@ namespace Mono.Linker.Tests.Cases.LinkXml { [SetupLinkerDescriptorFile ("LinkXmlErrorCases.xml")] [SetupLinkerArgument ("--skip-unresolved", "true")] + [SetupLinkerArgument ("--verbose")] [ExpectedWarning ("IL2001", "TypeWithNoFields", FileName = "LinkXmlErrorCases.xml", SourceLine = 3, SourceColumn = 6)] [ExpectedWarning ("IL2002", "TypeWithNoMethods", FileName = "LinkXmlErrorCases.xml", SourceLine = 4, SourceColumn = 6)] @@ -17,10 +18,15 @@ namespace Mono.Linker.Tests.Cases.LinkXml [ExpectedWarning ("IL2017", "NonExistentProperty", "TypeWithNoProperties", FileName = "LinkXmlErrorCases.xml", SourceLine = 21, SourceColumn = 8)] [ExpectedWarning ("IL2018", "SetOnlyProperty", "TypeWithProperties", FileName = "LinkXmlErrorCases.xml", SourceLine = 25, SourceColumn = 8)] [ExpectedWarning ("IL2019", "GetOnlyProperty", "TypeWithProperties", FileName = "LinkXmlErrorCases.xml", SourceLine = 26, SourceColumn = 8)] - [ExpectedWarning ("IL2025", "Method", Tool.Trimmer, "", FileName = "LinkXmlErrorCases.xml", SourceLine = 39, SourceColumn = 8)] - [ExpectedWarning ("IL2025", "Event", Tool.Trimmer, "", FileName = "LinkXmlErrorCases.xml", SourceLine = 40, SourceColumn = 8)] - [ExpectedWarning ("IL2025", "Field", Tool.Trimmer, "", FileName = "LinkXmlErrorCases.xml", SourceLine = 41, SourceColumn = 8)] - [ExpectedWarning ("IL2025", "Property", Tool.Trimmer, "", FileName = "LinkXmlErrorCases.xml", SourceLine = 42, SourceColumn = 8)] + [LogContains ("Duplicate preserve of 'System.Int32 Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases/TypeWithEverything::Field'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases.TypeWithEverything.TypeWithEverything()'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases.TypeWithEverything.Method()'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'System.EventHandler Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases/TypeWithEverything::Event'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases.TypeWithEverything.Event.add'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases.TypeWithEverything.Event.remove'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'System.Int32 Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases/TypeWithEverything::Property()'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases.TypeWithEverything.Property.get'", ProducedBy = Tool.Trimmer)] + [LogContains ("Duplicate preserve of 'Mono.Linker.Tests.Cases.LinkXml.LinkXmlErrorCases.TypeWithEverything.Property.set'", ProducedBy = Tool.Trimmer)] // NativeAOT doesn't support wildcard * and will skip usages of it, including if they would warn // https://github.com/dotnet/runtime/issues/80466 [ExpectedWarning ("IL2100", Tool.Trimmer, "", FileName = "LinkXmlErrorCases.xml", SourceLine = 50, SourceColumn = 4)]