Compare commits

...

1 Commits

Author SHA1 Message Date
Michael B. Gale
21710fa597 Add diagnostics rule for arbitrary build messages 2023-03-08 14:27:23 +00:00
5 changed files with 222 additions and 0 deletions

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Semmle.Util;
@@ -57,6 +59,112 @@ namespace Semmle.Autobuild.Shared
public virtual void Fire(DiagnosticClassifier classifier, Match match) { }
}
/// <summary>
/// A <see cref="DiagnosticRule" /> which detects arbitrary build messages from MSBuild, CSC, NETSDK, etc. and
/// reports them, up to a limit, as telemetry-only diagnostics.
/// </summary>
public class BuildMessageRule : DiagnosticRule
{
public class Result : IDiagnosticsResult, IEquatable<Result>
{
/// <summary>
/// A value indicating whether this a warning or an error.
/// </summary>
public string Type { get; }
/// <summary>
/// The source of the message, such as "MSB" or "NETSDK".
/// </summary>
public string Source { get; }
/// <summary>
/// The numeric id of the message.
/// </summary>
public int Code { get; }
/// <summary>
/// The message contents.
/// </summary>
public string Message { get; }
public Result(string type, string source, int code, string message)
{
this.Type = type;
this.Source = source;
this.Code = code;
this.Message = message;
}
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared => new(
builder.Options.Language,
$"{this.Source.ToLower()}-{this.Code}",
$"{this.Source.ToUpper()}{this.Code}",
plaintextMessage: this.Message,
severity:
this.Type.Equals("error") ?
DiagnosticMessage.TspSeverity.Error :
DiagnosticMessage.TspSeverity.Warning,
// the messages we capture here are visible in the build log, so there is no need
// to show them other than in telemetry
visibility: new(telemetry: true)
);
public bool Equals(Result? x)
{
return x is not null &&
x.Code == this.Code &&
x.Type == this.Type &&
x.Source == this.Source &&
x.Message == this.Message;
}
public override bool Equals(object? obj)
{
return obj is Result && this.Equals(obj);
}
public override int GetHashCode()
{
return HashCode.Combine(this.Type, this.Source, this.Code, this.Message);
}
}
/// <summary>
/// The maximum number of diagnostics we should emit for this rule.
/// </summary>
private const int maxDiagnostics = 10;
/// <summary>
/// The number of diagnostics this rule has emitted so far.
/// </summary>
private int diagnostics = 0;
public BuildMessageRule() : base("(?<type>error|warning) (?<source>[A-Z]+)(?<code>\\d+): (?<message>.*)") { }
public override void Fire(DiagnosticClassifier classifier, Match match)
{
if (!match.Groups.TryGetValue("type", out var type))
throw new ArgumentException("Expected regular expression match to contain type");
if (!match.Groups.TryGetValue("source", out var source))
throw new ArgumentException("Expected regular expression match to contain source");
if (!match.Groups.TryGetValue("code", out var codeStr))
throw new ArgumentException("Expected regular expression match to contain code");
if (!match.Groups.TryGetValue("message", out var message))
throw new ArgumentException("Expected regular expression match to contain message");
if (!int.TryParse(codeStr.Value, out var code))
throw new ArgumentException("Expected code to be numeric");
// check that we have not yet exceeded our limit for emitting diagnostics
if (this.diagnostics < BuildMessageRule.maxDiagnostics)
{
var result = new Result(type.Value, source.Value, code, message.Value);
// add this result if we don't already have an identical one
if (!classifier.Results.OfType<Result>().Any(d => d.Equals(result)))
{
classifier.Results.Add(result);
this.diagnostics++;
}
}
}
}
public class DiagnosticClassifier
{
private readonly List<DiagnosticRule> rules;
@@ -66,6 +174,8 @@ namespace Semmle.Autobuild.Shared
{
this.rules = new List<DiagnosticRule>();
this.Results = new List<IDiagnosticsResult>();
this.AddRule(new BuildMessageRule());
}
/// <summary>

View File

@@ -34,3 +34,35 @@
"telemetry": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "The reference assemblies for .NETFramework,Version=v4.0 were not found. To resolve this, install the Developer Pack (SDK/Targeting Pack) for this framework version or retarget your application. You can download .NET Framework Developer Packs at https://aka.ms/msbuild/developerpacks [<test-root-directory>/test.csproj]",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msb-3644",
"name": "MSB3644"
},
"visibility": {
"telemetry": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "Too many project files specified",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msbuild-4",
"name": "MSBUILD4"
},
"visibility": {
"telemetry": true
}
}

View File

@@ -34,3 +34,35 @@
"telemetry": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "The project file \"<test-root-directory>/Example.Test.csproj\" was not found. [<test-root-directory>/test.sln]",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msb-3202",
"name": "MSB3202"
},
"visibility": {
"telemetry": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "The project file \"<test-root-directory>/Example.csproj\" was not found. [<test-root-directory>/test.sln]",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msb-3202",
"name": "MSB3202"
},
"visibility": {
"telemetry": true
}
}

View File

@@ -52,3 +52,35 @@
"telemetry": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "The imported project \"/usr/local/share/dotnet/sdk/7.0.102/Xamarin/iOS/Xamarin.iOS.CSharp.targets\" was not found. Confirm that the expression in the Import declaration \"/usr/local/share/dotnet/sdk/7.0.102/Xamarin/iOS/Xamarin.iOS.CSharp.targets\" is correct, and that the file exists on disk. [<test-root-directory>/test.csproj]",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msb-4019",
"name": "MSB4019"
},
"visibility": {
"telemetry": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "Too many project files specified",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msbuild-4",
"name": "MSBUILD4"
},
"visibility": {
"telemetry": true
}
}

View File

@@ -0,0 +1,16 @@
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "Too many project files specified",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/msbuild-4",
"name": "MSBUILD4"
},
"visibility": {
"telemetry": true
}
}