Open sandboxFocusImprove this doc

LAMA0085: Compile-time code holds a reference that keeps the compilation in memory

Severity Warning
Reported by Metalama.Framework
Message The compile-time type '<UserType>' holds a reference to a '<PinnedType>', which pins the Roslyn compilation. At design time, the objects that hold this reference outlive the compilation, therefore the reference prevents that version of the project from being released while the solution is open. The chain of references is: <Path>. Store a durable reference, obtained with ToDurableRef() or IRef.ToDurable(), instead, and resolve it against the current compilation with GetTarget().

Cause

In the IDE, Metalama doesn't run your compile-time code on every keystroke. It keeps what your fabrics registered, your inheritable aspect instances, and your annotations, and reuses them for later versions of the project. Any object of yours held by these results outlives the compilation it was created from. If one of its fields holds a declaration, a Roslyn symbol, a syntax node, or a non-durable IRef, the whole version of the project behind it can't be released, and the memory of the IDE grows while you edit.

Metalama reports LAMA0085 only when you build with the MetalamaDiagnoseMemoryLeaks MSBuild property set to true. After the pipeline has executed, Metalama walks the object graph reachable from the fabric contributors, the inheritable aspect instances, the transitive contributors, the annotations, and the static fields of your compile-time types. It reports one warning for each object that pins a compilation and is reached through one of your own types, up to 20 warnings. Retentions found only in Metalama's own objects are counted in the summary warning LAMA0086.

In the message, <UserType> is your type that holds the reference, or the static field that holds it, <PinnedType> is the type of the object being held, for instance SourceNamedType, and <Path> is the chain of fields that leads from a long-lived object to it. The part of the chain that you can act on is the segment inside your own types.

This diagnostic is reported only when a dedicated MSBuild property enables an analysis of the object graph, so it can't be shown in an example.

Resolution

  1. Find the field named in the chain of references, in the type given by <UserType>.
  2. Don't store declaration objects in anything that outlives a single pipeline run: a field of a fabric, a variable captured by a lambda that you pass to a query, a static field, or a field of an inheritable aspect marked [NonCompileTimeSerialized].
  3. Store a durable reference instead: declare the field as IDurableRef<T>, fill it with ToDurableRef() on a declaration or ToDurable() on a reference, and resolve it with GetTarget when you need the declaration. When the value must cross a project boundary, store the SerializableDeclarationId returned by ToSerializableId() instead.
  4. Build again with MetalamaDiagnoseMemoryLeaks set, and check that the warning is gone.

The analysis is slow, so pass the property on the command line of a single build rather than setting it in your project file. The Diagnosing memory leaks caused by compile-time code article explains how to read the chain of references and shows an example of the fix.

See also