Skip to content

Commit 2440d5e

Browse files
committed
v0.9.5
1 parent 27c483e commit 2440d5e

9 files changed

Lines changed: 120 additions & 5 deletions

File tree

.github/workflows/build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ jobs:
4040
--logger "trx;LogFileName=test-results.trx"
4141
--results-directory artifacts/test-results
4242
43+
- name: Verify sample diagnostics
44+
shell: pwsh
45+
run: ./build/verify-sample.ps1 -Configuration Release -NoDependencies
46+
4347
- name: Pack
4448
run: dotnet pack src/ObjectPoolLinter.Package/ObjectPoolLinter.Package.csproj -c Release --no-build -o artifacts/nuget
4549

CHANGELOG.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [v0.9.5] - 2026-09-12
11+
12+
### Build
13+
- `samples/SampleUnityCode` is part of `ObjectPoolLinter.slnx`, so every solution build and every CI
14+
run compiles it. Previously the sample was outside the solution and nothing built it.
15+
- New `build/verify-sample.ps1` rebuilds the sample and asserts it produces exactly the expected
16+
OPL001 warnings: the two `List` allocations and the `Instantiate` call in `Update`, and the array
17+
in `FixedUpdate`. Warnings are matched by allocation and method name, not by line, and the match is
18+
exact, so both a lost warning and a new false positive (in `Start`, in a struct allocation, or in a
19+
class that is not a `MonoBehaviour`) fail the check. The build workflow runs it after the tests.
20+
Closes C4.
21+
- The sample keeps OPL001 as a warning under `-warnaserror` (`WarningsNotAsErrors` in its project
22+
file) so CI can assert the warnings instead of failing on them.
23+
24+
### Fixed
25+
- The sample no longer produces compiler warnings of its own under `Nullable` and `-warnaserror`:
26+
the Unity stubs return `null!`, `PlayerBehaviour.prefab` is initialized, and the unused `Vector3`
27+
local in `Update2` is discarded.
28+
1029
## [v0.9.4] - 2026-09-12
1130

1231
### Build
@@ -454,7 +473,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
454473
### Removed
455474
- Empty placeholder test `tests/ObjectPoolLinter.Tests/UnitTest1.cs`.
456475

457-
[Unreleased]: https://github.com/joezhuo2/ObjectPoolLinter/compare/v0.9.4...HEAD
476+
[Unreleased]: https://github.com/joezhuo2/ObjectPoolLinter/compare/v0.9.5...HEAD
477+
[v0.9.5]: https://github.com/joezhuo2/ObjectPoolLinter/compare/v0.9.4...v0.9.5
458478
[v0.9.4]: https://github.com/joezhuo2/ObjectPoolLinter/compare/v0.9.3...v0.9.4
459479
[v0.9.3]: https://github.com/joezhuo2/ObjectPoolLinter/compare/v0.9.2...v0.9.3
460480
[v0.9.2]: https://github.com/joezhuo2/ObjectPoolLinter/compare/v0.9.1...v0.9.2

