From cb652052e911908935ac49b80b79c9436b13019b Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 25 Sep 2024 05:26:33 -0700 Subject: [PATCH 1/5] Remove dynamic conversion from explicit reference conversions (#1180) --- standard/conversions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/conversions.md b/standard/conversions.md index 6502b94ac..2af5d9733 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -470,7 +470,7 @@ The explicit nullable conversions are those nullable conversions ([§10.6.1](con The explicit reference conversions are: -- From object and dynamic to any other *reference_type*. +- From object to any other *reference_type*. - From any *class_type* `S` to any *class_type* `T`, provided `S` is a base class of `T`. - From any *class_type* `S` to any *interface_type* `T`, provided `S` is not sealed and provided `S` does not implement `T`. - From any *interface_type* `S` to any *class_type* `T`, provided `T` is not sealed or provided `T` implements `S`. From dbb527cc7f0a7cd5dee58c2f980b8dd92936b755 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:44:12 -0400 Subject: [PATCH 2/5] Bump xunit in /tools in the dotnet group across 1 directory (#1182) Bumps the dotnet group with 1 update in the /tools directory: [xunit](https://github.com/xunit/xunit). Updates `xunit` from 2.9.0 to 2.9.2 - [Commits](https://github.com/xunit/xunit/compare/v2-2.9.0...v2-2.9.2) --- updated-dependencies: - dependency-name: xunit dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj b/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj index 1c126ef5b..0cd0e114f 100644 --- a/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj +++ b/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj @@ -16,7 +16,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From a23bd55f3c081e4f086909cca3dd8b1741f55f67 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 3 Oct 2024 12:51:33 -0400 Subject: [PATCH 3/5] Remove explicit dynamic conversions (#1184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove §10.3.8 on explicit dynamic conversions. §10.2.10 already defines these conversions as *implicit* dynamic conversions. Therefore, none of these conversions would apply. Remove references to the removed section. --- standard/conversions.md | 44 +---------------------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index 2af5d9733..d886586be 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -72,7 +72,7 @@ The pre-defined implicit conversions always succeed and never cause exceptions t For the purposes of conversion, the types `object` and `dynamic` are identity convertible ([§10.2.2](conversions.md#1022-identity-conversion)). -However, dynamic conversions ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions) and [§10.3.8](conversions.md#1038-explicit-dynamic-conversions)) apply only to expressions of type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)). +However, dynamic conversions ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions)) apply only to expressions of type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)). ### 10.2.2 Identity conversion @@ -394,7 +394,6 @@ The following conversions are classified as explicit conversions: - Explicit interface conversions - Unboxing conversions ([§10.3.7](conversions.md#1037-unboxing-conversions)) - Explicit type parameter conversions ([§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters)) -- Explicit dynamic conversions ([§10.3.8](conversions.md#1038-explicit-dynamic-conversions)) - User-defined explicit conversions ([§10.3.10](conversions.md#10310-user-defined-explicit-conversions)) Explicit conversions can occur in cast expressions ([§12.9.7](expressions.md#1297-cast-expressions)). @@ -539,47 +538,6 @@ For an unboxing conversion to a given *non_nullable_value_type* to succeed at ru For an unboxing conversion to a given *nullable_value_type* to succeed at run-time, the value of the source operand shall be either null or a reference to a boxed value of the underlying *non_nullable_value_type* of the *nullable_value_type*. If the source operand is a reference to an incompatible object, a `System.InvalidCastException` is thrown. -### 10.3.8 Explicit dynamic conversions - -An explicit dynamic conversion exists from an expression of type `dynamic` to any type `T`. The conversion is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)), which means that an explicit conversion will be sought at run-time from the run-time type of the expression to `T`. If no conversion is found, a run-time exception is thrown. - -If dynamic binding of the conversion is not desired, the expression can be first converted to `object`, and then to the desired type. - -> *Example*: Assume the following class is defined: -> -> -> -> ```csharp -> class C -> { -> int i; -> -> public C(int i) -> { -> this.i = i; -> } -> -> public static explicit operator C(string s) -> { -> return new C(int.Parse(s)); -> } -> } -> ``` -> -> The following illustrates explicit dynamic conversions: -> -> -> ```csharp -> object o = "1"; -> dynamic d = "2"; -> var c1 = (C)o; // Compiles, but explicit reference conversion fails -> var c2 = (C)d; // Compiles and user defined conversion succeeds -> ``` -> -> The best conversion of `o` to `C` is found at compile-time to be an explicit reference conversion. This fails at run-time, because `"1"` is not in fact a `C`. The conversion of `d` to `C` however, as an explicit dynamic conversion, is suspended to run-time, where a user defined conversion from the run-time type of `d` (`string`) to `C` is found, and succeeds. -> -> *end example* - ### 10.3.9 Explicit conversions involving type parameters For a *type_parameter* `T` that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following explicit reference conversions ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exist: From f0957869b86c88320bce65680b69e0777f89e0f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:56:32 -0400 Subject: [PATCH 4/5] [create-pull-request] automated change (#1185) Co-authored-by: BillWagner --- standard/README.md | 5 ++--- standard/conversions.md | 12 ++++++------ standard/types.md | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/standard/README.md b/standard/README.md index f61d5a917..54bda09ba 100644 --- a/standard/README.md +++ b/standard/README.md @@ -212,9 +212,8 @@ - [§10.3.5](conversions.md#1035-explicit-reference-conversions) Explicit reference conversions - [§10.3.6](conversions.md#1036-explicit-tuple-conversions) Explicit tuple conversions - [§10.3.7](conversions.md#1037-unboxing-conversions) Unboxing conversions - - [§10.3.8](conversions.md#1038-explicit-dynamic-conversions) Explicit dynamic conversions - - [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters) Explicit conversions involving type parameters - - [§10.3.10](conversions.md#10310-user-defined-explicit-conversions) User-defined explicit conversions + - [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters) Explicit conversions involving type parameters + - [§10.3.9](conversions.md#1039-user-defined-explicit-conversions) User-defined explicit conversions - [§10.4](conversions.md#104-standard-conversions) Standard conversions - [§10.4.1](conversions.md#1041-general) General - [§10.4.2](conversions.md#1042-standard-implicit-conversions) Standard implicit conversions diff --git a/standard/conversions.md b/standard/conversions.md index d886586be..494312915 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -393,8 +393,8 @@ The following conversions are classified as explicit conversions: - Explicit reference conversions ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) - Explicit interface conversions - Unboxing conversions ([§10.3.7](conversions.md#1037-unboxing-conversions)) -- Explicit type parameter conversions ([§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters)) -- User-defined explicit conversions ([§10.3.10](conversions.md#10310-user-defined-explicit-conversions)) +- Explicit type parameter conversions ([§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters)) +- User-defined explicit conversions ([§10.3.9](conversions.md#1039-user-defined-explicit-conversions)) Explicit conversions can occur in cast expressions ([§12.9.7](expressions.md#1297-cast-expressions)). @@ -487,7 +487,7 @@ The explicit reference conversions are: - If `Xᵢ` is invariant, then `Sᵢ` is identical to `Tᵢ`. - If `Xᵢ` is covariant, then there is an identity conversion, implicit reference conversion or explicit reference conversion from `Sᵢ` to `Tᵢ`. - If `Xᵢ` is contravariant, then `Sᵢ` and `Tᵢ` are either identical or both reference types. -- Explicit conversions involving type parameters that are known to be reference types. For more details on explicit conversions involving type parameters, see [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters). +- Explicit conversions involving type parameters that are known to be reference types. For more details on explicit conversions involving type parameters, see [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters). The explicit reference conversions are those conversions between *reference_type*s that require run-time checks to ensure they are correct. @@ -510,7 +510,7 @@ An unboxing conversion permits a *reference_type* to be explicitly converted to - From any *interface_type* `I` to any *non_nullable_value_type* where there is an unboxing conversion from an *interface_type* `I₀` to the *non_nullable_value-type* and an identity conversion from `I` to `I₀`. - From any *interface_type* `I` to any *non_nullable_value_type* where there is an unboxing conversion from an *interface_type* `I₀` to the *non_nullable_value_type* and either either `I₀` is variance_convertible to `I` or `I` is variance-convertible to `I₀` ([§18.2.3.3](interfaces.md#18233-variance-conversion)). - From any *reference_type* to any *nullable_value_type* where there is an unboxing conversion from *reference_type* to the underlying *non_nullable_value_type* of the *nullable_value_type*. -- From a type parameter which is not known to be a value type to any type such that the conversion is permitted by [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters). +- From a type parameter which is not known to be a value type to any type such that the conversion is permitted by [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters). An unboxing operation to a *non_nullable_value_type* consists of first checking that the object instance is a boxed value of the given *non_nullable_value_type*, and then copying the value out of the instance. @@ -538,7 +538,7 @@ For an unboxing conversion to a given *non_nullable_value_type* to succeed at ru For an unboxing conversion to a given *nullable_value_type* to succeed at run-time, the value of the source operand shall be either null or a reference to a boxed value of the underlying *non_nullable_value_type* of the *nullable_value_type*. If the source operand is a reference to an incompatible object, a `System.InvalidCastException` is thrown. -### 10.3.9 Explicit conversions involving type parameters +### 10.3.8 Explicit conversions involving type parameters For a *type_parameter* `T` that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following explicit reference conversions ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exist: @@ -593,7 +593,7 @@ The above rules do not permit a direct explicit conversion from an unconstrained > > *end example* -### 10.3.10 User-defined explicit conversions +### 10.3.9 User-defined explicit conversions A user-defined explicit conversion consists of an optional standard explicit conversion, followed by execution of a user-defined implicit or explicit conversion operator, followed by another optional standard explicit conversion. The exact rules for evaluating user-defined explicit conversions are described in [§10.5.5](conversions.md#1055-user-defined-explicit-conversions). diff --git a/standard/types.md b/standard/types.md index 5e860a075..ca3b6974a 100644 --- a/standard/types.md +++ b/standard/types.md @@ -609,7 +609,7 @@ Since a type parameter can be instantiated with many different type arguments, t > > - A type parameter cannot be used directly to declare a base class ([§15.2.4.2](classes.md#15242-base-classes)) or interface ([§18.2.4](interfaces.md#1824-base-interfaces)). > - The rules for member lookup on type parameters depend on the constraints, if any, applied to the type parameter. They are detailed in [§12.5](expressions.md#125-member-lookup). -> - The available conversions for a type parameter depend on the constraints, if any, applied to the type parameter. They are detailed in [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters) and [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters). +> - The available conversions for a type parameter depend on the constraints, if any, applied to the type parameter. They are detailed in [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters) and [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters). > - The literal `null` cannot be converted to a type given by a type parameter, except if the type parameter is known to be a reference type ([§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters)). However, a default expression ([§12.8.20](expressions.md#12820-default-value-expressions)) can be used instead. In addition, a value with a type given by a type parameter *can* be compared with null using `==` and `!=` ([§12.12.7](expressions.md#12127-reference-type-equality-operators)) unless the type parameter has the value type constraint. > - A `new` expression ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) can only be used with a type parameter if the type parameter is constrained by a *constructor_constraint* or the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)). > - A type parameter cannot be used anywhere within an attribute. From 0e44170629c5acc9456cdca3c7f6065c92fbcab3 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 4 Oct 2024 10:26:25 -0400 Subject: [PATCH 5/5] Add status checks to the test runner (#1179) * Add status checks to the test runner Contributes to #1125 Replace the existing logging code with the utility class that writes to the console, and adds a status check record. Log failures as status checks, but status is only to the output console. * Update tools/Utilities/StatusCheckLogger.cs * Apply suggestions from code review * Feedback from code review A bit of refactoring. --- .github/workflows/renumber-sections.yaml | 1 + .github/workflows/test-examples.yaml | 5 ++ .github/workflows/word-converter.yaml | 1 + .gitignore | 3 + tools/ExampleTester/ExampleTester.csproj | 1 + tools/ExampleTester/GeneratedExample.cs | 35 ++++++----- tools/ExampleTester/Program.cs | 17 +++++- tools/MarkdownConverter/Spec/Reporter.cs | 4 +- .../ReferenceUpdateProcessor.cs | 2 +- .../TocSectionNumberBuilder.cs | 2 +- tools/Utilities/StatusCheckLogger.cs | 58 ++++++++++++++++--- tools/tools.sln | 3 + 12 files changed, 98 insertions(+), 34 deletions(-) diff --git a/.github/workflows/renumber-sections.yaml b/.github/workflows/renumber-sections.yaml index 021b34881..13f4ecba7 100644 --- a/.github/workflows/renumber-sections.yaml +++ b/.github/workflows/renumber-sections.yaml @@ -16,6 +16,7 @@ jobs: runs-on: ubuntu-latest permissions: checks: write + pull-requests: write env: DOTNET_NOLOGO: true GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-examples.yaml b/.github/workflows/test-examples.yaml index 8ecc583b3..727e996fb 100644 --- a/.github/workflows/test-examples.yaml +++ b/.github/workflows/test-examples.yaml @@ -16,8 +16,13 @@ on: jobs: test-extraction-and-runner: runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write env: DOTNET_NOLOGO: true + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} steps: - name: Check out our repo diff --git a/.github/workflows/word-converter.yaml b/.github/workflows/word-converter.yaml index f70fad9a9..696f998fd 100644 --- a/.github/workflows/word-converter.yaml +++ b/.github/workflows/word-converter.yaml @@ -16,6 +16,7 @@ jobs: runs-on: ubuntu-latest permissions: checks: write + pull-requests: write env: DOTNET_NOLOGO: true GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index a4e559bb3..17843972a 100644 --- a/.gitignore +++ b/.gitignore @@ -360,3 +360,6 @@ test-grammar/ # don't checkin jar files: *.jar + +# don't checkin launchSettings: +**/launchSettings.json diff --git a/tools/ExampleTester/ExampleTester.csproj b/tools/ExampleTester/ExampleTester.csproj index 507659351..582e6ac54 100644 --- a/tools/ExampleTester/ExampleTester.csproj +++ b/tools/ExampleTester/ExampleTester.csproj @@ -16,6 +16,7 @@ + diff --git a/tools/ExampleTester/GeneratedExample.cs b/tools/ExampleTester/GeneratedExample.cs index cafaec18c..6c06eab33 100644 --- a/tools/ExampleTester/GeneratedExample.cs +++ b/tools/ExampleTester/GeneratedExample.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using System.Reflection; using System.Text; +using Utilities; namespace ExampleTester; @@ -33,9 +34,9 @@ private static GeneratedExample Load(string directory) return new GeneratedExample(directory); } - internal async Task Test(TesterConfiguration configuration) + internal async Task Test(TesterConfiguration configuration, StatusCheckLogger logger) { - var outputLines = new List { $"Testing {Metadata.Name} from {Metadata.Source}" }; + logger.ConsoleOnlyLog(Metadata.Source, Metadata.StartLine, Metadata.EndLine, $"Testing {Metadata.Name} from {Metadata.Source}", "ExampleTester"); // Explicitly do a release build, to avoid implicitly defining DEBUG. var properties = new Dictionary { { "Configuration", "Release" } }; @@ -52,21 +53,16 @@ internal async Task Test(TesterConfiguration configuration) } bool ret = true; - ret &= ValidateDiagnostics("errors", DiagnosticSeverity.Error, Metadata.ExpectedErrors); - ret &= ValidateDiagnostics("warnings", DiagnosticSeverity.Warning, Metadata.ExpectedWarnings, Metadata.IgnoredWarnings); + ret &= ValidateDiagnostics("errors", DiagnosticSeverity.Error, Metadata.ExpectedErrors, logger); + ret &= ValidateDiagnostics("warnings", DiagnosticSeverity.Warning, Metadata.ExpectedWarnings, logger, Metadata.IgnoredWarnings); // Don't try to validate output if we've already failed in terms of errors and warnings, or if we expect errors. if (ret && Metadata.ExpectedErrors is null) { ret &= ValidateOutput(); } - - if (!ret || !configuration.Quiet) - { - outputLines.ForEach(Console.WriteLine); - } return ret; - bool ValidateDiagnostics(string type, DiagnosticSeverity severity, List expected, List? ignored = null) + bool ValidateDiagnostics(string type, DiagnosticSeverity severity, List expected, StatusCheckLogger logger, List? ignored = null) { expected ??= new List(); ignored ??= new List(); @@ -81,10 +77,12 @@ bool ValidateDiagnostics(string type, DiagnosticSeverity severity, List bool ret = ValidateExpectedAgainstActual(type, expected, actualIds); if (!ret) { - outputLines.Add($" Details of actual {type}:"); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, $" Details of actual {type}:", "ExampleTester"); foreach (var diagnostic in actualDiagnostics) { - outputLines.Add($" Line {diagnostic.Location.GetLineSpan().StartLinePosition.Line + 1}: {diagnostic.Id}: {diagnostic.GetMessage()}"); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, + $" Line {diagnostic.Location.GetLineSpan().StartLinePosition.Line + 1}: {diagnostic.Id}: {diagnostic.GetMessage()}", + "ExampleTester"); } } return ret; @@ -97,7 +95,7 @@ bool ValidateOutput() { if (Metadata.ExpectedOutput != null) { - outputLines.Add(" Output expected, but project has no entry point."); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, " Output expected, but project has no entry point.", "ExampleTester"); return false; } return true; @@ -114,7 +112,7 @@ bool ValidateOutput() var emitResult = compilation.Emit(ms); if (!emitResult.Success) { - outputLines.Add(" Failed to emit assembly"); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, " Failed to emit assembly", "ExampleTester"); return false; } @@ -122,13 +120,13 @@ bool ValidateOutput() var type = generatedAssembly.GetType(typeName); if (type is null) { - outputLines.Add($" Failed to find entry point type {typeName}"); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, $" Failed to find entry point type {typeName}", "ExampleTester"); return false; } var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method is null) { - outputLines.Add($" Failed to find entry point method {typeName}.{methodName}"); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, $" Failed to find entry point method {typeName}.{methodName}", "ExampleTester"); return false; } var arguments = method.GetParameters().Any() @@ -197,7 +195,7 @@ bool MaybeReportError(bool result, string message) { if (!result) { - outputLines.Add(message); + logger.LogFailure(Metadata.Source, Metadata.StartLine, Metadata.EndLine, message, "ExampleTester"); } return result; } @@ -207,7 +205,8 @@ bool ValidateExpectedAgainstActual(string type, List expected, List ExecuteAsync(TesterConfiguration configuration) { @@ -31,7 +42,7 @@ async Task ExecuteAsync(TesterConfiguration configuration) foreach (var example in examples) { // The Run method explains any failures, we just need to count them. - if (!await example.Test(configuration)) + if (!await example.Test(configuration, logger)) { failures++; } @@ -42,4 +53,4 @@ async Task ExecuteAsync(TesterConfiguration configuration) Console.WriteLine($"Failures: {failures}"); return failures; -} \ No newline at end of file +} diff --git a/tools/MarkdownConverter/Spec/Reporter.cs b/tools/MarkdownConverter/Spec/Reporter.cs index 47aad0fac..4adaa52c8 100644 --- a/tools/MarkdownConverter/Spec/Reporter.cs +++ b/tools/MarkdownConverter/Spec/Reporter.cs @@ -59,14 +59,14 @@ public void Error(string code, string msg, SourceLocation? loc = null) { loc = loc ?? Location; IncrementErrors(); - githubLogger.LogFailure(new Diagnostic(loc.File ?? "mdspec2docx", loc.StartLine, loc.EndLine, msg, code)); + githubLogger.LogFailure(new StatusCheckMessage(loc.File ?? "mdspec2docx", loc.StartLine, loc.EndLine, msg, code)); } public void Warning(string code, string msg, SourceLocation? loc = null, int lineOffset = 0) { loc = loc ?? Location; IncrementWarnings(); - githubLogger.LogWarning(new Diagnostic(loc.File ?? "mdspec2docx", loc.StartLine+lineOffset, loc.EndLine+lineOffset, msg, code)); + githubLogger.LogWarning(new StatusCheckMessage(loc.File ?? "mdspec2docx", loc.StartLine+lineOffset, loc.EndLine+lineOffset, msg, code)); } public void Log(string code, string msg, SourceLocation? loc = null) diff --git a/tools/StandardAnchorTags/ReferenceUpdateProcessor.cs b/tools/StandardAnchorTags/ReferenceUpdateProcessor.cs index 4dafcc990..10e91490b 100644 --- a/tools/StandardAnchorTags/ReferenceUpdateProcessor.cs +++ b/tools/StandardAnchorTags/ReferenceUpdateProcessor.cs @@ -62,7 +62,7 @@ private string ProcessSectionLinks(string line, int lineNumber, string path) if ((referenceText.Length > 1) && (!linkMap.ContainsKey(referenceText))) { - var diagnostic = new Diagnostic(path, lineNumber, lineNumber, $"`{referenceText}` not found", DiagnosticIDs.TOC002); + var diagnostic = new StatusCheckMessage(path, lineNumber, lineNumber, $"`{referenceText}` not found", DiagnosticIDs.TOC002); logger.LogFailure(diagnostic); } else { diff --git a/tools/StandardAnchorTags/TocSectionNumberBuilder.cs b/tools/StandardAnchorTags/TocSectionNumberBuilder.cs index 64d0e7791..9b3b21ad4 100644 --- a/tools/StandardAnchorTags/TocSectionNumberBuilder.cs +++ b/tools/StandardAnchorTags/TocSectionNumberBuilder.cs @@ -69,7 +69,7 @@ public async Task AddFrontMatterTocEntries(string fileName) return; } // Getting here means this file doesn't have an H1. That's an error: - var diagnostic = new Diagnostic(path, 1, 1, "File doesn't have an H1 tag as its first line.", DiagnosticIDs.TOC001); + var diagnostic = new StatusCheckMessage(path, 1, 1, "File doesn't have an H1 tag as its first line.", DiagnosticIDs.TOC001); logger.LogFailure(diagnostic); } diff --git a/tools/Utilities/StatusCheckLogger.cs b/tools/Utilities/StatusCheckLogger.cs index e98ea1e57..2feaeb65c 100644 --- a/tools/Utilities/StatusCheckLogger.cs +++ b/tools/Utilities/StatusCheckLogger.cs @@ -10,7 +10,7 @@ namespace Utilities; /// The error message ID /// The start line (index from 1) /// The end line (index from 1) -public record Diagnostic(string file, int StartLine, int EndLine, string Message, string Id); +public record StatusCheckMessage(string file, int StartLine, int EndLine, string Message, string Id); /// /// This class writes the status of the check to the console in the format GitHub supports @@ -30,7 +30,33 @@ public class StatusCheckLogger(string pathToRoot, string toolName) // Utility method to format the path to unix style, from the root of the repository. private string FormatPath(string path) => Path.GetRelativePath(pathToRoot, path).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - private void WriteMessageToConsole(string prefix, Diagnostic d) => Console.WriteLine($"{prefix}{toolName}-{d.Id}::file={FormatPath(d.file)},line={d.StartLine}::{d.Message}"); + private void WriteMessageToConsole(string prefix, StatusCheckMessage d) => Console.WriteLine($"{prefix}{toolName}-{d.Id}::file={FormatPath(d.file)},line={d.StartLine}::{d.Message}"); + + /// + /// Log a notice from the status check to the console only + /// + /// source file + /// start line + /// end line + /// The error message + /// The error ID + /// + /// Log the diagnostic information to console. These will only appear in the console window, not + /// as annotations on the changes in the PR. + /// + public void ConsoleOnlyLog(string source, int startLine, int endLine, string message, string id) => + ConsoleOnlyLog(source, new StatusCheckMessage(source, startLine, endLine, message, id)); + + /// + /// Log a notice from the status check to the console only + /// + /// The diagnostic + /// + /// Log the diagnostic information to console. These will only appear in the console window, not + /// as annotations on the changes in the PR. + /// + public void ConsoleOnlyLog(string source, StatusCheckMessage d) => + WriteMessageToConsole("", d); /// /// Log a notice from the status check @@ -40,7 +66,7 @@ public class StatusCheckLogger(string pathToRoot, string toolName) /// Add the diagnostic to the annotation list and /// log the diagnostic information to console. /// - public void LogNotice(Diagnostic d) + public void LogNotice(StatusCheckMessage d) { WriteMessageToConsole("", d); annotations.Add( @@ -59,7 +85,7 @@ public void LogNotice(Diagnostic d) /// log the warning notice to the console. /// Warnings are logged, but the process reports "success" to GitHub. /// - public void LogWarning(Diagnostic d) + public void LogWarning(StatusCheckMessage d) { WriteMessageToConsole("⚠️", d); annotations.Add( @@ -69,6 +95,18 @@ public void LogWarning(Diagnostic d) ); } + /// + /// Log a failure from the status check + /// + /// The source file + /// Start line in source + /// End line in source + /// The error message + /// The string ID for the error + public void LogFailure(string source, int startLine, int endLine, string message, string id) => + LogFailure(new(source, startLine, endLine, message, id)); + + /// /// Log a failure from the status check /// @@ -76,11 +114,11 @@ public void LogWarning(Diagnostic d) /// /// Add the diagnostic to the annotation list and /// log the failure notice to the console. - /// This method is distinct from in + /// This method is distinct from in /// that this method does not throw an exception. Its purpose is to log /// the failure but allow the tool to continue running further checks. /// - public void LogFailure(Diagnostic d) + public void LogFailure(StatusCheckMessage d) { WriteMessageToConsole("❌", d); annotations.Add( @@ -98,11 +136,11 @@ public void LogFailure(Diagnostic d) /// /// Add the diagnostic to the annotation list and /// log the failure notice to the console. - /// This method is distinct from in + /// This method is distinct from in /// that this method throws an exception. Its purpose is to log /// the failure and immediately exit, foregoing any further checks. /// - public void ExitOnFailure(Diagnostic d) + public void ExitOnFailure(StatusCheckMessage d) { LogFailure(d); throw new InvalidOperationException(d.Message); @@ -138,9 +176,11 @@ public async Task BuildCheckRunResult(string token, string owner, string repo, s } // If the token does not have the correct permissions, we will get a 403 // Once running on a branch on the dotnet org, this should work correctly. - catch (ForbiddenException) + catch (ForbiddenException e) { Console.WriteLine("===== WARNING: Could not create a check run.====="); + Console.WriteLine("Exception details:"); + Console.WriteLine(e); } } } diff --git a/tools/tools.sln b/tools/tools.sln index afa87f0cc..be1786ffb 100644 --- a/tools/tools.sln +++ b/tools/tools.sln @@ -14,6 +14,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleExtractor", "ExampleExtractor\ExampleExtractor.csproj", "{571E69B9-07A3-4682-B692-876B016315CD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleTester", "ExampleTester\ExampleTester.csproj", "{829FE7D6-B7E7-48DF-923A-73A79921E997}" + ProjectSection(ProjectDependencies) = postProject + {835C6333-BDB5-4DEC-B3BE-4300E3F948AF} = {835C6333-BDB5-4DEC-B3BE-4300E3F948AF} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleFormatter", "ExampleFormatter\ExampleFormatter.csproj", "{82D1A159-5637-48C4-845D-CC1390995CC2}" EndProject