|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Mono.Cecil;
namespace Mono.Linker
{
public class WarningSuppressionWriter
{
private readonly Dictionary<AssemblyNameDefinition, HashSet<(int Code, IMemberDefinition Member)>> _warnings;
private readonly FileOutputKind _fileOutputKind;
readonly LinkContext _context;
public WarningSuppressionWriter (LinkContext context, FileOutputKind fileOutputKind = FileOutputKind.CSharp)
{
_warnings = new Dictionary<AssemblyNameDefinition, HashSet<(int, IMemberDefinition)>> ();
_fileOutputKind = fileOutputKind;
_context = context;
}
public bool IsEmpty => _warnings.Count == 0;
public void AddWarning (int code, ICustomAttributeProvider provider)
{
// We don't have a targeted suppression mechanism for
// warnings from assembly-level attributes.
if (provider is not IMemberDefinition memberDefinition)
return;
if (UnconditionalSuppressMessageAttributeState.GetModuleFromProvider (provider) is not ModuleDefinition module)
return;
var assemblyName = module.Assembly.Name;
if (!_warnings.TryGetValue (assemblyName, out var warnings)) {
warnings = new HashSet<(int, IMemberDefinition)> ();
_warnings.Add (assemblyName, warnings);
}
warnings.Add ((code, memberDefinition));
}
public void OutputSuppressions (string directory)
{
foreach (var assemblyName in _warnings.Keys) {
if (_fileOutputKind == FileOutputKind.Xml) {
OutputSuppressionsXmlFormat (assemblyName, directory);
} else {
OutputSuppressionsCSharpFormat (assemblyName, directory);
}
}
}
void OutputSuppressionsXmlFormat (AssemblyNameDefinition assemblyName, string directory)
{
var xmlTree = new XElement ("linker");
var xmlAssembly = new XElement ("assembly", new XAttribute ("fullname", assemblyName.FullName));
xmlTree.Add (xmlAssembly);
foreach (var warning in GetListOfWarnings (assemblyName)) {
xmlAssembly.Add (
new XElement ("attribute",
new XAttribute ("fullname", "System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute"),
new XElement ("argument", Constants.ILLink),
new XElement ("argument", $"IL{warning.Code}"),
new XElement ("property", new XAttribute ("name", UnconditionalSuppressMessageAttributeState.ScopeProperty),
GetWarningSuppressionScopeString (warning.MemberDocumentationSignature)),
new XElement ("property", new XAttribute ("name", UnconditionalSuppressMessageAttributeState.TargetProperty),
warning.MemberDocumentationSignature)));
}
XDocument xdoc = new XDocument (xmlTree);
using (var xw = XmlWriter.Create (Path.Combine (directory, $"{assemblyName.Name}.WarningSuppressions.xml"),
new XmlWriterSettings { Indent = true })) {
xdoc.Save (xw);
}
}
void OutputSuppressionsCSharpFormat (AssemblyNameDefinition assemblyName, string directory)
{
using (var sw = new StreamWriter (Path.Combine (directory, $"{assemblyName.Name}.WarningSuppressions.cs"))) {
StringBuilder sb = new StringBuilder ("using System.Diagnostics.CodeAnalysis;").AppendLine ().AppendLine ();
foreach (var warning in GetListOfWarnings (assemblyName)) {
sb.Append ("[assembly: UnconditionalSuppressMessage (\"")
.Append (Constants.ILLink)
.Append ("\", \"IL").Append (warning.Code)
.Append ("\", Scope = \"").Append (GetWarningSuppressionScopeString (warning.MemberDocumentationSignature))
.Append ("\", Target = \"").Append (warning.MemberDocumentationSignature)
.AppendLine ("\")]");
}
sw.Write (sb.ToString ());
}
}
List<(int Code, string MemberDocumentationSignature)> GetListOfWarnings (AssemblyNameDefinition assemblyName)
{
List<(int Code, string MemberDocumentationSignature)> listOfWarnings = new List<(int Code, string MemberDocumentationSignature)> ();
StringBuilder sb = new StringBuilder ();
foreach (var warning in _warnings[assemblyName].ToList ()) {
DocumentationSignatureGenerator.VisitMember (warning.Member, sb, _context);
listOfWarnings.Add ((warning.Code, sb.ToString ()));
sb.Clear ();
}
listOfWarnings.Sort ();
return listOfWarnings;
}
static string GetWarningSuppressionScopeString (string memberDocumentationSignature)
{
if (memberDocumentationSignature.StartsWith (DocumentationSignatureGenerator.TypePrefix))
return "type";
return "member";
}
public enum FileOutputKind
{
CSharp,
Xml
};
}
}
|