ObjectPoolLinter.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<Project Path="src/ObjectPoolLinter.CodeFixes/ObjectPoolLinter.CodeFixes.csproj" />
55
<Project Path="src/ObjectPoolLinter.Package/ObjectPoolLinter.Package.csproj" />
66
</Folder>
7+
<Folder Name="/samples/">
8+
<Project Path="samples/SampleUnityCode/SampleUnityCode.csproj" />
9+
</Folder>
710
<Folder Name="/tests/">
811
<Project Path="tests/ObjectPoolLinter.Tests/ObjectPoolLinter.Tests.csproj" />
912
</Folder>

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ dotnet build ObjectPoolLinter.slnx -c Release
114114
dotnet test ObjectPoolLinter.slnx -c Release
115115
```
116116

117+
The solution includes `samples/SampleUnityCode`, a small MonoBehaviour compiled against Unity stubs
118+
with the analyzer attached. Building the solution prints its OPL001 warnings; those are expected. To
119+
check that the sample reports exactly the warnings it should, as CI does:
120+
121+
```
122+
pwsh build/verify-sample.ps1
123+
```
124+
117125
This SDK requirement applies only to building this repository. Projects that consume the analyzer
118126
need only the hosts listed under [Requirements](#requirements).
119127

build/verify-sample.ps1

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<#
2+
.SYNOPSIS
3+
Builds samples/SampleUnityCode and asserts it produces exactly the expected OPL001 warnings.
4+
5+
.DESCRIPTION
6+
The sample is the end-to-end check that the analyzer loads from a ProjectReference and reports
7+
through a real compilation, not just through the test harness. This script rebuilds it, collects
8+
every distinct OPL001 warning from the build output, and compares them against the list below.
9+
10+
A diagnostic is identified by the allocated expression and the enclosing method, as they appear in
11+
the message, rather than by line and column, so reformatting the sample does not break the check.
12+
The comparison is exact: a missing warning (regression) and an unexpected one (false positive, for
13+
example in Start or in a class that is not a MonoBehaviour) both fail.
14+
15+
.PARAMETER Configuration
16+
Build configuration. Defaults to Release.
17+
18+
.PARAMETER NoDependencies
19+
Rebuild only the sample and reuse the analyzer assemblies already built. CI passes this after the
20+
solution build; locally, leave it off so the analyzer is built first.
21+
#>
22+
[CmdletBinding()]
23+
param(
24+
[string]$Configuration = 'Release',
25+
[switch]$NoDependencies
26+
)
27+
28+
Set-StrictMode -Version Latest
29+
$ErrorActionPreference = 'Stop'
30+
31+
# One entry per OPL001 warning in samples/SampleUnityCode/SampleBehaviour.cs: '<allocation> in <method>'.
32+
$expected = @(
33+
'System.Collections.Generic.List<int> in Update'
34+
'List<string> in Update'
35+
'Instantiate in Update'
36+
'int[10] in FixedUpdate'
37+
)
38+
39+
$repoRoot = Split-Path -Parent $PSScriptRoot
40+
$sampleProject = Join-Path $repoRoot 'samples/SampleUnityCode/SampleUnityCode.csproj'
41+
42+
# --no-incremental forces the compiler to run, so warnings are reported even when the sample is
43+
# already up to date. -warnaserror matches CI; the sample exempts OPL001 in its project file.
44+
$buildArgs = @('build', $sampleProject, '-c', $Configuration, '--nologo', '--no-incremental', '-warnaserror', '-clp:NoSummary')
45+
if ($NoDependencies) { $buildArgs += '--no-dependencies' }
46+
47+
Write-Host "dotnet $($buildArgs -join ' ')"
48+
$output = & dotnet @buildArgs 2>&1 | ForEach-Object { "$_" }
49+
$exitCode = $LASTEXITCODE
50+
$output | Write-Host
51+
if ($exitCode -ne 0) { throw "Sample build failed with exit code $exitCode." }
52+
53+
# MSBuild can echo a warning more than once; key on file(line,col) to count each diagnostic once.
54+
$pattern = '^(?<location>.+?\(\d+,\d+\)): warning OPL001: ''(?<allocation>.+?)'' is allocated inside the frequently-called method ''(?<method>.+?)''\.'
55+
$byLocation = [ordered]@{}
56+
foreach ($line in $output) {
57+
$match = [regex]::Match($line, $pattern)
58+
if ($match.Success) {
59+
$byLocation[$match.Groups['location'].Value.Trim()] = "$($match.Groups['allocation'].Value) in $($match.Groups['method'].Value)"
60+
}
61+
}
62+
$actual = @($byLocation.Values)
63+
64+
$missing = @($expected | Where-Object { $actual -notcontains $_ })
65+
$unexpected = @($actual | Where-Object { $expected -notcontains $_ })
66+
67+
if ($missing.Count -gt 0 -or $unexpected.Count -gt 0 -or $actual.Count -ne $expected.Count) {
68+
$message = "Sample OPL001 warnings do not match. Expected $($expected.Count), found $($actual.Count)."
69+
if ($missing.Count -gt 0) { $message += "`n Missing:`n " + ($missing -join "`n ") }
70+
if ($unexpected.Count -gt 0) { $message += "`n Unexpected:`n " + ($unexpected -join "`n ") }
71+
throw $message
72+
}
73+
74+
Write-Host "Sample produced the $($expected.Count) expected OPL001 warnings:"
75+
$expected | ForEach-Object { Write-Host " $_" }

samples/SampleUnityCode/SampleBehaviour.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
public class PlayerBehaviour : MonoBehaviour
44
{
5-
public UnityEngine.Object prefab;
5+
public UnityEngine.Object prefab = null!;
66

77
// HOT PATH - should trigger OPL001 warnings
88
void Update()
@@ -31,6 +31,7 @@ void Start()
3131
void Update2()
3232
{
3333
var v = new Vector3();
34+
_ = v;
3435
}
3536
}
3637

samples/SampleUnityCode/SampleUnityCode.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
8+
<!-- The sample exists to produce OPL001. Keep those as warnings even when CI builds the
9+
solution with -warnaserror, so build/verify-sample.ps1 can assert them. -->
10+
<WarningsNotAsErrors>$(WarningsNotAsErrors);OPL001</WarningsNotAsErrors>
11+
<MSBuildWarningsNotAsErrors>$(MSBuildWarningsNotAsErrors);OPL001</MSBuildWarningsNotAsErrors>
812
</PropertyGroup>
913

1014
<ItemGroup>

samples/SampleUnityCode/UnityStubs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ namespace UnityEngine
44
{
55
public class Object
66
{
7-
public static Object Instantiate(Object original) => null;
8-
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation) => null;
7+
public static Object Instantiate(Object original) => null!;
8+
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation) => null!;
99
}
1010

1111
public struct Vector3 { }

src/ObjectPoolLinter.Package/ObjectPoolLinter.Package.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<PropertyGroup>
1111
<PackageId>ObjectPoolLinter</PackageId>
12-
<Version>0.9.4</Version>
12+
<Version>0.9.5</Version>
1313
<Authors>Joe Zhuo</Authors>
1414
<Copyright>Copyright (c) 2026 Joe Zhuo</Copyright>
1515
<Description>Roslyn analyzer for Unity C# that flags object allocations in hot paths and offers code fixes that route them through an object pool, minimizing GC and frame hitches.</Description>

0 commit comments

Comments
 (0)