Skip to content

Diagnostics & Code Fixes

The ExpressiveSharp source generator and companion analyzers emit diagnostics during compilation to help you identify and fix issues. Many diagnostics also have IDE code fixes that resolve them automatically.

Looking for help with a specific problem?

See Troubleshooting for symptom-oriented guidance -- find the error message or behavior you see and get step-by-step resolution.

Retired diagnostics

EXP0016 ("[ExpressiveFor] stub must be static") has been retired. Instance stubs on the target type are now permitted; constructor-stub and unrelated-type mismatches surface as EXP0015 (member not found) instead.

Overview

IDSeverityTitleCode Fix
EXP0001ErrorMember must have a body definition--
EXP0002ErrorMissing parameterless constructor--
EXP0003ErrorDelegated constructor cannot be analyzed--
EXP0004ErrorBlock body requires opt-in--
EXP0005ErrorSide effects in block body--
EXP0006WarningUnsupported statement in block body--
EXP0007WarningUnsupported initializer in object creation--
EXP0008WarningUnsupported expression operation--
EXP0009WarningUnsupported operator--
EXP0010WarningInterceptor emission failed--
EXP0011WarningUnresolvable member in pattern--
EXP0012InfoFactory method can be converted to constructor--
EXP0013WarningReferenced member could benefit from [Expressive]Add [Expressive]
EXP0014Error[ExpressiveFor] target type not found--
EXP0015Error[ExpressiveFor] target member not found--
EXP0017Error[ExpressiveFor] return type mismatch--
EXP0019Error[ExpressiveFor] conflicts with [Expressive]--
EXP0020ErrorDuplicate [ExpressiveFor] mapping--
EXP0027InfoPlain IQueryable chain references an [Expressive] member without .AsExpressive()Wrap with .AsExpressive()
EXP0031Error[ExpressiveProperty] target name is already defined--
EXP0032Error[ExpressiveProperty] requires a partial containing type--
EXP0033Error[ExpressiveProperty] requires an expression-bodied property stub--
EXP0034Error[ExpressiveProperty] requires an instance stub--
EXP0035Error[ExpressiveProperty] target shadows inherited member--
EXP0036InfoIExpressiveQueryable<T> chain dropped to plain IQueryable<T>--
EXP1001WarningReplace [Projectable] with [Expressive]Replace attribute
EXP1002WarningReplace UseProjectables() with UseExpressives()Replace method call
EXP1003WarningReplace Projectables namespaceReplace namespace

Core Diagnostics (EXP0001--EXP0012)

EXP0001 -- Member must have a body definition

Severity: Error Category: Design

Message:

Method or property '{0}' should expose a body definition (e.g. an expression-bodied member
or a block-bodied method) to be used as the source for the generated expression tree.

Cause: An [Expressive] member has no body -- it is abstract, an interface declaration, or an auto-property.

Fix: Provide a body:

csharp
// Error: no body
[Expressive]
public string FullName { get; set; }

// Fixed: expression-bodied property
[Expressive]
public string FullName => $"{FirstName} {LastName}";

EXP0002 -- Missing parameterless constructor

Severity: Error Category: Design

Message:

Class '{0}' must have a parameterless constructor to be used with an [Expressive] constructor.
The generated projection uses 'new {0}() { ... }' (object-initializer syntax), which requires
an accessible parameterless constructor.

Cause: A constructor is marked [Expressive], but the class does not have an accessible parameterless constructor. The generator emits new T() { ... } syntax which requires one.

Fix: Add a parameterless constructor:

csharp
public class CustomerDto
{
    public CustomerDto() { }  // required

    [Expressive]
    public CustomerDto(Customer c)
    {
        Id = c.Id;
        Name = $"{c.FirstName} {c.LastName}";
    }
}

EXP0003 -- Delegated constructor cannot be analyzed

Severity: Error Category: Design

Message:

The delegated constructor '{0}' in type '{1}' has no source available and cannot be analyzed.
Base/this initializer in member '{2}' will not be projected.

