diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/DiagnosticClassifier.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/DiagnosticClassifier.cs
index 94af39ca959..d6a97dd9722 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/DiagnosticClassifier.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/DiagnosticClassifier.cs
@@ -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) { }
}
+ ///
+ /// A which detects arbitrary build messages from MSBuild, CSC, NETSDK, etc. and
+ /// reports them, up to a limit, as telemetry-only diagnostics.
+ ///
+ public class BuildMessageRule : DiagnosticRule
+ {
+ public class Result : IDiagnosticsResult, IEquatable
+ {
+ ///
+ /// A value indicating whether this a warning or an error.
+ ///
+ public string Type { get; }
+ ///
+ /// The source of the message, such as "MSB" or "NETSDK".
+ ///
+ public string Source { get; }
+ ///
+ /// The numeric id of the message.
+ ///
+ public int Code { get; }
+ ///
+ /// The message contents.
+ ///
+ 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(Autobuilder 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);
+ }
+ }
+
+ ///
+ /// The maximum number of diagnostics we should emit for this rule.
+ ///
+ private const int maxDiagnostics = 10;
+ ///
+ /// The number of diagnostics this rule has emitted so far.
+ ///
+ private int diagnostics = 0;
+
+ public BuildMessageRule() : base("(?error|warning) (?[A-Z]+)(?\\d+): (?.*)") { }
+
+ 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().Any(d => d.Equals(result)))
+ {
+ classifier.Results.Add(result);
+ this.diagnostics++;
+ }
+ }
+ }
+ }
+
public class DiagnosticClassifier
{
private readonly List rules;
@@ -66,6 +174,8 @@ namespace Semmle.Autobuild.Shared
{
this.rules = new List();
this.Results = new List();
+
+ this.AddRule(new BuildMessageRule());
}
///
diff --git a/csharp/ql/integration-tests/all-platforms/diag_dotnet_incompatible/diagnostics.expected b/csharp/ql/integration-tests/all-platforms/diag_dotnet_incompatible/diagnostics.expected
index 91a9bbbb267..37174a3bd03 100644
--- a/csharp/ql/integration-tests/all-platforms/diag_dotnet_incompatible/diagnostics.expected
+++ b/csharp/ql/integration-tests/all-platforms/diag_dotnet_incompatible/diagnostics.expected
@@ -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.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
+ }
+}
diff --git a/csharp/ql/integration-tests/all-platforms/diag_missing_project_files/diagnostics.expected b/csharp/ql/integration-tests/all-platforms/diag_missing_project_files/diagnostics.expected
index da2b3d93941..5c12599bd35 100644
--- a/csharp/ql/integration-tests/all-platforms/diag_missing_project_files/diagnostics.expected
+++ b/csharp/ql/integration-tests/all-platforms/diag_missing_project_files/diagnostics.expected
@@ -34,3 +34,35 @@
"telemetry": true
}
}
+{
+ "attributes": {},
+ "helpLinks": [],
+ "internal": false,
+ "location": {},
+ "plaintextMessage": "The project file \"/Example.Test.csproj\" was not found. [/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 \"/Example.csproj\" was not found. [/test.sln]",
+ "severity": "error",
+ "source": {
+ "extractorName": "csharp",
+ "id": "csharp/autobuilder/msb-3202",
+ "name": "MSB3202"
+ },
+ "visibility": {
+ "telemetry": true
+ }
+}
diff --git a/csharp/ql/integration-tests/all-platforms/diag_missing_xamarin_sdk/diagnostics.expected b/csharp/ql/integration-tests/all-platforms/diag_missing_xamarin_sdk/diagnostics.expected
index 0becfa08cee..c3c4ef179ee 100644
--- a/csharp/ql/integration-tests/all-platforms/diag_missing_xamarin_sdk/diagnostics.expected
+++ b/csharp/ql/integration-tests/all-platforms/diag_missing_xamarin_sdk/diagnostics.expected
@@ -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.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
+ }
+}
diff --git a/csharp/ql/integration-tests/all-platforms/msbuild/diagnostics.expected b/csharp/ql/integration-tests/all-platforms/msbuild/diagnostics.expected
new file mode 100644
index 00000000000..70a2ed8b1aa
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/msbuild/diagnostics.expected
@@ -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
+ }
+}