Refactor to avoid public setters

This commit is contained in:
Michael B. Gale
2023-02-28 15:22:20 +00:00
parent e3762c7f93
commit fea29d5172
10 changed files with 172 additions and 144 deletions

View File

@@ -105,23 +105,30 @@ namespace Semmle.Autobuild.CSharp
// otherwise, we just report that the one script we found didn't work
if (this.autoBuildRule.BuildCommandAutoRule.CandidatePaths.Count() > 1)
{
message = MakeDiagnostic("multiple-build-scripts", "There are multiple potential build scripts");
message.MarkdownMessage =
"CodeQL found multiple potential build scripts for your project and " +
$"attempted to run `{relScriptPath}`, which failed. " +
"This may not be the right build script for your project. " +
$"Set up a [manual build command]({buildCommandDocsUrl}).";
message = new(
this.Options.Language,
"multiple-build-scripts",
"There are multiple potential build scripts",
markdownMessage:
"CodeQL found multiple potential build scripts for your project and " +
$"attempted to run `{relScriptPath}`, which failed. " +
"This may not be the right build script for your project. " +
$"Set up a [manual build command]({buildCommandDocsUrl})."
);
}
else
{
message = MakeDiagnostic("script-failure", "Unable to build project using build script");
message.MarkdownMessage =
"CodeQL attempted to build your project using a script located at " +
$"`{relScriptPath}`, which failed. " +
$"Set up a [manual build command]({buildCommandDocsUrl}).";
message = new(
this.Options.Language,
"script-failure",
"Unable to build project using build script",
markdownMessage:
"CodeQL attempted to build your project using a script located at " +
$"`{relScriptPath}`, which failed. " +
$"Set up a [manual build command]({buildCommandDocsUrl})."
);
}
message.Severity = DiagnosticMessage.TspSeverity.Error;
AddDiagnostic(message);
}
@@ -135,50 +142,63 @@ namespace Semmle.Autobuild.CSharp
// then neither of those rules would've worked
if (this.ProjectsOrSolutionsToBuild.Count == 0)
{
var message = MakeDiagnostic("no-projects-or-solutions", "No project or solutions files found");
message.PlaintextMessage =
"CodeQL could not find any project or solution files in your repository. " +
$"Set up a [manual build command]({buildCommandDocsUrl}).";
message.Severity = DiagnosticMessage.TspSeverity.Error;
AddDiagnostic(message);
this.AddDiagnostic(new(
this.Options.Language,
"no-projects-or-solutions",
"No project or solutions files found",
markdownMessage:
"CodeQL could not find any project or solution files in your repository. " +
$"Set up a [manual build command]({buildCommandDocsUrl})."
));
}
// show a warning if there are projects which are not compatible with .NET Core, in case that is unintentional
else if (foundNotDotNetProjects.Any())
{
var message = MakeDiagnostic("dotnet-incompatible-projects", "Some projects are incompatible with .NET Core");
message.MarkdownMessage =
"CodeQL found some projects which cannot be built with .NET Core:\n" +
autoBuildRule.DotNetRule.NotDotNetProjects.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 5);
message.Severity = DiagnosticMessage.TspSeverity.Warning;
this.AddDiagnostic(new(
this.Options.Language,
"dotnet-incompatible-projects",
"Some projects are incompatible with .NET Core",
severity: DiagnosticMessage.TspSeverity.Warning,
markdownMessage: $"""
CodeQL found some projects which cannot be built with .NET Core:
AddDiagnostic(message);
{autoBuildRule.DotNetRule.NotDotNetProjects.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 5)}
"""
));
}
// report any projects that failed to build with .NET Core
if (autoBuildRule.DotNetRule.FailedProjectsOrSolutions.Any())
{
var message = MakeDiagnostic("dotnet-build-failure", "Some projects or solutions failed to build using .NET Core");
message.MarkdownMessage =
"CodeQL was unable to build the following projects using .NET Core:\n" +
autoBuildRule.DotNetRule.FailedProjectsOrSolutions.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 10) +
$"\nSet up a [manual build command]({buildCommandDocsUrl}).";
message.Severity = DiagnosticMessage.TspSeverity.Error;
this.AddDiagnostic(new(
this.Options.Language,
"dotnet-build-failure",
"Some projects or solutions failed to build using .NET Core",
markdownMessage: $"""
CodeQL was unable to build the following projects using .NET Core:
AddDiagnostic(message);
{autoBuildRule.DotNetRule.FailedProjectsOrSolutions.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 10)}
Set up a [manual build command]({buildCommandDocsUrl}).
"""
));
}
// report any projects that failed to build with MSBuild
if (autoBuildRule.MsBuildRule.FailedProjectsOrSolutions.Any())
{
var message = MakeDiagnostic("msbuild-build-failure", "Some projects or solutions failed to build using MSBuild");
message.MarkdownMessage =
"CodeQL was unable to build the following projects using MSBuild:\n" +
autoBuildRule.MsBuildRule.FailedProjectsOrSolutions.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 10) +
$"\nSet up a [manual build command]({buildCommandDocsUrl}).";
message.Severity = DiagnosticMessage.TspSeverity.Error;
this.AddDiagnostic(new(
this.Options.Language,
"msbuild-build-failure",
"Some projects or solutions failed to build using MSBuild",
markdownMessage: $"""
CodeQL was unable to build the following projects using MSBuild:
AddDiagnostic(message);
{autoBuildRule.MsBuildRule.FailedProjectsOrSolutions.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 10)}
Set up a [manual build command]({buildCommandDocsUrl}).
"""
));
}
}

