using System.Collections.Generic; using Microsoft.CodeAnalysis; using Semmle.Util.Logging; namespace Semmle.Extraction { /// /// Implementation of the main extractor state. /// public class Extractor { public bool Standalone { get; private set; } /// /// Creates a new extractor instance for one compilation unit. /// /// If the extraction is standalone. /// The name of the output DLL/EXE, or null if not specified (standalone extraction). /// The object used for logging. /// The object used for path transformations. public Extractor(bool standalone, string outputPath, ILogger logger, PathTransformer pathTransformer) { Standalone = standalone; OutputPath = outputPath; Logger = logger; PathTransformer = pathTransformer; } // Limit the number of error messages in the log file // to handle pathological cases. private const int maxErrors = 1000; private readonly object mutex = new object(); public void Message(Message msg) { lock (mutex) { if (msg.Severity == Severity.Error) { ++Errors; if (Errors == maxErrors) { Logger.Log(Severity.Info, " Stopping logging after {0} errors", Errors); } } if (Errors >= maxErrors) { return; } Logger.Log(msg.Severity, $" {msg.ToLogString()}"); } } // Roslyn framework has no apparent mechanism to associate assemblies with their files. // So this lookup table needs to be populated. private readonly Dictionary referenceFilenames = new Dictionary(); public void SetAssemblyFile(string assembly, string file) { referenceFilenames[assembly] = file; } public string GetAssemblyFile(string assembly) { return referenceFilenames[assembly]; } public int Errors { get; private set; } private readonly ISet missingTypes = new SortedSet(); private readonly ISet missingNamespaces = new SortedSet(); public void MissingType(string fqn, bool fromSource) { if (fromSource) { lock (mutex) missingTypes.Add(fqn); } } public void MissingNamespace(string fqdn, bool fromSource) { if (fromSource) { lock (mutex) missingNamespaces.Add(fqdn); } } public IEnumerable MissingTypes => missingTypes; public IEnumerable MissingNamespaces => missingNamespaces; public string OutputPath { get; private set; } public ILogger Logger { get; private set; } public static string Version => $"{ThisAssembly.Git.BaseTag} ({ThisAssembly.Git.Sha})"; public PathTransformer PathTransformer { get; } } }