Skip to content

Unblock the build (header + LF migration) and fix 66 SonarCloud issues - #157

Merged
matt-edmondson merged 6 commits into
mainfrom
chore/sonarcloud-cleanup
Aug 14, 2026
Merged

Unblock the build (header + LF migration) and fix 66 SonarCloud issues#157
matt-edmondson merged 6 commits into
mainfrom
chore/sonarcloud-cleanup

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Unblocks the build (it currently fails on main, locally and in CI) and fixes 66 SonarCloud issues.

The build was broken — 8604 errors on main

Two independent problems, both from recent SDK/repo changes:

1. File headers vs. ktsu.Sdk 2.26.1 — 1168 × IDE0073. The SDK rewrites .editorconfig on every build to set

file_header_template = Copyright (c) 2023-2026 ktsu-dev contributors

but every source file still carried the old three-line ktsu.dev header, so IDE0073 fired on all of them. Migrated 523 .cs files to the one-line form, preserving the UTF-8 BOM on the 226 that carry one.

This is the same migration already drafted on chore/sdk-2.21.1-lf-migration — that branch is 24 commits behind main and pinned to SDK 2.21.1, so this redoes it against current main. It can be closed.

Critically, GeneratorBase.WriteHeaderTo also emits the header into generated output, so it was updated too — otherwise verify-generated would fail on drift. All 226 generated files reproduce byte-identically.

2. Line endings — IDE0055 on every line. The repo moved to * text=auto eol=lf, but files checked out beforehand were still CRLF on disk (i/lf w/crlf), which core.autocrlf=true hid from git status. Normalized the working tree to LF.

3. KTSU0002. Added AssemblyInfo.cs exposing internals to ktsu.Semantics.Test for Semantics.Color and Semantics.Music, matching the existing files in Semantics.Paths and Semantics.Quantities.

Result: 0 errors, 0 warnings across all target frameworks.

SonarCloud fixes — 66

BLOCKERs — 3

  • S2187 ×2UtilityTests and AdvancedUtilityTests are static container classes carrying a spurious [TestClass]; the nested [TestClass] types hold the actual tests and are discovered independently. Also clears MSTEST0016 ×2.
  • S2699PrefixAndSuffixAttribute_ValidString_ShouldPass asserted nothing and duplicated the test below it. Renamed to PrefixAndSuffixAttribute_AppliedToType_ExposesConfiguredPrefixAndSuffix and made it verify the attribute metadata on the fixture type.

MSTEST0068 — 36

CollectionAssert.AreEqualAssert.AreSequenceEqual. Since AreSequenceEqual takes IEnumerable<T>, the accompanying .ToArray()/.ToList() became redundant (IDE0305) and inline new List<T> { … } arguments became collection-expression candidates (IDE0028); both cleaned up.

⚠️Worth knowing: AreSequenceEqual is not a drop-in replacement when the element type is itself IEnumerable.
GetAncestors_EndsAtTheRootWithoutRepeatingIt compares AbsoluteDirectoryPath values, and SemanticString explicitly implements IEnumerable<char>, so AreSequenceEqual recursed into each element instead of using the record's value equality that CollectionAssert.AreEqual relied on. The assertion failed while printing identical expected and actual text. That site now compares the underlying WeakString values, matching how the rest of the class asserts. string is special-cased by MSTest, which is why no other site was affected.

MSTEST0037 — 21

FromToCount
Assert.AreEqual(n, x.Count)Assert.HasCount(n, x)11
Assert.AreEqual(0, x.Count)Assert.IsEmpty(x)3
Assert.IsFalse(x.Any())Assert.IsEmpty(x)2
Assert.IsTrue(a > b)Assert.IsGreaterThan(b, a)4
Assert.IsTrue(a >= b)Assert.IsGreaterThanOrEqualTo(b, a)1
Assert.IsTrue(a < b)Assert.IsLessThan(b, a)2

MSTEST0054 — 4

TestContext.CancellationTokenSource.TokenTestContext.CancellationToken.

Still open — ~94

Not addressed here, to keep this reviewable: S1192 magic strings (19), S4136 overload adjacency (10), S1133 deprecation TODOs (10), S3776 cognitive complexity (9, mostly in the source generators), S3267 (6), S1172 unused parameters (6), S6610 EndsWith(char) (6), plus a tail of smaller rules.

Verification

  • dotnet build0 errors, 0 warnings, all target frameworks (was 8604 errors)
  • dotnet test1079/1079 pass