View File

@@ -26,16 +26,13 @@ namespace Semmle.Autobuild.CSharp
this.SDKName = sdkName;
}
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder) where T : AutobuildOptionsShared
{
var diag = builder.MakeDiagnostic(
$"missing-xamarin-{this.SDKName.ToLower()}-sdk",
$"Missing Xamarin SDK for {this.SDKName}"
);
diag.MarkdownMessage = $"[Configure your workflow]({docsUrl}) for this SDK before running CodeQL.";
return diag;
}
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared => new(
builder.Options.Language,
$"missing-xamarin-{this.SDKName.ToLower()}-sdk",
$"Missing Xamarin SDK for {this.SDKName}",
severity: severity ?? DiagnosticMessage.TspSeverity.Error,
markdownMessage: $"[Configure your workflow]({docsUrl}) for this SDK before running CodeQL."
);
}
public MissingXamarinSdkRule() :
@@ -74,24 +71,23 @@ namespace Semmle.Autobuild.CSharp
this.MissingProjectFiles = new HashSet<string>();
}
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder) where T : AutobuildOptionsShared
{
var diag = builder.MakeDiagnostic(
$"missing-project-files",
$"Missing project files"
);
diag.MarkdownMessage =
"Some project files were not found when CodeQL built your project:\n\n" +
this.MissingProjectFiles.AsEnumerable().Select(p => builder.MakeRelative(p)).ToMarkdownList(MarkdownUtil.CodeFormatter, 5) +
"\n\nThis may lead to subsequent failures. " +
"You can check for common causes for missing project files:\n\n" +
$"- Ensure that the project is built using the {runsOnDocsUrl.ToMarkdownLink("intended operating system")} and that filenames on case-sensitive platforms are correctly specified.\n" +
$"- If your repository uses Git submodules, ensure that those are {checkoutDocsUrl.ToMarkdownLink("checked out")} before the CodeQL action is run.\n" +
"- If you auto-generate some project files as part of your build process, ensure that these are generated before the CodeQL action is run.";
diag.Severity = DiagnosticMessage.TspSeverity.Warning;
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared => new(
builder.Options.Language,
"missing-project-files",
"Missing project files",
severity: severity ?? DiagnosticMessage.TspSeverity.Warning,
markdownMessage: $"""
Some project files were not found when CodeQL built your project:
return diag;
}
{this.MissingProjectFiles.AsEnumerable().Select(p => builder.MakeRelative(p)).ToMarkdownList(MarkdownUtil.CodeFormatter, 5)}
This may lead to subsequent failures. You can check for common causes for missing project files:
- Ensure that the project is built using the {runsOnDocsUrl.ToMarkdownLink("intended operating system")} and that filenames on case-sensitive platforms are correctly specified.
- If your repository uses Git submodules, ensure that those are {checkoutDocsUrl.ToMarkdownLink("checked out")} before the CodeQL action is run.
- If you auto-generate some project files as part of your build process, ensure that these are generated before the CodeQL action is run.
"""
);
}
public MissingProjectFileRule() :

View File

