Skip to content

feat: add lifetime-safety diagnostics (AWT105 captive, AWT106 disposable transient, AWT107 conflicting lifetime)#14

Merged
vbreuss merged 2 commits into
mainfrom
feat/lifetime-safety-diagnostics
Jun 27, 2026
Merged

feat: add lifetime-safety diagnostics (AWT105 captive, AWT106 disposable transient, AWT107 conflicting lifetime)#14
vbreuss merged 2 commits into
mainfrom
feat/lifetime-safety-diagnostics

Conversation

@vbreuss

@vbreuss vbreuss commented Jun 27, 2026

Copy link
Copy Markdown
Member

Three compile-time checks over the resolved object graph. AWT105 (error) flags a captive dependency — a singleton that depends, directly or transitively through transients, on a shorter-lived scoped service it would capture for the container's lifetime. AWT106 (warning) flags a disposable transient, whose root-resolved instances are not released until the container is disposed and so accumulate; resolving it from a scope releases it with the scope. AWT107 (error) flags an implementation registered with two different lifetimes, which coalescing into a single instance would otherwise silently reconcile to the first.

Registering one implementation under several services with the same lifetime stays valid (no AWT107). Generator tests cover each diagnostic, the transitive captive case, and the same-lifetime multi-service case staying clean.

@vbreuss vbreuss self-assigned this Jun 27, 2026
@vbreuss vbreuss added the enhancement New feature or request label Jun 27, 2026
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Test Results

 18 files  ± 0   18 suites  ±0   33s ⏱️ +22s
 57 tests +13   56 ✅ +13  1 💤 ±0  0 ❌ ±0 
275 runs  +39  274 ✅ +39  1 💤 ±0  0 ❌ ±0 

Results for commit d238954. ± Comparison against base commit 10975b1.

This pull request removes 5 and adds 18 tests. Note that renamed tests count towards both.
Awaiten.SourceGenerators.Tests.GeneralTests ‑ DependencyCycle_ReportsAwt102WithThePath
Awaiten.SourceGenerators.Tests.GeneralTests ‑ MissingDependency_ReportsAwt101
Awaiten.SourceGenerators.Tests.GeneralTests ‑ NoAccessibleConstructor_ReportsAwt104
Awaiten.SourceGenerators.Tests.GeneralTests ‑ NotInstantiableImplementation_ReportsAwt103(implementationDeclaration: "public abstract class Foo { }")
Awaiten.SourceGenerators.Tests.GeneralTests ‑ NotInstantiableImplementation_ReportsAwt103(implementationDeclaration: "public interface Foo { }")
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt101MissingDependency ‑ ReportsWhenAConstructorParameterIsNotRegistered
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt102DependencyCycle ‑ ReportsWithThePath
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt103NotInstantiable ‑ ReportsForAnInterfaceOrAbstractImplementation(implementationDeclaration: "public abstract class Foo { }")
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt103NotInstantiable ‑ ReportsForAnInterfaceOrAbstractImplementation(implementationDeclaration: "public interface Foo { }")
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt104NoAccessibleConstructor ‑ ReportsWhenTheOnlyConstructorIsPrivate
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt105CaptiveDependency ‑ DoesNotReportWhenAScopedDependsOnScoped
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt105CaptiveDependency ‑ DoesNotReportWhenASingletonDependsOnASingleton
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt105CaptiveDependency ‑ DoesNotReportWhenATransientDependsOnScoped
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt105CaptiveDependency ‑ NamesTheServiceAliasTheConstructorReferenced
Awaiten.SourceGenerators.Tests.DiagnosticTests+Awt105CaptiveDependency ‑ ReportsWhenASingletonDependsOnScoped
…

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

👽 Mutation Results

Mutation testing badge

Awaiten

Details
File Score Killed Survived Timeout No Coverage Ignored Compile Errors Runtime Errors Total Detected Total Undetected Total Mutants

The final mutation score is NaN%

Coverage Thresholds: high:80 low:60 break:0

…ble transient, AWT107 conflicting lifetime)

Three compile-time checks over the resolved object graph. AWT105 (error) flags a captive dependency — a singleton that depends, directly or transitively through transients, on a shorter-lived scoped service it would capture for the container's lifetime. AWT106 (warning) flags a disposable transient, whose root-resolved instances are not released until the container is disposed and so accumulate; resolving it from a scope releases it with the scope. AWT107 (error) flags an implementation registered with two different lifetimes, which coalescing into a single instance would otherwise silently reconcile to the first.

Registering one implementation under several services with the same lifetime stays valid (no AWT107). Generator tests cover each diagnostic, the transitive captive case, and the same-lifetime multi-service case staying clean.
@vbreuss vbreuss force-pushed the feat/lifetime-safety-diagnostics branch 3 times, most recently from 35ca5a8 to d238954 Compare June 27, 2026 13:56
…lias in AWT105

- AWT107: detect conflicting lifetimes even when the same service type is
  re-registered with a different lifetime; the per-service dedup previously
  skipped the conflict and silently dropped one lifetime
- AWT105: name the service alias the constructor actually referenced rather
  than an arbitrary first service type of the captured implementation
- AWT106: document that it is reported per registration, not per resolution
  site (advisory warning)

Refactor: split the diagnostic tests out of GeneralTests into per-diagnostic
DiagnosticTests partial classes (one file each) and add false-positive
coverage for AWT105 (singleton->singleton, transient/scoped->scoped) and
AWT106 (disposable singleton/scoped, non-disposable transient).

Also tighten the test IAwaitenContainer mock's TryResolve with [NotNullWhen(true)].
@vbreuss vbreuss force-pushed the feat/lifetime-safety-diagnostics branch from d238954 to e292ab6 Compare June 27, 2026 14:00
@vbreuss vbreuss enabled auto-merge (squash) June 27, 2026 14:01
@sonarqubecloud

Copy link
Copy Markdown

@vbreuss vbreuss disabled auto-merge June 27, 2026 14:02
@vbreuss vbreuss merged commit 75a155e into main Jun 27, 2026
9 checks passed
@vbreuss vbreuss deleted the feat/lifetime-safety-diagnostics branch June 27, 2026 14:02
@github-actions

Copy link
Copy Markdown

This is addressed in release v0.1.0.

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

Labels

enhancement New feature or request state: released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant