Releases: joezhuo2/ObjectPoolLinter
Releases · joezhuo2/ObjectPoolLinter
Release list
ObjectPoolLinter 1.5.0
Added
- Code fixes for OPL002, in a new
HiddenAllocationCodeFixProvider. Each is offered only for the shapes
it can rewrite without changing behaviour; the others are still reported, with no fix.- Cache the lambda in a field assigned in Awake():
Run(() => count + 1)becomesRun(_next),
the lambda is assigned to_nextinAwake()(added when the class has none), and each captured
local becomes a field whose declaration turns into an assignment. Offered on aMonoBehaviourfor
lambdas that capture no parameter, call no local function and are not nested in another lambda,
when every captured local is declared alone, with an initializer, outside a loop. - Cache the delegate in a field assigned in Awake():
Action callback = Spawn;becomes
Action callback = _spawn;with_spawn = Spawn;inAwake(). Offered for methods of the class
itself and static methods, not forother.Methodor local functions. - Build the string with a reused StringBuilder:
var text = $"hp: {hp}";becomes
_textBuilder.Clear().Append("hp: ").Append(hp);andvar text = _textBuilder.ToString();, with a
readonly StringBuilderfield andusing System.Text;added when missing. Struct, enum and array
holes are appended as(object)valueso they format as interpolation does. Not offered for
alignment or format clauses, conditionally evaluated strings, or statements that have side effects
before the string. - Replace LINQ with a loop filling a reused List<T>:
Where(...).ToList(),
Select(...).ToList()andWhere(...).Select(...).ToList()over aList<T>or an array, with
single-parameter expression lambdas, become aforloop that clears and fills areadonly List<T>
field, and the result refers to that list.
- Cache the lambda in a field assigned in Awake():
- 15 tests, for 154 in total.
Changed
- docs/rules/OPL002.md has a Code fixes section with before-and-after code and
the exact conditions for each fix. The README, the Unity package README and the NuGet description
mention the new fixes.
Notes
- The new fixes have no fix-all support: each picks a free field name from the document as it stands,
so fixes applied in one batch could pick the same name. - The
StringBuilderfix still allocates the final string, which OPL002 keeps reporting as
StringBuilder.ToString(). The LINQ fix returns the same list on every run, so a result kept past the
frame must be copied.
ObjectPoolLinter 1.4.0
Added
- OPL002 reports explicit string building in hot paths:
string.Concat(...), every overload (two or more strings or objects, theparamsform, and
IEnumerable<string>), asstring.Concat();string.Format(...), every overload including those taking anIFormatProvider, as
string.Format();StringBuilder.ToString()andStringBuilder.ToString(int, int), asStringBuilder.ToString().
Reusing aStringBuilderavoids intermediate strings, but eachToString()still allocates the
result.
- The implicit
paramsarray built for astring.Concatorstring.Formatcall, and the boxing of
value-type arguments passed to one, are part of the call's allocation and are not reported again.
a + b, which compiles tostring.Concat, is still reported once asstring concatenation. - 5 tests, for 139 in total.
Changed
- The OPL002 descriptor description, docs/rules/OPL002.md, the README and the
Unity package README list the new constructs.
ObjectPoolLinter 1.3.0
Added
- OPL002, Hidden allocation in hot path, Info by default. It reports allocations with no
newin
the source, in the same hot paths OPL001 watches: string concatenation (reported once per+chain,
constants excluded) and interpolation, lambdas that capture a local, a parameter orthis,
method groups converted to delegates (static ones only below C# 11, which caches them), implicit
paramsarrays with at least one element, LINQ method chains (reported once, on the outermost call)
and query expressions, boxing of an existing value (object o = count;), and struct calls to
object,ValueTypeorEnummethods the struct does not override. It stays out of OPL001's way:
newexpressions, includingnew Action(Spawn)and a struct boxed as it is created, are not
reported twice. It is Info rather than Warning so it does not bury OPL001; the Unity Console shows it
only afterdotnet_diagnostic.OPL002.severity = warning. Documented in
docs/rules/OPL002.md. - OPL003, Allocating Unity API in hot path, Warning by default. It reports any method or property
getter declared in the coreUnityEnginenamespace that returns an array
(GetComponentsInChildren<T>(),Physics.RaycastAll,Camera.allCameras,Input.touches,
Mesh.vertices), plus reads ofObject.name,Component.tagandGameObject.tag. Buffer-filling
overloads, property writes, and managed packages such asUnityEngine.UIare not matched.
GameObject.Findis not reported, because it allocates nothing. The message names the returned
type:'Physics.RaycastAll' returns a new 'RaycastHit[]' on every call inside the frequently-called method 'Update'.Documented, with a non-allocating replacement for each API, in
docs/rules/OPL003.md. - Both rules honour
object_pool_linter.additional_hot_methodsandobject_pool_linter.excluded_types. - The sample interpolates a string and reads
Camera.allCamerasinUpdate, and its.editorconfig
raises OPL002 to a warning.build/verify-sample.ps1now matches all three rules and identifies each
warning by rule ID as well as allocation and method. - 31 tests for the new rules, for 134 in total.
Changed
- The hot-path detection and
.editorconfigparsing moved out ofObjectPoolAnalyzerinto a shared
HotPathDetector, so the three rules agree on which methods are hot. No behavior change for OPL001. - The README's
Known limitations, the OPL001 page and the Unity package README describe the three
rules and the gaps that remain, replacing the note that these allocations were planned as OPL002+.
ObjectPoolLinter 1.2.0
Added
- OPL001 reads two
.editorconfigoptions. Closes A9.object_pool_linter.additional_hot_methods: comma-separated method names treated as hot paths in
addition to the 18 built-in Unity messages, for custom update loops (Tick,Simulate) and
messages the list leaves out (OnPreCull). A bare name matches on any type with any signature;
Type.Methodlimits the entry to one type.object_pool_linter.excluded_types: comma-separated type names, simple or namespace-qualified,
whose methods are never reported. It applies to methods declared on the listed type, not to
derived types, and wins overadditional_hot_methods.- Names are case-sensitive and options are read per file. Documented under
Configuration, including the caveat that Unity's own editor
compile has not been verified to pass the options to analyzers.
- The sample has an
.editorconfigthat addsTickand excludesLoadingScreen;
build/verify-sample.ps1expects the newTickwarning and none fromLoadingScreen.
ObjectPoolLinter 1.1.0
Added
- OPL001 reports a struct that is boxed as it is created in a hot path:
object o = new MyStruct();,
a cast toobjector an interface, or a struct passed or returned as one. The message names the
target type:'new MyStruct boxed to object' allocates inside the frequently-called method 'Update'.
Previously the value-type filter dropped these before the conversion was considered. Boxing an
existing value (object o = count;),new int?()(which boxes to null) and struct calls to
non-overriddenobjectmethods are still not reported. Closes A8. - On a boxed struct, the TODO-comment fix suggests keeping the value typed as the struct or reusing a
single box. The object-poolGet()fix is not offered there, because a pooled struct is boxed
again at the same conversion. - The sample boxes a
Vector3inUpdate;build/verify-sample.ps1expects the new warning.
Changed
- Removed a dead clause from the value-type check in the OPL001 analyzer:
type.IsValueType && type is not IArrayTypeSymbolis nowtype.IsValueType. Array types are
never value types, so the second clause could not change the result. No behavior change.
Closes A7.
ObjectPoolLinter 1.0.0
First published release: the ObjectPoolLinter package on nuget.org, plus the Unity
.unitypackage and UPM .tgz attached to the GitHub release. The 0.x versions below were never
published.
Changed
- The OPL001 message names the allocation consistently and puts it first:
'new List<int>' allocates inside the frequently-called method 'Update'.Allocations are named
from the resolved type rather than the source text, sonew System.Collections.Generic.List<int>(),
new List<int>()andnew()all readnew List<int>, and arrays read as their type
(new int[]) instead of echoing the size (int[10]).Instantiatecalls read
'Instantiate' allocates inside ...rather than'Instantiate' is allocated inside ....
Anything that parses the message text needs the new wording;build/verify-sample.ps1is updated. - The message format arguments are in reading order:
{0}is the allocation,{1}the method.
Closes A6. - OPL001 moved from
AnalyzerReleases.Unshipped.mdto aRelease 1.0.0section in
AnalyzerReleases.Shipped.md.
Build
- The release workflow publishes to nuget.org with Trusted Publishing instead of the static
NUGET_API_KEYsecret.NuGet/login@v1exchanges the job's GitHub OIDC token for a one-hour API
key, so no long-lived key is stored. The workflow now requestsid-token: writeand reads the
nuget.org profile name from theNUGET_USERsecret.