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} "); 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 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"; } } }