Cause: An [Expressive] constructor delegates to another constructor via : base(...) or : this(...), but the target constructor's source code is not available in the current compilation (e.g., it lives in a referenced binary).

Fix: Ensure the delegated constructor's source is available in the same project, or restructure to avoid the delegation.


EXP0004 -- Block body requires AllowBlockBody

Severity: Error Category: Design

Message:

Member '{0}' uses a block body ({ }) which requires [Expressive(AllowBlockBody = true)].
Block bodies support local variables, if/else, and foreach loops, but not all constructs
are translatable by every LINQ provider. Use an expression-bodied member (=>) for full
compatibility, or opt in with AllowBlockBody = true.

Cause: An [Expressive] member uses a block body { } without opting in.

Fix: Either opt in to block bodies or convert to an expression body:

csharp
// Error: block body without opt-in
[Expressive]
public string GetCategory()
{
    if (Value > 100) return "High";
    return "Low";
}

// Option 1: opt in to block body
[Expressive(AllowBlockBody = true)]
public string GetCategory()
{
    if (Value > 100) return "High";
    return "Low";
}

// Option 2: convert to expression body
[Expressive]
public string GetCategory() => Value > 100 ? "High" : "Low";

You can also enable block bodies globally via MSBuild:

xml
<PropertyGroup>
    <Expressive_AllowBlockBody>true</Expressive_AllowBlockBody>
</PropertyGroup>

EXP0005 -- Side effects in block body

Severity: Error Category: Design

Message: Context-specific (e.g., property assignment, compound assignment, or increment/decrement detected).

Cause: A block-bodied [Expressive] member modifies state. Expression trees cannot represent side effects.

Fix: Remove the side-effecting statement. [Expressive] members must be pure functions.

csharp
// Error: side effects
[Expressive(AllowBlockBody = true)]
public int Compute()
{
    Counter++;       // EXP0005: side effect
    return Counter;
}

// Fixed: pure computation
[Expressive]
public int Compute() => Counter + 1;

EXP0006 -- Unsupported statement in block body

Severity: Warning Category: Design

Message:

Method '{0}' contains an unsupported statement: {1}

Cause: A block-bodied [Expressive] member contains a statement type that cannot be converted to an expression tree (e.g., while loops, try/catch, throw, async/await).

Fix: Refactor to use only supported constructs (if/else, switch, foreach, local variables, return), or convert to an expression-bodied member.


EXP0007 -- Unsupported initializer

Severity: Warning Category: Design

Message:

Object initializer contains an unsupported element ({0}). Only property and field
assignments are supported in expression trees.

Cause: An object initializer in an [Expressive] member contains something other than a property or field assignment (e.g., collection initializer syntax, index initializer).

Fix: Restructure the initializer to use only property and field assignments.


EXP0008 -- Unsupported expression operation

Severity: Warning Category: Design

Message:

Expression contains an unsupported operation ({0}). A default value will be used instead.

Cause: The member body contains an operation that cannot be represented in an expression tree. The generator substitutes a default value to allow compilation to proceed.

Fix: Rewrite the unsupported operation using supported C# constructs. See the Supported C# Features table in the main documentation for what is supported.

WARNING

This diagnostic is a warning, not an error. The generated code will compile, but the defaulted value may produce incorrect results at runtime. Always address EXP0008 warnings.


EXP0009 -- Unsupported operator

Severity: Warning Category: Design

Message:

Operator '{0}' is not supported in expression trees. A default value will be used instead.

Cause: An operator in the expression body has no expression tree equivalent.

Fix: Rewrite using a supported operator or method call.


EXP0010 -- Interceptor emission failed

Severity: Warning Category: Design

Message:

Failed to generate interceptor for call site: {0}. The original delegate stub will be used at runtime.

Cause: The polyfill interceptor generator could not produce an interceptor for a specific call site (e.g., on IExpressiveQueryable<T> or ExpressionPolyfill.Create). The original delegate-based stub will be used instead.

