Skip to content

feat(analyzers): suggest state and a static lambda for capturing generators - #248

Open
cosmin-staicu wants to merge 1 commit into
mainfrom
pr/cache-analyzer
Open

cosmin-staicu wants to merge 1 commit into
mainfrom
pr/cache-analyzer

Conversation

@cosmin-staicu

@cosmin-staicu cosmin-staicu commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

UIPCACHE001 (category UiPath.Caching, Info) reports a generator that allocates on every
call, hits included, passed to GetOrAddAsync or GetOrAddWithExpirationAsync on
ICache, ICache, IHashCache or IHashCache, an extension of one, or a class
member that implements one: a lambda that captures this, a local or a
parameter, itself or through a local function it calls, an instance method
group, or a local function that captures; and a capturing lambda passed to a
state overload. A name used only in nameof is not a capture. It reports only
where a state overload exists for the call, the single-key state or the batch
argument, found by comparing parameter symbols with the closure overload's,
the sibling's own type parameters bound as it goes.

The fix moves the call to that overload with a static lambda: one capture is
the state (this becomes self, implicit member access goes through it), several
become a named tuple, a method group's target is passed, and spelled type
arguments get the state's type. It is not offered when the state, read once at
the call, could differ from what the lambda read when it ran (a captured
variable assigned in the method, a primary constructor parameter assigned in
the type, base, a capturing local function, a captured local named by
inference or nameof), for a call on a class whose state overload is only on the
interface (a class of the caller's own; the library's public caches declare
theirs), or below C# 9; and only when the rewritten call, compiled in place,
binds to the state overload without an error. Fix All uses the batch fixer; a
generator inside another is fixed on the next pass. The rule has a category of
its own, so bulk configuration of Performance does not raise it.

The analyzer and the fix (apart, so the analyzer does not reference
Workspaces) target netstandard2.0 on Microsoft.CodeAnalysis 4.8, the .NET 8
SDK's compiler, and ship in the UiPath.Caching.Abstractions package under
analyzers/dotnet/cs, packed from the analyzer projects' own outputs; NuGet carries a dependency's analyzers to every package
built on it. The blocking GetOrAdd wrappers pass their generator as state.

The repository runs the analyzer on itself: src and samples reference both
projects as analyzers, with UIPCACHE001 a warning there.

@github-actions github-actions Bot added the needs-cla-review A maintainer should assess whether a signed CLA is required (see CONTRIBUTING.md) label Oct 16, 2026
@github-actions

Copy link
Copy Markdown

🔎 Maintainer heads-up: automated triage flagged this PR as potentially material, so it may need a signed CLA in addition to the DCO sign-off.

Strong signals

  • adds public API surface (PublicAPI.Unshipped.txt in src/UiPath.Caching.Analyzers.CodeFixes, src/UiPath.Caching.Analyzers)
  • changes shipped public API — possible removal/breaking change (PublicAPI.Shipped.txt in src/UiPath.Caching.Analyzers.CodeFixes, src/UiPath.Caching.Analyzers)
  • adds dependencies: Microsoft.CodeAnalysis.CSharp, Microsoft.CodeAnalysis.CSharp.Workspaces, Microsoft.CodeAnalysis.Analyzers, Microsoft.CodeAnalysis.CSharp.CodeFix.Testing
  • adds new project(s): src/UiPath.Caching.Analyzers.CodeFixes/UiPath.Caching.Analyzers.CodeFixes.csproj, src/UiPath.Caching.Analyzers/UiPath.Caching.Analyzers.csproj

Other signals

  • large production change (+997 lines under src/)

This is advisory only — the bot does not decide. Please judge against the CLA criteria (material, product-critical, patent-sensitive, corporate contributor, broad commercial use). Note that thresholds can be gamed by splitting PRs, so use your judgement.

  • If a CLA is needed → add the cla-required label (a contributor comment with signing steps is posted automatically).
  • If it is not needed → replace needs-cla-review with cla-not-required so later pushes don't re-flag it.

@cosmin-staicu
cosmin-staicu force-pushed the feat/cache-state-overloads branch from 401e714 to 50b4f5d Compare October 16, 2026 10:18
@cosmin-staicu
cosmin-staicu force-pushed the feat/cache-state-overloads branch from 50b4f5d to 1c90388 Compare October 16, 2026 13:13
@cosmin-staicu
cosmin-staicu force-pushed the feat/cache-state-overloads branch from 1c90388 to 5fd4a97 Compare October 16, 2026 13:44
@cosmin-staicu
cosmin-staicu force-pushed the feat/cache-state-overloads branch from 5fd4a97 to 8993858 Compare October 16, 2026 14:10
@cosmin-staicu
cosmin-staicu force-pushed the feat/cache-state-overloads branch from 8993858 to da7c321 Compare October 16, 2026 14:19
Base automatically changed from feat/cache-state-overloads to main October 16, 2026 14:37
@cosmin-staicu
cosmin-staicu requested a balanced review from Copilot October 16, 2026 14:43
@cosmin-staicu cosmin-staicu added cla-not-required Maintainer reviewed: no CLA required for this contribution and removed needs-cla-review A maintainer should assess whether a signed CLA is required (see CONTRIBUTING.md) labels Oct 16, 2026
@cosmin-staicu cosmin-staicu self-assigned this Oct 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Several code-fix paths can change runtime behavior or produce uncompilable code.

9 open findings
What changed in this PR

Adds UIPCACHE001 to identify allocating cache generators and suggest state-based static lambdas.

Changes:

  • Adds and packages the analyzer and code fix.
  • Converts synchronous wrappers to state overloads.
  • Adds tests, documentation, and repository-wide analyzer configuration.
