From 93e48e1ed4e6f1fec6e918f2a27501d3d4540fb8 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 22 Aug 2026 23:08:33 +1000 Subject: [PATCH] Keep the whole NoWarn list, and each target framework once Directory.Build.props declared NoWarn twice. The second declaration replaced the first rather than adding to it, so CS0649, NU1608 and NU1109 were suppressed nowhere - and with TreatWarningsAsErrors on, the next unassigned field would have failed the build rather than warned in it. One list now, with everything both had. DiffEngine.csproj also listed net9.0 and net10.0 in both of its TargetFrameworks lines. MSBuild collapses that, so it built the same set, but the file said something it did not mean. Neither shows up in a build, which is why the two rules are asserted in a test instead. --- src/DiffEngine.Tests/BuildFileTests.cs | 58 ++++++++++++++++++++++++++ src/DiffEngine/DiffEngine.csproj | 5 ++- src/Directory.Build.props | 8 +++- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 src/DiffEngine.Tests/BuildFileTests.cs diff --git a/src/DiffEngine.Tests/BuildFileTests.cs b/src/DiffEngine.Tests/BuildFileTests.cs new file mode 100644 index 00000000..71db86fc --- /dev/null +++ b/src/DiffEngine.Tests/BuildFileTests.cs @@ -0,0 +1,58 @@ +/// +/// Two things about the build files that a build cannot tell anyone. A second property of the same +/// name replaces the first rather than adding to it, and a target framework listed twice is +/// collapsed, so both mistakes build clean and say nothing. +/// +public class BuildFileTests +{ + /// + /// There were two, so only the later list was in force and CS0649, NU1608 and NU1109 were + /// suppressed nowhere - which under TreatWarningsAsErrors is a build failure waiting for the + /// first unassigned field. + /// + [Test] + public async Task NoWarn_is_declared_once() + { + var props = await File.ReadAllTextAsync(Path.Combine(Source(), "Directory.Build.props")); + + var declarations = props.Split([""], StringSplitOptions.None).Length - 1; + + await Assert.That(declarations).IsEqualTo(1); + } + + [Test] + public async Task No_target_framework_is_listed_twice() + { + var project = await File.ReadAllTextAsync(Path.Combine(Source(), "DiffEngine", "DiffEngine.csproj")); + + var listed = project + .Split('\n') + .Where(_ => _.Contains(" _[(_.IndexOf('>') + 1)..^"".Length].Split(';')) + .Where(_ => _.StartsWith("net", StringComparison.Ordinal)) + .ToList(); + + await Assert.That(listed).IsNotEmpty(); + await Assert.That(listed.Distinct()).IsEquivalentTo(listed); + } + + /// + /// The src directory, found by walking up from the test output rather than by counting + /// directories, which differs per target framework and configuration. + /// + static string Source() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory != null) + { + if (File.Exists(Path.Combine(directory.FullName, "Directory.Build.props"))) + { + return directory.FullName; + } + + directory = directory.Parent; + } + + throw new("Could not find Directory.Build.props above the test output."); + } +} diff --git a/src/DiffEngine/DiffEngine.csproj b/src/DiffEngine/DiffEngine.csproj index 77f0320f..e5badf6f 100644 --- a/src/DiffEngine/DiffEngine.csproj +++ b/src/DiffEngine/DiffEngine.csproj @@ -1,7 +1,8 @@ - net462;net472;net48;net9.0;net10.0 - $(TargetFrameworks);net6.0;net7.0;net8.0;net9.0;net10.0 + net6.0;net7.0;net8.0;net9.0;net10.0 + + net462;net472;net48;$(TargetFrameworks) true true diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 939fab26..c6e07baf 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,6 @@ - CS1591;CS0649;NU1608;NU1109 20.0.0-beta.30 1.0.0 Testing, Snapshot, Diff, Compare @@ -14,7 +13,12 @@ false true true - CA1416;CS1591 + + CS1591;CS0649;NU1608;NU1109;CA1416 true true true