Fix: This is typically an internal generator issue. If you encounter it, check that the call site uses supported syntax and consider filing an issue.


EXP0011 -- Unresolvable member in pattern

Severity: Warning Category: Design

Message:

Pattern sub-expression for member '{0}' could not be resolved and was skipped.
The pattern may not match correctly.

Cause: A property pattern references a member that could not be resolved during analysis. The pattern sub-expression is skipped.

Fix: Ensure the member referenced in the pattern exists and is accessible. Check for typos or missing using directives.


EXP0012 -- Factory method can be converted to a constructor

Severity: Info Category: Design

Message:

Factory method '{0}' creates and returns an instance of the containing class via object
initializer. Consider converting it to an [Expressive] constructor.

Cause: An [Expressive] method creates and returns a new T { ... } object initializer where T is the containing class. This pattern is equivalent to an [Expressive] constructor.

Fix: Convert the factory method to a constructor:

csharp
// Before: factory method (triggers EXP0012)
[Expressive]
public static CustomerDto FromCustomer(Customer c) => new CustomerDto
{
    Id = c.Id,
    Name = $"{c.FirstName} {c.LastName}"
};

// After: expressive constructor
[Expressive]
public CustomerDto(Customer c)
{
    Id = c.Id;
    Name = $"{c.FirstName} {c.LastName}";
}

Analyzer Diagnostic (EXP0013)

EXP0013 -- Referenced member could benefit from [Expressive]

Severity: Warning Category: Design Source: MissingExpressiveAnalyzer (in ExpressiveSharp.CodeFixers)

Message:

Member '{0}' is referenced in an [Expressive] expression but is not marked [Expressive].
Adding [Expressive] would allow its body to be inlined into the expression tree.

Cause: A member referenced inside an [Expressive] body, an ExpressionPolyfill.Create() lambda, or an IExpressiveQueryable LINQ lambda has an expandable body (expression-bodied or block-bodied) but is not marked [Expressive]. Without the attribute, the member call remains opaque in the generated expression tree and cannot be translated by LINQ providers.

Fix:

The IDE offers a code fix that adds [Expressive] to the referenced member automatically (including the using ExpressiveSharp; directive if needed):

csharp
// Warning: Total is referenced in an [Expressive] body but not marked [Expressive]
public double Total => Price * Quantity;

// Fixed: add [Expressive]
[Expressive]
public double Total => Price * Quantity;

TIP

Enum method calls are excluded from this diagnostic -- the generator expands those automatically via per-value ternary chains, so [Expressive] is not needed on the enum extension method.


External Mapping Diagnostics (EXP0014--EXP0020)

These diagnostics are specific to [ExpressiveFor] and [ExpressiveForConstructor]. See [ExpressiveFor] Mapping for full usage details.

EXP0014 -- Target type not found

Severity: Error Category: Design

Message:

[ExpressiveFor] target type '{0}' could not be resolved

Cause: The Type argument passed to [ExpressiveFor] does not resolve to a valid type in the compilation.

Fix: Ensure the type is accessible and correctly spelled. Add the necessary using directive or assembly reference.


EXP0015 -- Target member not found

Severity: Error Category: Design

Message:

No member '{0}' found on type '{1}' matching the stub's parameter signature

Cause: No member with the given name exists on the target type, or no overload matches the stub's parameter types.

Fix: Verify the member name (use nameof(...) to catch typos) and ensure the stub's parameters match the target's signature:

csharp
// Error: wrong parameter types
[ExpressiveFor(typeof(Math), nameof(Math.Clamp))]
static double Clamp(int value, int min, int max) // should be double, not int
    => value < min ? min : (value > max ? max : value);

// Fixed: matching parameter types
[ExpressiveFor(typeof(Math), nameof(Math.Clamp))]
static double Clamp(double value, double min, double max)
    => value < min ? min : (value > max ? max : value);

