From eddec1df991651c9dd57c691ac444203856df7ac Mon Sep 17 00:00:00 2001 From: ancplua Date: Mon, 6 Jul 2026 06:19:41 +0200 Subject: [PATCH] chore: arm the warnings-as-errors gate and drive the build to zero warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TreatWarningsAsErrors gate keyed on ContinuousIntegrationBuild, which CI never set (GitHub Actions only sets CI=true) — so CI built green with ~236 unique analyzer warnings. Directory.Build.props now maps CI=true to ContinuousIntegrationBuild=true, and the warning debt is cleared: Fixed in code: - CA1305: TypeCache Convert.ToInt32 with invariant culture - CA1307: ordinal Contains in Guard.Path; ordinal Replace in tests - CA2007: ConfigureAwait(false) scope pattern for await-using in Testing MSBuild helpers - CA1063: full Dispose(bool) pattern on SolutionRefactoringTest - CA1859: concrete return types on three private helpers - CA1822: removed dead Dispose on private NonGenericEnumerator - RS2008: pragma for the test-only descriptor - CA1849: pragma x2 (CancelAsync unavailable on netstandard2.0) - xUnit1051 x6: TestContext cancellation tokens in recent tests (caught by the newly armed gate — errors, not warnings, proving the gate works) Configured off in .editorconfig, each with a written justification: CA1062 (redundant with enforced NRT), CA1815, CA1031, CA1034, CA1054/CA1055, CA1028, CA1024, CA1710/CA1711/CA1721, CA2225, CA1819. CI=true clean rebuild: 0 warnings, 0 errors. 215 tests green. Co-Authored-By: Claude Fable 5 --- .claude/TASK.md | 36 +++++++++++++++ .editorconfig | 46 +++++++++++++++++++ Directory.Build.props | 3 ++ .../MSBuild/DotNetSdkHelpers.cs | 9 ++-- .../MSBuild/PackageTestBase.cs | 6 ++- .../SolutionRefactoringTest.cs | 14 +++++- .../Async/ParallelAsyncExtensions.cs | 4 ++ .../CodeGeneration.cs | 2 +- .../DocumentationExtensions.cs | 2 +- src/ANcpLua.Roslyn.Utilities/Enumerator.cs | 4 -- src/ANcpLua.Roslyn.Utilities/Guard.Path.cs | 4 +- .../SyntaxExtensions.cs | 2 +- src/ANcpLua.Roslyn.Utilities/TypeCache.cs | 4 +- .../ConvertExtensionsTests.cs | 4 +- ...ncrementalValuesProviderExtensionsTests.cs | 3 +- .../TreeBoundLocationTests.cs | 9 ++-- .../TypeDeclarationInfoTests.cs | 15 ++++-- 17 files changed, 139 insertions(+), 28 deletions(-) create mode 100644 .claude/TASK.md diff --git a/.claude/TASK.md b/.claude/TASK.md new file mode 100644 index 0000000..2a0a1c5 --- /dev/null +++ b/.claude/TASK.md @@ -0,0 +1,36 @@ +# TASK — two arcs: (A) hygiene: real warnings-as-errors + zero-warning build → 2.2.34; (B) features: GetFullyQualifiedMetadataName + cachability assertions → 2.2.35 + +Status: active + +## Arc A — hygiene (this branch: `claude/warnings-as-errors`) +The `TreatWarningsAsErrors` gate keys on `ContinuousIntegrationBuild`, which CI never sets — so CI +builds green with ~236 unique analyzer warnings. Plan: +- [x] Map `CI=true` → `ContinuousIntegrationBuild=true` in `Directory.Build.props`. +- [x] Fixed in code: CA1305 (TypeCache invariant culture), CA1307 (Guard.Path ordinal Contains, + test Replace calls), CA2007 (Testing await-using ConfigureAwait scope pattern ×5), + CA1063 (SolutionRefactoringTest full Dispose pattern), CA1859 (3 private helpers → concrete + return types), CA1822 (dead Dispose on private NonGenericEnumerator removed), RS2008 + (pragma, test-only descriptor), CA1849 (pragma ×2 — CancelAsync unavailable on netstandard2.0), + plus 6 xUnit1051 errors the armed gate caught in the 2.2.30–33 tests. +- [x] Configured off with written justification in `.editorconfig`: CA1062 (redundant with + enforced NRT), CA1815, CA1031, CA1034, CA1054/CA1055, CA1028, CA1024, CA1710/CA1711/CA1721, + CA2225, CA1819. +- [x] `CI=true` clean rebuild: **0 warnings, 0 errors**; 215 tests passed. +- [ ] PR → auto-merge → publish **2.2.34** (auto-bump), verify tag+index, bump qyl. + +## Arc B — features (branch: `claude/metadata-name-caching-assertions`, after A merges) +- [ ] `TypeDeclarationInfo.GetFullyQualifiedMetadataName()` — reconstruct + ``Deep.Outer`1+Middle+Inner`1`` from the snapshot (inverse of `From` w.r.t. lookup), for + `Compilation.GetTypeByMetadataName` in later pipeline stages. Tests incl. round-trip: + `GetTypeByMetadataName(info.GetFullyQualifiedMetadataName())` resolves the original symbol. +- [ ] Cachability assertions in `.Testing`: run-twice helper asserting all tracked incremental + steps report `IncrementalStepRunReason.Cached`/`Unchanged` on the second run (TrackingNames + pattern). Integrate with existing CachingHintBuilder/ModelEqualityClassifier if they overlap. +- [ ] Tests, README, build+test green. +- [ ] PR → merge → publish **2.2.35**, verify, bump qyl 2.2.34 → 2.2.35. +- [ ] Remove this file in a final `.claude/`-only PR. + +## Notes +- Latest: 2.2.33 (qyl on 2.2.33 via qyl#492). +- cref discipline: overloaded members always get explicit parameter lists (memory: cref-overloads-explicit). +- Known ambient: none — ambiguous crefs were cleared in 2.2.33. diff --git a/.editorconfig b/.editorconfig index 78b36ca..561ce51 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1 +1,47 @@ root = true + +# Analyzer tuning for a Roslyn utility library (AnalysisLevel=latest-all baseline). +# Every disabled rule below is a deliberate, categorical decision — new warning IDs +# not listed here fail the CI build (warnings-as-errors when ContinuousIntegrationBuild=true). + +[*.cs] +# Nullable reference types are enabled and enforced repo-wide; inputs are Roslyn pipeline +# objects that are non-null by contract. Null guards on ~390 public members would add +# hot-path cost and churn without catching real defects NRT doesn't already catch. +dotnet_diagnostic.CA1062.severity = none + +# Value types here are scopes (IndentScope, TypeDeclarationScope, ScopedActivity), builders +# (HashCombiner), and enumerator/entry structs — equality on them is meaningless by design. +dotnet_diagnostic.CA1815.severity = none + +# Deliberate catch-alls: Try*/SelectAndCaptureExceptions/DiagnosticFlow.Try exist to convert +# arbitrary user-code exceptions into data (diagnostics/results). OperationCanceledException +# is rethrown where it matters. +dotnet_diagnostic.CA1031.severity = none + +# Nested public types are the established zero-allocation enumerator pattern +# (LineSplitEnumerator/LineSplitEntry) and intentional grouping (TimeConversions.Iso8601). +dotnet_diagnostic.CA1034.severity = none + +# String-typed URLs are intentional: OTel schema URLs are opaque identifiers, and the +# QueryString/ShortId helpers are string-in/string-out by design. +dotnet_diagnostic.CA1054.severity = none +dotnet_diagnostic.CA1055.severity = none + +# OTel enums are byte-backed to match the wire format. +dotnet_diagnostic.CA1028.severity = none + +# Method-vs-property and naming rules would break shipped public API +# (EquatableArray, ReadOnlyCollection, ReadOnlyDictionary, HashCombiner.HashCode). +dotnet_diagnostic.CA1024.severity = none +dotnet_diagnostic.CA1710.severity = none +dotnet_diagnostic.CA1711.severity = none +dotnet_diagnostic.CA1721.severity = none + +# Implicit operators ship documented named alternates using the As* naming family +# (AsImmutableArray, AsEquatableArray); CA2225 only recognizes To*/From* names. +dotnet_diagnostic.CA2225.severity = none + +# Array-returning properties on result/report types (BuildResult, DataReaderExtensions) +# expose materialized snapshots by design. +dotnet_diagnostic.CA1819.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index 934f240..7273307 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,6 +20,9 @@ true latest-all + + true + true true true diff --git a/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/DotNetSdkHelpers.cs b/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/DotNetSdkHelpers.cs index c41d77a..dda2ada 100644 --- a/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/DotNetSdkHelpers.cs +++ b/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/DotNetSdkHelpers.cs @@ -145,8 +145,10 @@ public static async Task Get(NetSdkVersion version) else { using var ms = new MemoryStream(bytes); - await using var gz = new GZipStream(ms, CompressionMode.Decompress); - await using var tar = new TarReader(gz); + var gz = new GZipStream(ms, CompressionMode.Decompress); + await using var gzScope = gz.ConfigureAwait(false); + var tar = new TarReader(gz); + await using var tarScope = tar.ConfigureAwait(false); while ((await tar.GetNextEntryAsync().ConfigureAwait(false)) is { } entry) { var destinationPath = tempFolder / entry.Name; @@ -160,7 +162,8 @@ public static async Task Get(NetSdkVersion version) if (Path.GetDirectoryName(destinationPath) is { } parentDir) Directory.CreateDirectory(parentDir); var entryStream = entry.DataStream; - await using var outputStream = File.Create(destinationPath); + var outputStream = File.Create(destinationPath); + await using var outputScope = outputStream.ConfigureAwait(false); if (entryStream is not null) await entryStream.CopyToAsync(outputStream).ConfigureAwait(false); break; } diff --git a/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/PackageTestBase.cs b/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/PackageTestBase.cs index 24a7f16..a15e0b4 100644 --- a/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/PackageTestBase.cs +++ b/src/ANcpLua.Roslyn.Utilities.Testing/MSBuild/PackageTestBase.cs @@ -290,11 +290,12 @@ protected override async Task QuickBuild( string tfm = Tfm.Net100, params (string Key, string Value)[] extraProps) { - await using var project = CreateProjectBuilder( + var project = CreateProjectBuilder( TestOutputHelper, Fixture.PackageDirectory, "TestPackage", Fixture.Version); + await using var projectScope = project.ConfigureAwait(false); return await project .WithTargetFramework(tfm) @@ -341,11 +342,12 @@ protected override async Task BuildExe( string tfm = Tfm.Net100, params (string Key, string Value)[] extraProps) { - await using var project = CreateProjectBuilder( + var project = CreateProjectBuilder( TestOutputHelper, Fixture.PackageDirectory, "TestPackage", Fixture.Version); + await using var projectScope = project.ConfigureAwait(false); return await project .WithTargetFramework(tfm) diff --git a/src/ANcpLua.Roslyn.Utilities.Testing/SolutionRefactoringTest.cs b/src/ANcpLua.Roslyn.Utilities.Testing/SolutionRefactoringTest.cs index e800828..1f0e591 100644 --- a/src/ANcpLua.Roslyn.Utilities.Testing/SolutionRefactoringTest.cs +++ b/src/ANcpLua.Roslyn.Utilities.Testing/SolutionRefactoringTest.cs @@ -66,10 +66,22 @@ public abstract class SolutionRefactoringTest : IDisposable /// public void Dispose() { - _workspace.Dispose(); + Dispose(true); GC.SuppressFinalize(this); } + /// + /// Disposes the underlying workspace when is true. + /// + /// + /// true when called from ; false when called from a finalizer. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + _workspace.Dispose(); + } + /// /// Verifies a refactoring across multiple documents in a single project. /// diff --git a/src/ANcpLua.Roslyn.Utilities/Async/ParallelAsyncExtensions.cs b/src/ANcpLua.Roslyn.Utilities/Async/ParallelAsyncExtensions.cs index e0f878b..1ac81f1 100644 --- a/src/ANcpLua.Roslyn.Utilities/Async/ParallelAsyncExtensions.cs +++ b/src/ANcpLua.Roslyn.Utilities/Async/ParallelAsyncExtensions.cs @@ -157,7 +157,9 @@ void RecordFailure(Exception ex) } finally { +#pragma warning disable CA1849 // CancelAsync is unavailable on netstandard2.0 cts.Cancel(); +#pragma warning restore CA1849 try { await completion.ConfigureAwait(false); @@ -317,7 +319,9 @@ void RecordFailure(Exception ex) } finally { +#pragma warning disable CA1849 // CancelAsync is unavailable on netstandard2.0 cts.Cancel(); +#pragma warning restore CA1849 try { await completion.ConfigureAwait(false); diff --git a/src/ANcpLua.Roslyn.Utilities/CodeGeneration.cs b/src/ANcpLua.Roslyn.Utilities/CodeGeneration.cs index 63837e1..f3535a4 100644 --- a/src/ANcpLua.Roslyn.Utilities/CodeGeneration.cs +++ b/src/ANcpLua.Roslyn.Utilities/CodeGeneration.cs @@ -173,7 +173,7 @@ public static string RestoreWarnings(params string[] warningIds) return warningIds.Length is 0 ? string.Empty : $"#pragma warning restore {string.Join(", ", ValidateWarningIds(warningIds))}"; } - private static IReadOnlyList ValidateWarningIds(string[] warningIds) + private static string[] ValidateWarningIds(string[] warningIds) { foreach (var warningId in warningIds) if (!IsValidWarningId(warningId)) diff --git a/src/ANcpLua.Roslyn.Utilities/DocumentationExtensions.cs b/src/ANcpLua.Roslyn.Utilities/DocumentationExtensions.cs index 821d0df..41e266b 100644 --- a/src/ANcpLua.Roslyn.Utilities/DocumentationExtensions.cs +++ b/src/ANcpLua.Roslyn.Utilities/DocumentationExtensions.cs @@ -193,7 +193,7 @@ static bool IsEligibleForAutomaticInheritdoc(ISymbol symbol) } } - private static IEnumerable RewriteInheritdocElements( + private static XNode[] RewriteInheritdocElements( ISymbol symbol, HashSet? visitedSymbols, Compilation compilation, diff --git a/src/ANcpLua.Roslyn.Utilities/Enumerator.cs b/src/ANcpLua.Roslyn.Utilities/Enumerator.cs index 2b27502..0da7f16 100644 --- a/src/ANcpLua.Roslyn.Utilities/Enumerator.cs +++ b/src/ANcpLua.Roslyn.Utilities/Enumerator.cs @@ -49,10 +49,6 @@ public bool MoveNext() public void Reset() { } - - public void Dispose() - { - } } private static class EmptyEnumeratorCache diff --git a/src/ANcpLua.Roslyn.Utilities/Guard.Path.cs b/src/ANcpLua.Roslyn.Utilities/Guard.Path.cs index 7742f2b..549c081 100644 --- a/src/ANcpLua.Roslyn.Utilities/Guard.Path.cs +++ b/src/ANcpLua.Roslyn.Utilities/Guard.Path.cs @@ -201,7 +201,7 @@ public static string ValidExtension( if (value.StartsWith(".", StringComparison.Ordinal)) throw new ArgumentException("Extension must not start with a period ('.').", paramName); - if (value.Contains('\\') || value.Contains('/')) + if (value.Contains('\\', StringComparison.Ordinal) || value.Contains('/', StringComparison.Ordinal)) throw new ArgumentException("Extension must not contain path separators.", paramName); return value; @@ -232,7 +232,7 @@ public static string NormalizedExtension( { NotNullOrEmpty(value, paramName); - if (value.Contains('\\') || value.Contains('/')) + if (value.Contains('\\', StringComparison.Ordinal) || value.Contains('/', StringComparison.Ordinal)) throw new ArgumentException("Extension must not contain path separators.", paramName); return value.StartsWith(".", StringComparison.Ordinal) ? value : $".{value}"; diff --git a/src/ANcpLua.Roslyn.Utilities/SyntaxExtensions.cs b/src/ANcpLua.Roslyn.Utilities/SyntaxExtensions.cs index ae572d9..3056078 100644 --- a/src/ANcpLua.Roslyn.Utilities/SyntaxExtensions.cs +++ b/src/ANcpLua.Roslyn.Utilities/SyntaxExtensions.cs @@ -605,7 +605,7 @@ public static bool IsPrimitiveKeyword(this string typeName) /// from "Dictionary<string, List<int>>"). /// /// A list of individual type argument strings, trimmed of whitespace. - private static IEnumerable ParseGenericArguments(string argsContent) + private static List ParseGenericArguments(string argsContent) { var args = new List(); var depth = 0; diff --git a/src/ANcpLua.Roslyn.Utilities/TypeCache.cs b/src/ANcpLua.Roslyn.Utilities/TypeCache.cs index 08915f3..83f143b 100644 --- a/src/ANcpLua.Roslyn.Utilities/TypeCache.cs +++ b/src/ANcpLua.Roslyn.Utilities/TypeCache.cs @@ -34,7 +34,7 @@ public TypeCache(Func resolver) var maxValue = 0; foreach (TEnum value in enumValues) { - var intValue = Convert.ToInt32(value); + var intValue = Convert.ToInt32(value, CultureInfo.InvariantCulture); if (intValue > maxValue) maxValue = intValue; } @@ -59,7 +59,7 @@ public TypeCache(Func resolver) /// public INamedTypeSymbol? Get(TEnum type) { - var index = Convert.ToInt32(type); + var index = Convert.ToInt32(type, CultureInfo.InvariantCulture); if (index < 0 || index >= _cache.Length) return null; diff --git a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/ConvertExtensionsTests.cs b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/ConvertExtensionsTests.cs index 0bdff2a..8175df0 100644 --- a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/ConvertExtensionsTests.cs +++ b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/ConvertExtensionsTests.cs @@ -60,7 +60,7 @@ public void Method() { } } """; - var tree = CSharpSyntaxTree.ParseText(source.Replace("ARG", argumentExpression)); + var tree = CSharpSyntaxTree.ParseText(source.Replace("ARG", argumentExpression, StringComparison.Ordinal)); var semanticModel = GetSemanticModel(tree); var method = tree.GetRoot().DescendantNodes().OfType().Single(); @@ -89,7 +89,7 @@ public void Method() { } } """; - var tree = CSharpSyntaxTree.ParseText(source.Replace("ARG", argumentExpression)); + var tree = CSharpSyntaxTree.ParseText(source.Replace("ARG", argumentExpression, StringComparison.Ordinal)); var semanticModel = GetSemanticModel(tree); var method = tree.GetRoot().DescendantNodes().OfType().Single(); diff --git a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/IncrementalValuesProviderExtensionsTests.cs b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/IncrementalValuesProviderExtensionsTests.cs index 4ed0756..6342b01 100644 --- a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/IncrementalValuesProviderExtensionsTests.cs +++ b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/IncrementalValuesProviderExtensionsTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Globalization; using System.Linq; using System.Threading; using ANcpLua.Roslyn.Utilities; @@ -97,7 +98,7 @@ public void SelectAndReportExceptions_SingleValueOverload_ReportsDiagnosticWitho result.Exception.Should().BeNull(); result.Diagnostics.Should().ContainSingle(diagnostic => diagnostic.Id == "SINGLE001" && - diagnostic.GetMessage().Contains("single boom", StringComparison.Ordinal)); + diagnostic.GetMessage(CultureInfo.InvariantCulture).Contains("single boom", StringComparison.Ordinal)); } private static InvalidOperationException CaptureThrow() diff --git a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TreeBoundLocationTests.cs b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TreeBoundLocationTests.cs index f64e2d7..0801cf1 100644 --- a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TreeBoundLocationTests.cs +++ b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TreeBoundLocationTests.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Linq; using ANcpLua.Roslyn.Utilities.Models; using AwesomeAssertions; @@ -18,6 +19,7 @@ public class Widget } """; +#pragma warning disable RS2008 // release tracking does not apply to a test-only descriptor private static readonly DiagnosticDescriptor Descriptor = new( "TST001", "Test title", @@ -25,6 +27,7 @@ public class Widget "Tests", DiagnosticSeverity.Warning, true); +#pragma warning restore RS2008 [Fact] public void ToLocation_WithOriginTree_ReturnsTreeBoundLocation() @@ -57,7 +60,7 @@ public void ToLocation_WithTreeTooShortForSpan_FallsBackToPathBasedLocation() { var (_, node) = ParseWidget(); var info = LocationInfo.From(node); - var unrelatedShortTree = CSharpSyntaxTree.ParseText("//"); + var unrelatedShortTree = CSharpSyntaxTree.ParseText("//", cancellationToken: TestContext.Current.CancellationToken); var location = info.ToLocation(unrelatedShortTree); @@ -75,7 +78,7 @@ public void ToDiagnostic_WithTree_ReportsTreeBoundLocation() diagnostic.Location.IsInSource.Should().BeTrue(); diagnostic.Location.SourceTree.Should().BeSameAs(tree); - diagnostic.GetMessage().Should().Be("message"); + diagnostic.GetMessage(CultureInfo.InvariantCulture).Should().Be("message"); } [Fact] @@ -89,7 +92,7 @@ public void ToDiagnostic_WithNullTree_MatchesParameterlessBehavior() withNull.Location.IsInSource.Should().BeFalse(); withNull.Location.GetLineSpan().Should().Be(parameterless.Location.GetLineSpan()); - withNull.GetMessage().Should().Be(parameterless.GetMessage()); + withNull.GetMessage(CultureInfo.InvariantCulture).Should().Be(parameterless.GetMessage(CultureInfo.InvariantCulture)); } private static (SyntaxTree Tree, ClassDeclarationSyntax Node) ParseWidget() diff --git a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TypeDeclarationInfoTests.cs b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TypeDeclarationInfoTests.cs index 8cb3740..dcbf209 100644 --- a/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TypeDeclarationInfoTests.cs +++ b/tests/ANcpLua.Roslyn.Utilities.Testing.Tests/TypeDeclarationInfoTests.cs @@ -187,11 +187,15 @@ public void BeginDeclaration_Output_CompilesTogetherWithOriginalSource() var compilation = CSharpCompilation.Create( "PartialMerge", - [CSharpSyntaxTree.ParseText(NestedSource), CSharpSyntaxTree.ParseText(builder.ToString())], + [ + CSharpSyntaxTree.ParseText(NestedSource, cancellationToken: TestContext.Current.CancellationToken), + CSharpSyntaxTree.ParseText(builder.ToString(), cancellationToken: TestContext.Current.CancellationToken) + ], [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)], new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - var errors = compilation.GetDiagnostics().Where(d => d.Severity is DiagnosticSeverity.Error).ToArray(); + var errors = compilation.GetDiagnostics(TestContext.Current.CancellationToken) + .Where(d => d.Severity is DiagnosticSeverity.Error).ToArray(); errors.Should().BeEmpty(); } @@ -318,7 +322,8 @@ public void GetHintName_IsAcceptedByRoslynHintNameValidation() new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var driver = CSharpGeneratorDriver.Create(new AddSourceProbeGenerator(hintName).AsSourceGenerator()); - var result = driver.RunGenerators(compilation).GetRunResult().Results.Single(); + var result = driver.RunGenerators(compilation, TestContext.Current.CancellationToken) + .GetRunResult().Results.Single(); result.Exception.Should().BeNull(); result.GeneratedSources.Length.Should().Be(1); @@ -337,7 +342,7 @@ private static INamedTypeSymbol GetType(string source, string metadataName) { var compilation = CSharpCompilation.Create( "TypeDeclarationShapes", - [CSharpSyntaxTree.ParseText(source)], + [CSharpSyntaxTree.ParseText(source, cancellationToken: TestContext.Current.CancellationToken)], [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)], new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); @@ -347,6 +352,6 @@ private static INamedTypeSymbol GetType(string source, string metadataName) private static string Normalize(string text) { - return text.Replace("\r\n", "\n"); + return text.Replace("\r\n", "\n", StringComparison.Ordinal); } }