- S2187/MSTEST0016: drop spurious [TestClass] from the static container
classes UtilityTests and AdvancedUtilityTests; the nested [TestClass]
types hold the actual tests and are discovered independently.
- S2699: PrefixAndSuffixAttribute_ValidString_ShouldPass asserted nothing
and duplicated the test below it. Renamed to
PrefixAndSuffixAttribute_AppliedToType_ExposesConfiguredPrefixAndSuffix
and made it verify the attribute metadata on the fixture type.
- MSTEST0054: TestContext.CancellationTokenSource.Token ->
TestContext.CancellationToken (4 sites).
ktsu.Sdk 2.26.1 rewrites .editorconfig on build to set
file_header_template = Copyright (c) 2023-2026 ktsu-dev contributors
but every source file still carried the old three-line ktsu.dev header,
so IDE0073 fired on all 1168 of them and the build could not complete.
This is the same migration already drafted on the stale
chore/sdk-2.21.1-lf-migration branch (which is 24 commits behind main and
pinned to SDK 2.21.1), redone against current main.
- Replaced the three-line header with the one-line form in 523 .cs files,
preserving the UTF-8 BOM on the 226 files that carry one.
- GeneratorBase.WriteHeaderTo now emits the one-line header, so
regenerated output matches the committed sources and the
verify-generated workflow stays green.
- Committed the SDK-generated .editorconfig template change.
- KTSU0002: added AssemblyInfo.cs exposing internals to ktsu.Semantics.Test
for Semantics.Color and Semantics.Music, matching the existing files in
Semantics.Paths and Semantics.Quantities.
Also normalized the working tree to LF. The repo moved to
"* text=auto eol=lf" but files checked out beforehand were still CRLF on
disk, which core.autocrlf=true hid from git status while IDE0055 fired on
every line.
Build: 0 errors, 0 warnings across all target frameworks (was 8604 errors).
Tests: 1079/1079 pass.
Migrated all 36 call sites. AreSequenceEqual takes IEnumerable<T>, so the
accompanying .ToArray()/.ToList() calls became redundant (IDE0305) and the
inline new List<T> { ... } arguments became collection-expression
candidates (IDE0028); both were cleaned up, hoisting the expected values
into typed locals where the call spanned multiple lines.
One behavioural trap worth recording: AreSequenceEqual is NOT a drop-in
replacement when the element type itself implements IEnumerable.
GetAncestors_EndsAtTheRootWithoutRepeatingIt compares
AbsoluteDirectoryPath values, and SemanticString explicitly implements
IEnumerable<char>, so AreSequenceEqual recursed into each element instead
of using the record value equality that CollectionAssert.AreEqual relied
on - the assert failed while printing identical expected/actual text.
That site now compares the underlying WeakString values, matching how the
rest of the class asserts. string is special-cased by MSTest, which is why
no other site was affected.
Build clean, 1079/1079 tests pass.
All 21 sites:
- Assert.AreEqual(n, x.Count) -> Assert.HasCount(n, x) (11)
- Assert.AreEqual(0, x.Count) -> Assert.IsEmpty(x) (3)
- Assert.IsFalse(x.Any()) -> Assert.IsEmpty(x) (2)
- Assert.IsTrue(a > b) -> Assert.IsGreaterThan(b, a) (4)
- Assert.IsTrue(a >= b) -> Assert.IsGreaterThanOrEqualTo (1)
- Assert.IsTrue(a < b) -> Assert.IsLessThan(b, a) (2)
Build clean, 1079/1079 tests pass.
The SonarCloud quality gate failed on new_coverage: the only uncovered new
line was in GeneratorBase.WriteHeaderTo. Semantics.SourceGenerators is 1077
lines at 0% coverage because it is a Roslyn component that runs at build
time and was not referenced by any test project, so any edit to it fails
the gate.
Rather than excluding it from analysis - which would also have hidden ~40
real findings (S3776 x9, S2223 x3, ...) in code that produces the shipped
library - this drives the generators directly through CSharpGeneratorDriver.
New project Semantics.SourceGenerators.Test covering all 8 generators with
the real production metadata:
- every generator emits sources carrying the canonical file header, which
is the regression guard for the drift this PR had to fix by hand: if
WriteHeaderTo and .editorconfig file_header_template fall out of step,
a test fails instead of every generated file failing IDE0073
- no generator reports diagnostics for valid metadata
- QuantitiesGenerator emits a non-trivial catalogue
- malformed metadata surfaces as the CONV001 diagnostic rather than
throwing
Two wiring notes, both captured as comments in the csproj:
- it needs its own project. The generator targets netstandard2.0 and its
GetDependencyTargetPaths target deliberately bundles every dependency
into consumers, so referencing it from Semantics.Test dragged the
System.Memory / System.Numerics.Vectors facades in and broke 142
span-using tests with CS0433.
- ktsu.CodeBlocker must be referenced explicitly. The generator declares
its dependencies PrivateAssets="all", so they do not flow to consumers,
but the generators genuinely execute here and fail with
FileNotFoundException without it.
Build clean, 1089/1089 tests pass (1079 + 10 new).
…e is collected
The generator tests initially lived in their own project, but KtsuBuild runs
"dotnet test --coverage --coverage-output coverage.xml" once at solution level,
so with two test projects both write the same coverage file and only one
survives. Verified by reproducing the exact invocation locally: the merged
report contained only Semantics.Test s six ktsu.Semantics.* modules and the
generator assembly was silently dropped, which is why the coverage gate stayed
at 0% even though the tests ran and passed.
(That is a KtsuBuild limitation worth fixing upstream - any ktsu repo with more
than one test project currently loses coverage for all but one of them.)
Moving the tests into the single existing test project sidesteps it. Two guards
make that reference safe, both documented in the project files:
- BundleAnalyzerDependencies=false on the ProjectReference. The generator s
GetDependencyTargetPaths target deliberately bundles every dependency into
consumers so they are present at analyzer load time; for a plain library
reference that pushed netstandard2.0 facades into our compile references and
broke 142 span-using tests with CS0433. The target is now conditional, so the
analyzer path (Semantics.Quantities) keeps the bundling it needs.
- ktsu.CodeBlocker is referenced with ExcludeAssets="compile" - needed at run
time because the generators actually execute, but taking its compile assets
would reintroduce the same facade collision.
Verified with KtsuBuild s own invocation: coverage.xml now contains
Semantics.SourceGenerators.dll alongside the six library modules.
Build clean, 1089/1089 tests pass.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@matt-edmondson