EXP0017 -- Return type mismatch

Severity: Error Category: Design

Message:

[ExpressiveFor] return type mismatch for '{0}': target returns '{1}' but stub returns '{2}'

Cause: The stub method's return type does not match the target member's return type.

Fix: Align the return types:

csharp
// Error: target Math.Clamp returns double, stub returns int
[ExpressiveFor(typeof(Math), nameof(Math.Clamp))]
static int Clamp(double value, double min, double max) => /* ... */;

// Fixed
[ExpressiveFor(typeof(Math), nameof(Math.Clamp))]
static double Clamp(double value, double min, double max) => /* ... */;

EXP0019 -- Conflicts with [Expressive]

Severity: Error Category: Design

Message:

Target member '{0}' on type '{1}' already has [Expressive]; remove [ExpressiveFor] or [Expressive]

Cause: The target member already has its own [Expressive] attribute. [ExpressiveFor] is meant for members that cannot use [Expressive] directly.

Fix: Remove either [ExpressiveFor] (if the member's own [Expressive] is sufficient) or [Expressive] (if you want the external mapping to take precedence).


EXP0020 -- Duplicate mapping

Severity: Error Category: Design

Message:

Duplicate [ExpressiveFor] mapping for member '{0}' on type '{1}'; only one stub per target member is allowed

Cause: Two or more [ExpressiveFor] stubs target the same member on the same type.

Fix: Remove the duplicate. Only one mapping per target member is allowed.


EXP0027 -- Plain IQueryable chain references an [Expressive] member without .AsExpressive()

Severity: Info Category: Usage

Message:

LINQ method '{0}' on a plain IQueryable<T> references the [Expressive] member '{1}'.
Without .AsExpressive(), the member's body will not be inlined into the expression tree;
the provider may evaluate the call in memory or fail to translate it. Wrap the source
with .AsExpressive().

Cause: A LINQ method on a plain IQueryable<T> receiver (one that is not IExpressiveQueryable<T>) is invoked with a lambda whose body references an [Expressive] member. Because the chain is not expressive-aware, the source generator does not rewrite the lambda into an expression tree that inlines the member's body — the underlying query provider receives a call to the runtime delegate. Most providers cannot translate this and will either evaluate the call client-side (silent overfetch) or throw at execution time.

Fix: Wrap the chain root with .AsExpressive() so that subsequent LINQ methods flow through the ExpressiveSharp delegate-based overloads, which inline [Expressive] member bodies at compile time.

csharp
// Before — IsAdult is silently evaluated on the client.
var adults = users.Where(u => u.IsAdult).ToList();

// After — IsAdult is inlined into the expression tree before the provider sees it.
var adults = users.AsExpressive().Where(u => u.IsAdult).ToList();

When you intentionally want to evaluate a member at runtime (e.g., it captures process state), mark the member with [NotExpressive] to suppress the diagnostic at every call site.

Code Fix: Wrap source with .AsExpressive()

The IDE offers a single code action: Wrap source with .AsExpressive(). It walks the LINQ chain to the leftmost non-LINQ expression, wraps it with .AsExpressive(), and inserts using ExpressiveSharp; if it is not already imported.


[ExpressiveProperty] Diagnostics (EXP0031--EXP0035)

These diagnostics apply to [ExpressiveProperty] stubs, which ask the generator to emit a new property on the stub's containing partial type. See [ExpressiveProperty] Attribute for the full feature reference.

Replacing [Expressive(Projectable = true)]

[ExpressiveProperty] replaces the now-removed [Expressive(Projectable = true)]. Diagnostic codes EXP0021--EXP0026 and EXP0028--EXP0030 were retired along with that feature and are not reused. (EXP0027 has been reassigned to the plain-IQueryable analyzer.) The migration recipe is in Migration from Projectables.

EXP0031 -- Target name is already defined

Severity: Error Category: Design

Message:

[ExpressiveProperty] target name '{0}' is already defined on '{1}' — rename the stub,
or use [ExpressiveFor(nameof({0}))] to map onto the existing member instead

Cause: The name passed to [ExpressiveProperty] already resolves to a member on the containing type. Synthesis would collide with the existing declaration.

Fix: Either rename the stub to pick a different target, or — if you want to bind to the existing property — drop [ExpressiveProperty] and switch to plain [ExpressiveFor(nameof(X))]:

csharp
// Error: Amount already exists on the class
public decimal Amount { get; set; }

[ExpressiveProperty("Amount")]
private decimal AmountExpression => TotalAmount - Discount;

// Fixed: map onto the existing property with [ExpressiveFor]
[ExpressiveFor(nameof(Amount))]
private decimal AmountExpression => TotalAmount - Discount;

EXP0032 -- Requires a partial containing type

Severity: Error Category: Design

Message:

[ExpressiveProperty] requires the containing type '{0}' to be declared 'partial'
(applies to class, struct, and record)

Cause: Source generators can only add members to types declared partial. Synthesized properties are emitted as a separate partial declaration alongside the user's source.

Fix: Add the partial modifier:

csharp
// Error
public class Account
{
    [ExpressiveProperty("Amount")]
    private decimal AmountExpression => TotalAmount - Discount;
}

// Fixed
public partial class Account
{
    [ExpressiveProperty("Amount")]
    private decimal AmountExpression => TotalAmount - Discount;
}

EXP0033 -- Requires an expression-bodied property stub

Severity: Error Category: Design

Message:

[ExpressiveProperty] must be placed on a property with an expression body '=> expr' —
accessor-list forms and method stubs are not supported

Cause: The attribute was placed on a method, an accessor-list property ({ get => expr; }), or a full { get; set; } shape.

Fix: Rewrite the stub as a top-level expression-bodied property:

csharp
// Error: method stub
[ExpressiveProperty("Amount")]
private decimal AmountExpression() => TotalAmount - Discount;

// Error: accessor-list form
[ExpressiveProperty("Amount")]
private decimal AmountExpression { get => TotalAmount - Discount; }

// Fixed: top-level expression body
[ExpressiveProperty("Amount")]
private decimal AmountExpression => TotalAmount - Discount;

EXP0034 -- Requires an instance stub

Severity: Error Category: Design

Message:

[ExpressiveProperty] is not supported on static stubs — stub '{0}' must be declared as
an instance member

Cause: The decorated stub is static. Synthesis is an instance-only feature.

Fix: Drop the static modifier. For a static computed value, use plain [Expressive] on a read-only member instead.


EXP0035 -- Target shadows inherited member

Severity: Error Category: Design

Message:

[ExpressiveProperty] target name '{0}' shadows an inherited member on '{1}' — rename
the target to avoid silent hiding, or drop [ExpressiveProperty] and use [Expressive]
on an override

Cause: The target name matches a member inherited from a base class. Synthesizing a hidden member would silently shadow the base declaration, which is surprising and error-prone.

Fix: Either pick a different target name, or drop [ExpressiveProperty] and make the computed value an override decorated with plain [Expressive].


EXP0036 -- IExpressiveQueryable<T> chain dropped to plain IQueryable<T>

Severity: Info Category: Usage

Message:

'{0}' returns IQueryable<T> from an IExpressiveQueryable<T> receiver, dropping the expressive
chain. Downstream LINQ skips ExpressiveSharp rewriting and [Expressive] members may evaluate
on the client. Add an IExpressiveQueryable<T>-typed overload of '{0}', wrap the result with
.AsExpressive(), or mark the method [NotExpressive] if the dropout is intentional.

Cause: A method invocation is being made on a receiver that implements IExpressiveQueryable<T>, but the method's return type is plain IQueryable<T> (or some derivative that is not also expressive). The chain loses its expressive type at this call site, and every downstream LINQ operation on the result skips ExpressiveSharp's rewrite step — [Expressive] members in subsequent Where / Select / Include / etc. fall back to runtime delegate invocation, which most providers cannot translate and will evaluate on the client.

The diagnostic fires once, at the dropout point itself, regardless of how many further calls follow.

Common dropout shapes:

csharp
// User-defined helper typed on plain IQueryable<T> — drops the chain.
public static IQueryable<T> Filter<T>(this IQueryable<T> source) => source.Where(...);

db.Orders.AsExpressiveDbSet().Filter()  // ⚠ EXP0036 fires on .Filter()
    .Include(o => o.Customer)           // chain is plain from here on
    .ToList();

Fix (preferred): Add a sibling overload typed on IExpressiveQueryable<T> that returns IExpressiveQueryable<T>:

csharp
public static IExpressiveQueryable<T> Filter<T>(this IExpressiveQueryable<T> source)
    => source.Where(...);

Fix (when the helper can't be modified): wrap the result with .AsExpressive() to restore the chain:

csharp
db.Orders.AsExpressiveDbSet()
    .ThirdPartyHelper()      // returns plain IQueryable<Order>
    .AsExpressive()          // re-wrap
    .Include(o => o.Customer);

Exemptions:

  • .AsQueryable() — the standard explicit downcast — is sanctioned and never reported.
  • Marking the offending method with [NotExpressive] suppresses the diagnostic at every call site, for cases where the dropout is intentional (the helper performs work that genuinely needs to run on the client).

Migration Diagnostics (EXP1001--EXP1003)

These diagnostics are emitted by the MigrationAnalyzer in the ExpressiveSharp.EntityFrameworkCore.CodeFixers package. They detect usage of the legacy EntityFrameworkCore.Projectables library and offer automated code fixes to migrate to ExpressiveSharp.

EXP1001 -- Replace [Projectable] with [Expressive]

Severity: Warning Category: Migration

Message:

[Projectable] should be replaced with [Expressive] from ExpressiveSharp

Cause: The code uses [Projectable] from EntityFrameworkCore.Projectables, which has been superseded by [Expressive].

Fix:

The IDE code fix replaces the attribute automatically. Only the Transformers property is preserved (all other properties like NullConditionalRewriteSupport, ExpandEnumMethods, and UseMemberBody have no equivalent and are removed):

csharp
// Before
[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
public string? FullAddress => Location?.AddressLine1;

// After (code fix applied)
[Expressive]
public string? FullAddress => Location?.AddressLine1;

EXP1002 -- Replace UseProjectables() with UseExpressives()

Severity: Warning Category: Migration

Message:

UseProjectables() should be replaced with UseExpressives() from ExpressiveSharp

Cause: The code calls UseProjectables() to configure EF Core, which has been superseded by UseExpressives().

Fix:

The IDE code fix replaces the method call and removes any configuration callback argument:

csharp
// Before
options.UseProjectables(p => p.CompatibilityMode(CompatibilityMode.Limited));

// After (code fix applied)
options.UseExpressives();

EXP1003 -- Replace Projectables namespace

Severity: Warning Category: Migration

Message:

Namespace '{0}' should be replaced with the ExpressiveSharp equivalent

Cause: The code has a using directive for an EntityFrameworkCore.Projectables namespace.

Fix:

The IDE code fix replaces the namespace:

Old NamespaceNew Namespace
EntityFrameworkCore.ProjectablesExpressiveSharp
EntityFrameworkCore.Projectables.ExtensionsExpressiveSharp
EntityFrameworkCore.Projectables.Infrastructure(removed -- no equivalent)

Suppressing Diagnostics

Individual warnings can be suppressed with standard C# pragma directives:

csharp
#pragma warning disable EXP0008
[Expressive]
public int Value => UnsupportedOperation();
#pragma warning restore EXP0008

Or via .editorconfig / Directory.Build.props:

xml
<PropertyGroup>
    <NoWarn>$(NoWarn);EXP0008</NoWarn>
</PropertyGroup>

Released under the MIT License.