HostLoom.Analyzers 0.5.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package HostLoom.Analyzers --version 0.5.0
                    
NuGet\Install-Package HostLoom.Analyzers -Version 0.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HostLoom.Analyzers" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HostLoom.Analyzers" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="HostLoom.Analyzers" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add HostLoom.Analyzers --version 0.5.0
                    
#r "nuget: HostLoom.Analyzers, 0.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package HostLoom.Analyzers@0.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=HostLoom.Analyzers&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=HostLoom.Analyzers&version=0.5.0
                    
Install as a Cake Tool

HostLoom.Analyzers

Roslyn analyzers for correct asynchronous and dependency-injection usage of HostLoom. The package contains compiler analyzers only: it adds no runtime assembly or dependency to the application.

dotnet add package HostLoom.Analyzers

For central package management:

<PackageReference Include="HostLoom.Analyzers" PrivateAssets="all" />

Rules

HLM0001

Pass an available cancellation token to HostLoom asynchronous calls. The analyzer recognizes both a CancellationToken parameter and an IPipeContext.CancellationToken available from an enclosing pipeline filter or helper. The cache and lock contracts (ICache, IDistributedLock, ILockHandle) are recognized by name, so the rule covers them whether the call goes through the real package or a test stub of the interface.

await client.GetResponseAsync(
    "orders",
    request,
    cancellationToken: cancellationToken
);

HLM0002

Await HostLoom asynchronous operations instead of blocking through .Result, .Wait(), or .GetAwaiter().GetResult(). The rule also recognizes .AsTask() over a HostLoom ValueTask, and covers the cache and lock contracts, whose members all return ValueTask.

HLM0003

Register implementations of IRequestHandler<,>, IEventHandler<>, and IRequestBehavior<,> as scoped services. HostLoom creates a dependency-injection scope per delivery; a singleton handler or behavior can share mutable state between deliveries or capture scoped dependencies. AddHandler, AddSubscriber, and AddBehavior already apply the correct scoped lifetime.

HLM0004

Assign every settable member of a mapped destination. An explicit map makes a forgotten member a silent data loss rather than a compile error — nothing requires the member to be written — which is the one axis on which explicit mapping is less safe than the convention mapping it replaces. A member supplied through the destination's constructor counts as assigned, so positional records and contracts with real constructors need nothing extra.

Omitting a member is often correct. Name each one, so that a member added to the contract later is still reported rather than excused by a blanket marker:

[UnmappedMembers(nameof(CfaTransfer.CardMask), nameof(CfaTransfer.ProviderId))]
public sealed class TransferModelToCfaTransferMapper : IMapper<TransferModel, CfaTransfer>

HLM0005

Keep a Map body in a shape completeness can be verified in. Two are recognised:

  • the destination constructed and returned directly, with an object initializer;
  • one local constructed, assigned into across any number of statements and branches, and returned.

A body outside both — the local passed to a method, built across two locals, or returned from somewhere else — is reported rather than skipped, so that "not checked" is never mistaken for "checked and complete". Conditional assignment counts as assigned: the rule targets forgotten members, and evidence that the author considered one is the whole signal.

Known blind spot. A map whose destination is a type parameter — a generic map class closed through MappingBuilder.Add<TSource, TDestination>(factory) — cannot have its members enumerated, so it is skipped silently and reports neither rule. Completeness of a generic map is not checked.

HLM0006

Do not capture the scoped IMapper dispatcher in a singleton. AddHostLoomMapping registers the non-generic dispatcher scoped, so a singleton or an IHostedService that takes one is a captive dependency — and the failure is asymmetric: it throws at host build where scope validation is enabled, the generic host's default in Development, and succeeds where it is not. Left to run time it is a Development-only failure that Production would not reproduce.

Inject the closed IMapper<TSource, TDestination> instead, which is transient and carries no such restriction, or take IServiceScopeFactory and resolve per unit of work. The rule reports only the non-generic dispatcher; the closed map it points at is never reported.

HLM0007

Hash a secret before it becomes part of a cache or lock key. A key is written to the backend, appears in logs, and is tagged on spans, so a key built from a token, secret, password, or API key copies the credential into all three. The rule reports an interpolation hole, a concatenation operand, or a string.Format, string.Concat, or string.Join argument of the key (or keys) argument of an ICache or IDistributedLock member that references a parameter, local, field, or property whose name contains token, secret, password, refreshToken, or apiKey. Wrap the value in CacheKey.FromSensitive or LockKey.FromSensitive, which hashes it so the key stays unique without carrying the secret:

await cache.GetOrCreateAsync(
    $"session:{CacheKey.FromSensitive(refreshToken)}",
    token => LoadSessionAsync(token),
    TimeSpan.FromMinutes(5),
    cancellationToken
);

A key that is entirely a FromSensitive call is never reported. A CancellationToken operand is ignored even though its name contains token.

HLM0008

Forward the cancellation token a get-or-create factory receives. GetOrCreateAsync hands the caller's token to the factory so the work it starts stops with the request; a factory that names the token and never uses it keeps running after the caller has gone, holding the per-key guard while it does. The rule reports a lambda, anonymous method, or method group whose CancellationToken parameter is never referenced in its body, including from nested lambdas. Name the parameter _ to state that the factory has nothing to cancel.

await cache.GetOrCreateAsync("catalog", token => LoadCatalogAsync(token), options, cancellationToken);
await cache.GetOrCreateAsync("constant", _ => ValueTask.FromResult(42), options, cancellationToken);

Configuration

All rules are warnings by default and use the HLM diagnostic prefix. Standard .editorconfig configuration can change a rule's severity:

dotnet_diagnostic.HLM0001.severity = error

Generated code is ignored, and analyzer execution is safe for concurrent compiler execution.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.20.0 226 10/31/2026
0.19.0 109 10/29/2026
0.18.0 135 10/27/2026
0.17.0 149 10/25/2026
0.16.1 134 10/22/2026
0.16.0 178 10/14/2026
0.15.1 128 10/12/2026
0.15.0 183 10/4/2026
0.14.0 106 10/4/2026
0.13.0 138 9/28/2026
0.12.0 141 9/27/2026
0.11.0 138 9/25/2026
0.10.0 131 9/22/2026
0.9.0 113 9/22/2026
0.8.0 145 9/19/2026
0.7.0 125 9/17/2026
0.6.0 126 9/6/2026
0.5.0 120 9/5/2026
0.4.0 120 9/3/2026
0.3.0 130 8/28/2026
Loading failed