@@ -330,11 +330,9 @@ namespace Semmle.Autobuild.Shared
// if the build succeeded, all diagnostics we captured from the build output should be warnings;
// otherwise they should all be errors
var diagSeverity = buildResult == 0 ? DiagnosticMessage.TspSeverity.Warning : DiagnosticMessage.TspSeverity.Error;
this.DiagnosticClassifier.Results.Select(result => result.ToDiagnosticMessage(this)).ForEach(result =>
{
result.Severity = diagSeverity;
AddDiagnostic(result);
});
this.DiagnosticClassifier.Results
.Select(result => result.ToDiagnosticMessage(this, diagSeverity))
.ForEach(AddDiagnostic);
return buildResult;
}
@@ -344,25 +342,6 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public abstract BuildScript GetBuildScript();
/// <summary>
/// Constructs a standard <see cref="DiagnosticMessage" /> for some message with
/// <see cref="id" /> and a human-friendly <see cref="name" />.
/// </summary>
/// <param name="id">The last part of the message id.</param>
/// <param name="name">The human-friendly description of the message.</param>
/// <returns>The resulting <see cref="DiagnosticMessage" />.</returns>
public DiagnosticMessage MakeDiagnostic(string id, string name)
{
DiagnosticMessage diag = new(new(
$"{this.Options.Language.UpperCaseName.ToLower()}/autobuilder/{id}",
name,
Options.Language.UpperCaseName.ToLower()
));
diag.Visibility.StatusPage = true;
return diag;
}
/// <summary>
/// Produces a diagnostic for the tool status page that we were unable to automatically
@@ -370,16 +349,16 @@ namespace Semmle.Autobuild.Shared
/// can be overriden to implement more specific messages depending on the origin of
/// the failure.
/// </summary>
protected virtual void AutobuildFailureDiagnostic()
{
var message = MakeDiagnostic("autobuild-failure", "Unable to build project");
message.PlaintextMessage =
"We were unable to automatically build your project. " +
"You can manually specify a suitable build command for your project.";
message.Severity = DiagnosticMessage.TspSeverity.Error;
AddDiagnostic(message);
}
protected virtual void AutobuildFailureDiagnostic() => AddDiagnostic(new DiagnosticMessage(
this.Options.Language,
"autobuild-failure",
"Unable to build project",
visibility: new DiagnosticMessage.TspVisibility(statusPage: true),
plaintextMessage: """
We were unable to automatically build your project.
Set up a manual build command.
"""
));
/// <summary>
/// Returns a build script that can be run upon autobuild failure.

View File

@@ -16,8 +16,11 @@ namespace Semmle.Autobuild.Shared
/// <param name="builder">
/// The autobuilder to use for constructing the base <see cref="DiagnosticMessage" />.
/// </param>
/// <param name="severity">
/// An optional severity value which overrides the default severity of the diagnostic.
/// </param>
/// <returns>The <see cref="DiagnosticMessage" /> corresponding to this result.</returns>
DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder) where T : AutobuildOptionsShared;
DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared;
}
public class DiagnosticRule

View File

@@ -52,21 +52,24 @@ namespace Semmle.Util
Error
}
/// <summary>
/// Stores flags indicating where the diagnostic should be displayed.
/// </summary>
public class TspVisibility
{
/// <summary>
/// True if the message should be displayed on the status page (defaults to false).
/// </summary>
public bool? StatusPage { get; set; }
public bool? StatusPage { get; }
/// <summary>
/// True if the message should be counted in the diagnostics summary table printed by
/// <c>codeql database analyze</c> (defaults to false).
/// </summary>
public bool? CLISummaryTable { get; set; }
public bool? CLISummaryTable { get; }
/// <summary>
/// True if the message should be sent to telemetry (defaults to false).
/// </summary>
public bool? Telemetry { get; set; }
public bool? Telemetry { get; }
public TspVisibility(bool? statusPage = null, bool? cliSummaryTable = null, bool? telemetry = null)
{
@@ -76,16 +79,31 @@ namespace Semmle.Util
}
}
/// <summary>
/// Represents source code locations for diagnostic messages.
/// </summary>
public class TspLocation
{
/// <summary>
/// Path to the affected file if appropriate, relative to the source root.
/// </summary>
public string? File { get; set; }
public int? StartLine { get; set; }
public int? StartColumn { get; set; }
public int? EndLine { get; set; }
public int? EndColumn { get; set; }
public string? File { get; }
/// <summary>
/// The line where the range to which the diagnostic relates to starts.
/// </summary>
public int? StartLine { get; }
/// <summary>
/// The column where the range to which the diagnostic relates to starts.
/// </summary>
public int? StartColumn { get; }
/// <summary>
/// The line where the range to which the diagnostic relates to ends.
/// </summary>
public int? EndLine { get; }
/// <summary>
/// The column where the range to which the diagnostic relates to ends.
/// </summary>
public int? EndColumn { get; }
public TspLocation(string? file = null, int? startLine = null, int? startColumn = null, int? endLine = null, int? endColumn = null)
{
@@ -100,20 +118,20 @@ namespace Semmle.Util
/// <summary>
/// ISO 8601 timestamp.
/// </summary>
public string Timestamp { get; set; }
public string Timestamp { get; }
/// <summary>
/// The source of the diagnostic message.
/// </summary>
public TspSource Source { get; set; }
public TspSource Source { get; }
/// <summary>
/// GitHub flavored Markdown formatted message. Should include inline links to any help pages.
/// </summary>
public string? MarkdownMessage { get; set; }
public string? MarkdownMessage { get; }
/// <summary>
/// Plain text message. Used by components where the string processing needed to support
/// Markdown is cumbersome.
/// </summary>
public string? PlaintextMessage { get; set; }
public string? PlaintextMessage { get; }
/// <summary>
/// List of help links intended to supplement <see cref="PlaintextMessage" />.
/// </summary>
@@ -121,11 +139,11 @@ namespace Semmle.Util
/// <summary>
/// SARIF severity.
/// </summary>
public TspSeverity? Severity { get; set; }
public TspSeverity? Severity { get; }
/// <summary>
/// If true, then this message won't be presented to users.
/// </summary>
public bool Internal { get; set; }
public bool Internal { get; }
public TspVisibility Visibility { get; }
public TspLocation Location { get; }
/// <summary>
@@ -133,14 +151,26 @@ namespace Semmle.Util
/// </summary>
public Dictionary<string, object> Attributes { get; }
public DiagnosticMessage(TspSource source)
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
)
{
Timestamp = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture);
Source = source;
HelpLinks = new List<string>();
Visibility = new TspVisibility();
Location = new TspLocation();
Attributes = new Dictionary<string, object>();
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<string>();
this.Attributes = new Dictionary<string, object>();
this.Severity = severity;
this.Visibility = visibility ?? new TspVisibility(statusPage: true);
this.Location = location ?? new TspLocation();
this.Internal = intrnl ?? false;
this.MarkdownMessage = markdownMessage;
this.PlaintextMessage = plaintextMessage;
}
}

