using System; using System.Collections.Generic; using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; namespace Semmle.Util { /// /// Represents diagnostic messages for the tool status page. /// public class DiagnosticMessage { /// /// Represents sources of diagnostic messages. /// public class TspSource { /// /// An identifier under which it makes sense to group this diagnostic message. /// This is used to build the SARIF reporting descriptor object. /// public string Id { get; } /// /// Display name for the ID. This is used to build the SARIF reporting descriptor object. /// public string Name { get; } /// /// Name of the CodeQL extractor. This is used to identify which tool component the reporting descriptor object should be nested under in SARIF. /// public string? ExtractorName { get; } public TspSource(string id, string name, string? extractorName = null) { Id = id; Name = name; ExtractorName = extractorName; } } /// /// Enumerates severity levels for diagnostics. /// [JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy))] public enum TspSeverity { Unknown, Note, Warning, Error } /// /// Stores flags indicating where the diagnostic should be displayed. /// public class TspVisibility { /// /// A read-only instance of which indicates that the /// diagnostic should be used in all supported locations. /// public static readonly TspVisibility All = new(true, true, true); /// /// True if the message should be displayed on the status page (defaults to false). /// public bool? StatusPage { get; } /// /// True if the message should be counted in the diagnostics summary table printed by /// codeql database analyze (defaults to false). /// public bool? CLISummaryTable { get; } /// /// True if the message should be sent to telemetry (defaults to false). /// public bool? Telemetry { get; } public TspVisibility(bool? statusPage = null, bool? cliSummaryTable = null, bool? telemetry = null) { this.StatusPage = statusPage; this.CLISummaryTable = cliSummaryTable; this.Telemetry = telemetry; } } /// /// Represents source code locations for diagnostic messages. /// public class TspLocation { /// /// Path to the affected file if appropriate, relative to the source root. /// public string? File { get; } /// /// The line where the range to which the diagnostic relates to starts. /// public int? StartLine { get; } /// /// The column where the range to which the diagnostic relates to starts. /// public int? StartColumn { get; } /// /// The line where the range to which the diagnostic relates to ends. /// public int? EndLine { get; } /// /// The column where the range to which the diagnostic relates to ends. /// public int? EndColumn { get; } public TspLocation(string? file = null, int? startLine = null, int? startColumn = null, int? endLine = null, int? endColumn = null) { this.File = file; this.StartLine = startLine; this.StartColumn = startColumn; this.EndLine = endLine; this.EndColumn = endColumn; } } /// /// ISO 8601 timestamp. /// public string Timestamp { get; } /// /// The source of the diagnostic message. /// public TspSource Source { get; } /// /// GitHub flavored Markdown formatted message. Should include inline links to any help pages. /// public string? MarkdownMessage { get; } /// /// Plain text message. Used by components where the string processing needed to support /// Markdown is cumbersome. /// public string? PlaintextMessage { get; } /// /// List of help links intended to supplement . /// public List HelpLinks { get; } /// /// SARIF severity. /// public TspSeverity? Severity { get; } /// /// If true, then this message won't be presented to users. /// public bool Internal { get; } public TspVisibility Visibility { get; } public TspLocation? Location { get; } /// /// Structured metadata about the diagnostic message. /// public Dictionary Attributes { get; } public DiagnosticMessage( Language language, string id, string name, string? markdownMessage = null, string? plaintextMessage = null, TspVisibility? visibility = null, TspLocation? location = null, TspSeverity? severity = TspSeverity.Error, DateTime? timestamp = null, bool? intrnl = null ) { this.Source = new TspSource( id: $"{language.UpperCaseName.ToLower()}/autobuilder/{id}", name: name, extractorName: language.UpperCaseName.ToLower() ); this.Timestamp = (timestamp ?? DateTime.UtcNow).ToString("o", CultureInfo.InvariantCulture); this.HelpLinks = new List(); this.Attributes = new Dictionary(); this.Severity = severity; this.Visibility = visibility ?? TspVisibility.All; this.Location = location; this.Internal = intrnl ?? false; this.MarkdownMessage = markdownMessage; this.PlaintextMessage = plaintextMessage; } } }