platform-compat/pns-scan/Program.cs

170 строки
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Cci;
using Microsoft.Cci.Extensions;
namespace NotImplementedScanner
{
internal static class Program
{
private static int Main(string[] args)
{
if (args.Length != 2)
{
var toolName = Path.GetFileNameWithoutExtension(Environment.GetCommandLineArgs()[0]);
Console.Error.WriteLine($"Usage: {toolName} <directory-or-binary> <out-path>");
return 1;
}
var inputPath = args[0];
var outputPath = args[1];
var isFile = File.Exists(inputPath);
var isDirectory = Directory.Exists(inputPath);
if (!isFile && !isDirectory)
{
Console.Error.WriteLine($"ERROR: '{inputPath}' must be a file or directory.");
return 1;
}
try
{
Run(inputPath, outputPath);
return 0;
}
catch (Exception ex)
{
Console.Error.WriteLine($"ERROR: {ex.Message}");
return 1;
}
}
private static void Run(string inputPath, string outputPath)
{
using (var textWriter = new StreamWriter(outputPath))
{
textWriter.Write("DocId");
textWriter.Write(",");
textWriter.Write("Namespace");
textWriter.Write(",");
textWriter.Write("Type");
textWriter.Write(",");
textWriter.Write("Member");
textWriter.WriteLine();
foreach (var assembly in LoadPaths(inputPath))
AnalyzeAssembly(textWriter, assembly);
}
}
private static void AnalyzeAssembly(StreamWriter textWriter, IAssembly assembly)
{
foreach (var type in assembly.GetAllTypes())
AnalyzeType(textWriter, type);
}
private static void AnalyzeType(StreamWriter textWriter, INamedTypeDefinition type)
{
if (!type.IsVisibleOutsideAssembly())
return;
foreach (var item in type.Members)
AnalyzeMember(textWriter, item);
}
private static void AnalyzeMember(StreamWriter textWriter, ITypeDefinitionMember item)
{
if (!item.IsVisibleOutsideAssembly())
return;
if (ThrowsPlaformNotSupported(item))
{
textWriter.Write("\"");
textWriter.Write(item.DocId());
textWriter.Write("\",");
textWriter.Write(item.ContainingTypeDefinition.GetNamespaceName());
textWriter.Write(",");
textWriter.Write(item.ContainingTypeDefinition.GetTypeName(false));
textWriter.Write(",");
textWriter.Write(item.Name.Value);
textWriter.WriteLine();
}
}
private static void AnalyzeMember(StreamWriter textWriter, INamedTypeDefinition type)
{
}
private static IEnumerable<IAssembly> LoadPaths(string input)
{
var inputPaths = HostEnvironment.SplitPaths(input);
var filePaths = HostEnvironment.GetFilePaths(inputPaths, SearchOption.AllDirectories).ToArray();
return HostEnvironment.LoadAssemblySet(filePaths);
}
private static bool ThrowsPlaformNotSupported(ITypeDefinitionMember item)
{
if (item is IMethodDefinition m)
{
if (m.IsPropertyOrEventAccessor())
return false;
return ThrowsPlaformNotSupported(m);
}
else if (item is IPropertyDefinition p)
{
return p.Accessors.Any(a => ThrowsPlaformNotSupported(a.ResolvedTypeDefinitionMember));
}
else if (item is IEventDefinition e)
{
return e.Accessors.Any(a => ThrowsPlaformNotSupported(a.ResolvedTypeDefinitionMember));
}
else if (item is IFieldDefinition || item is ITypeDefinition)
{
// Ignore
return false;
}
else
{
throw new NotImplementedException($"Unexpected type member: {item.FullName()} ({item.GetApiKind()})");
}
}
private static bool ThrowsPlaformNotSupported(IMethodDefinition method)
{
if (method.IsAbstract)
return false;
IMethodReference constructorReference = null;
foreach (var op in method.Body.Operations)
{
switch (op.OperationCode)
{
case OperationCode.Newobj:
constructorReference = op.Value as IMethodReference;
break;
case OperationCode.Nop:
// Ignore
break;
case OperationCode.Throw:
if (constructorReference != null)
return IsPlatformNotSupported(constructorReference);
break;
default:
constructorReference = null;
break;
}
}
return false;
}
private static bool IsPlatformNotSupported(IMethodReference constructorReference)
{
return constructorReference.ContainingType.FullName() == "System.PlatformNotSupportedException";
}
}
}