View File

@@ -3,7 +3,7 @@
"helpLinks": [],
"internal": false,
"location": {},
"markdownMessage": "CodeQL found some projects which cannot be built with .NET Core:\n- `test.csproj`",
"markdownMessage": "CodeQL found some projects which cannot be built with .NET Core:\n\n- `test.csproj`",
"severity": "warning",
"source": {
"extractorName": "csharp",
@@ -19,7 +19,7 @@
"helpLinks": [],
"internal": false,
"location": {},
"markdownMessage": "CodeQL was unable to build the following projects using MSBuild:\n- `test.csproj`\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"markdownMessage": "CodeQL was unable to build the following projects using MSBuild:\n\n- `test.csproj`\n\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",

View File

@@ -3,7 +3,7 @@
"helpLinks": [],
"internal": false,
"location": {},
"markdownMessage": "CodeQL was unable to build the following projects using MSBuild:\n- `test.sln`\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"markdownMessage": "CodeQL was unable to build the following projects using MSBuild:\n\n- `test.sln`\n\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",

View File

@@ -3,7 +3,7 @@
"helpLinks": [],
"internal": false,
"location": {},
"markdownMessage": "CodeQL was unable to build the following projects using .NET Core:\n- `test.csproj`\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"markdownMessage": "CodeQL was unable to build the following projects using .NET Core:\n\n- `test.csproj`\n\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",
@@ -19,7 +19,7 @@
"helpLinks": [],
"internal": false,
"location": {},
"markdownMessage": "CodeQL was unable to build the following projects using MSBuild:\n- `test.csproj`\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"markdownMessage": "CodeQL was unable to build the following projects using MSBuild:\n\n- `test.csproj`\n\nSet up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",

View File

@@ -19,7 +19,7 @@
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "CodeQL could not find any project or solution files in your repository. Set up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"markdownMessage": "CodeQL could not find any project or solution files in your repository. Set up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",

View File

@@ -1,3 +1,19 @@
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"markdownMessage": "CodeQL could not find any project or solution files in your repository. Set up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/no-projects-or-solutions",
"name": "No project or solutions files found"
},
"visibility": {
"statusPage": true
}
}
{
"attributes": {},
"helpLinks": [],
@@ -14,19 +30,3 @@
"statusPage": true
}
}
{
"attributes": {},
"helpLinks": [],
"internal": false,
"location": {},
"plaintextMessage": "CodeQL could not find any project or solution files in your repository. Set up a [manual build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
"severity": "error",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/no-projects-or-solutions",
"name": "No project or solutions files found"
},
"visibility": {
"statusPage": true
}
}