File Description
UiPath.Caching.slnx Registers analyzer and test projects.
tests/​UiPath.Caching.Tests/​SyncExtensionsTests.cs Tests synchronous wrappers.
tests/​UiPath.Caching.Analyzers.Tests/​Verify.cs Provides analyzer test harness.
tests/​UiPath.Caching.Analyzers.Tests/​UiPath.Caching.Analyzers.Tests.csproj Configures analyzer tests.
tests/​UiPath.Caching.Analyzers.Tests/​CapturingGeneratorTests.cs Tests diagnostics and rewrites.
src/​UiPath.Caching.Analyzers/​UiPath.Caching.Analyzers.csproj Configures analyzer assembly.
src/​UiPath.Caching.Analyzers/​Sibling.cs Describes matching state overloads.
src/​UiPath.Caching.Analyzers/​PublicAPI.Unshipped.txt Records new analyzer API.
src/​UiPath.Caching.Analyzers/​PublicAPI.Shipped.txt Initializes API baseline.
src/​UiPath.Caching.Analyzers/​GeneratorCaptures.cs Identifies captured values.
src/​UiPath.Caching.Analyzers/​CapturingGeneratorAnalyzer.cs Implements UIPCACHE001 diagnostics.
src/​UiPath.Caching.Analyzers/​CacheMethods.cs Recognizes cache methods and overloads.
src/​UiPath.Caching.Analyzers/​AnalyzerReleases.Unshipped.md Registers the new diagnostic.
src/​UiPath.Caching.Analyzers/​AnalyzerReleases.Shipped.md Initializes release tracking.
src/​UiPath.Caching.Analyzers.CodeFixes/​UiPath.Caching.Analyzers.CodeFixes.csproj Separates IDE dependencies.
src/​UiPath.Caching.Analyzers.CodeFixes/​StateRewrite.cs Generates state-based static lambdas.
src/​UiPath.Caching.Analyzers.CodeFixes/​PublicAPI.Unshipped.txt Records code-fix API.
src/​UiPath.Caching.Analyzers.CodeFixes/​PublicAPI.Shipped.txt Initializes API baseline.
src/​UiPath.Caching.Analyzers.CodeFixes/​PassStateCodeFixProvider.cs Registers fixes and Fix All.
src/​UiPath.Caching.Abstractions/​UiPath.Caching.Abstractions.csproj Packages analyzer assemblies.
src/​UiPath.Caching.Abstractions/​HashCacheSyncExtensions.cs Passes hash generators as state.
src/​UiPath.Caching.Abstractions/​CacheSyncExtensions.cs Passes cache generators as state.
docs/​how-to/​choosing-a-get-or-add-overload.md Documents diagnostics and fixes.
Directory.Packages.props Adds Roslyn dependencies.
Directory.Build.props Wires repository analyzer references.
CHANGELOG.md Announces analyzer support.
.editorconfig Sets diagnostic severity by scope.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs
Comment thread src/UiPath.Caching.Analyzers/CacheMethods.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers/CacheMethods.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers/CacheMethods.cs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/UiPath.Caching.Analyzers/CacheMethods.cs
Comment thread src/UiPath.Caching.Analyzers/UiPath.Caching.Analyzers.csproj
Comment thread src/UiPath.Caching.Abstractions/UiPath.Caching.Abstractions.csproj Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs
Comment thread src/UiPath.Caching.Analyzers/GeneratorCaptures.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
Comment thread src/UiPath.Caching.Analyzers.CodeFixes/StateRewrite.cs Outdated
@cosmin-staicu
cosmin-staicu force-pushed the pr/cache-analyzer branch 2 times, most recently from 22c49e7 to 1d48db9 Compare October 16, 2026 21:58
…rators

UIPCACHE001 (category UiPath.Caching, Info) reports a generator that allocates on every
call, hits included, passed to GetOrAddAsync or GetOrAddWithExpirationAsync on
ICache, ICache<T>, IHashCache or IHashCache<T>, an extension of one, or a class
member that implements one: a lambda that captures this, a local or a
parameter, itself or through a local function it calls, an instance method
group, or a local function that captures; and a capturing lambda passed to a
state overload. A name used only in nameof is not a capture. It reports only
where a state overload exists for the call, the single-key state or the batch
argument, found by comparing parameter symbols with the closure overload's,
the sibling's own type parameters bound as it goes.

The fix moves the call to that overload with a static lambda: one capture is
the state (this becomes self, implicit member access goes through it), several
become a named tuple, a method group's target is passed, and spelled type
arguments get the state's type. It is not offered when the state, read once at
the call, could differ from what the lambda read when it ran (a captured
variable assigned in the method, a primary constructor parameter assigned in
the type, base, a capturing local function, a captured local named by
inference or nameof), for a call on a class whose state overload is only on the
interface (a class of the caller's own; the library's public caches declare
theirs), or below C# 9; and only when the rewritten call, compiled in place,
binds to the state overload without an error. Fix All uses the batch fixer; a
generator inside another is fixed on the next pass. The rule has a category of
its own, so bulk configuration of Performance does not raise it.

The analyzer and the fix (apart, so the analyzer does not reference
Workspaces) target netstandard2.0 on Microsoft.CodeAnalysis 4.8, the .NET 8
SDK's compiler, and ship in the UiPath.Caching.Abstractions package under
analyzers/dotnet/cs, packed from the analyzer projects' own outputs; NuGet carries a dependency's analyzers to every package
built on it. The blocking GetOrAdd wrappers pass their generator as state.

The repository runs the analyzer on itself: src and samples reference both
projects as analyzers, with UIPCACHE001 a warning there.

Signed-off-by: Cosmin Staicu <cosmin.staicu@uipath.com>
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

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

Labels

cla-not-required Maintainer reviewed: no CLA required for this contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants