Diagnostics¶
What each Imposter diagnostic means and how to resolve it. The help link of every diagnostic points to its section on this page.
| ID | Severity | Summary |
|---|---|---|
| IMP002 | Error | The target is not an interface or a non-sealed class |
| IMP003 | Error | The project's C# version is older than 9.0 |
| IMP004 | Error | The target class has no constructor the imposter can call |
| IMP005 | Error | The generator failed unexpectedly |
| IMP006 | Warning | A closed generic type is registered as a target |
| IMP007 | Error | Two targets would generate the same imposter type |
| IMP008 | Error | The target class has abstract members your project cannot override |
| IMP009 | Error | The target has a member whose signature uses a ref-like type |
| IMP010 | Error | The target class has required members, and SetsRequiredMembersAttribute is missing |
| IMP011 | Error | The target has a member that returns by reference |
| IMP012 | Error | The target interface has a static abstract member without an implementation |
| IMP013 | Error | The target has a member whose signature uses a pointer type |
| IMPLOG001 | Info | Generator log message |
IMP002: Invalid imposter target¶
[GenerateImposter] was applied to a type that cannot be impersonated, such as a sealed class, a struct, an enum or a delegate. No imposter is generated for it.
Register an interface or a non-sealed class instead. If you own the class, remove sealed, or extract an interface and register that.
Note
Only virtual and abstract members of a class can be impersonated.
IMP003: C# version not supported¶
Generated imposters use C# 9.0 features. When the project compiles with an older language version, the generator reports IMP003 once and generates no imposters.
Set the language version to 9.0 or later in the project file, for example <LangVersion>9.0</LangVersion>. Projects that target .NET Framework, .NET Standard or .NET Core 3.x or earlier default to C# 7.3 or 8.0, so they need this setting.
IMP004: No accessible constructor¶
The generated imposter derives from the target class, so it must call one of the class's constructors. IMP004 means none of them is accessible from your project: every constructor is private, or the class is in another assembly and its constructors are internal or private protected without InternalsVisibleTo for your project.
Add a public or protected constructor to the class, or an internal one if the class is in your project or its assembly grants your project InternalsVisibleTo.
IMP005: Generator crash¶
The generator hit an unexpected exception. The message includes the exception, and no imposter is generated for the affected target.
This is a bug in Imposter. Please open an issue with the full message and, if possible, the target type that triggers it.
IMP006: Closed generic imposter target¶
A closed generic type such as typeof(IRepository<User>) was registered. It generates the same generic imposter as the open type, IRepositoryImposter<T>, and the type arguments in the registration have no effect. With putInTheSameNamespace: false, the imposter also goes into the open type's namespace, such as Imposters.Sample.IRepository_T_. Registrations of one generic type, closed or open, share that one imposter.
Register the open generic type instead, for example typeof(IRepository<>), and choose the type arguments where you create the imposter. See Generics.
Warning
With TreatWarningsAsErrors, IMP006 fails the build. Switch to the open registration, or suppress IMP006 with #pragma warning disable IMP006 or <NoWarn>.
IMP007: Imposter type name collision¶
An imposter is named after its target, IServiceImposter for IService, and by default goes into the target's namespace. Two registered targets that share a name and namespace, such as interfaces nested in different classes, would therefore get the same imposter type. The generator reports IMP007 for both and generates neither.
Example
[assembly: GenerateImposter(typeof(Sample.A.IService))]
[assembly: GenerateImposter(typeof(Sample.B.IService), putInTheSameNamespace: false)]
Register one of them, or both, with putInTheSameNamespace: false. Its imposter then goes into a namespace of its own, such as Imposters.Sample.B.IService.
IMP008: Abstract members the project cannot override¶
The generated imposter derives from the target class, so it must override every abstract member the class leaves abstract. IMP008 means one of them is not accessible from your project: the class is in another assembly, and the member, or one of its property accessors, is internal or private protected without InternalsVisibleTo for your project. Only its own assembly, or one it grants InternalsVisibleTo, can derive from such a class, so no imposter is generated.
Register an interface the class implements instead, or ask the class's owner to grant your project InternalsVisibleTo.
IMP009: Member with a ref-like type¶
An imposter records every argument and result of the members it impersonates, and matches arguments with Arg<T>. It keeps them in fields, delegates and matchers, and none of these can hold a ref-like value: Span<T>, ReadOnlySpan<T> or another ref struct. The exception is a method's Span<T> or ReadOnlySpan<T> parameter, a span it returns by value, the span value or keys of a property or indexer, and a span an event's delegate takes (see Span properties, Span keys and values and Span parameters of events). The imposter copies the elements a span argument arrives with into an array and matches them with SpanArg<T> or ReadOnlySpanArg<T>, or OutSpanArg<T> or OutReadOnlySpanArg<T> for an out span (see Span parameters), and Returns takes the array a returned span covers (see Methods). A method's parameter or by-value result, a property's or indexer's value, an indexer's key, or an event delegate's parameter, of another ref struct type isn't kept or matched at all: the imposter only passes it between the caller and its delegates (see Ref struct parameters, Methods, Ref struct properties, and Ref struct values and keys of indexers). Nor is a value of a method's type parameter that allows ref structs (see Generics).
IMP009 means a member the imposter would impersonate uses a ref-like type anywhere else in its signature: as an indexer key of another ref struct type taken by in, a class indexer's value of one next to a key taken by in or ref readonly when its getter has a base implementation, a method's parameter or result of a ref struct type that uses the method's type parameters, a generic method's ref struct result next to a ref, in or ref readonly parameter, a span returned by reference, a property or indexer returned by reference, another ref struct as the return type of an event's delegate or as a parameter of an async one, or a span an async event's delegate takes by ref, out or ref readonly. The two generic method cases don't apply when one of the method's type parameters allows ref structs. A compiler older than Roslyn 4.14 also reports IMP009 for a method's type parameter that allows ref structs (where T : allows ref struct), since the imposter would have to repeat the anti-constraint. The diagnostic names the first such member and type, and no imposter is generated.
Change the member to take or return a type the imposter can store, such as ReadOnlyMemory<T>, Memory<T> or an array, or register an interface without the member. On a class target, only virtual and abstract members are impersonated, so a non-virtual member with a ref-like type doesn't cause IMP009.
IMP010: Required members without SetsRequiredMembersAttribute¶
The imposter creates its instance without setting the target's C# 11 required members. Its constructors allow that with System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute, which is built into .NET 7 and later. IMP010 means the target class has required members, and your project can't use that attribute: it targets an older framework, such as .NET Standard, .NET Framework or .NET 6, and declares only the attributes the compiler needs for required members. No imposter is generated.
Target .NET 7 or later, or declare the attribute in your project, as polyfill packages do:
Example
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Constructor)]
internal sealed class SetsRequiredMembersAttribute : Attribute { }
}
IMP011: Member that returns by reference¶
An imposter returns the results you set up with Returns, or default, by value. It has no storage of its own to hand out a reference to, so it can't implement or override a method, property or indexer that returns by ref or ref readonly. IMP011 names the first such member, and no imposter is generated. A ref-like type returned by reference reports IMP009 instead.
Change the member to return by value, or register an interface without the member. On a class target, only virtual and abstract members are impersonated, so a non-virtual member that returns by reference doesn't cause IMP011. On an interface target, a member that returns by reference and has a default body doesn't cause it either: the imposter leaves it out, and calls reach that body.
IMP012: Static abstract member¶
An imposter passes its target interface as a type argument, as in IHaveImposterInstance<IService>. C# doesn't allow an interface as a type argument while one of its static abstract members, declared or inherited, has no implementation in the interface (CS8920). IMP012 names the first such member, and no imposter is generated.
Register an interface without the member, such as one that declares only the instance members your tests need, or give the member a body with static virtual. Static virtual members don't cause IMP012, and neither does a class target that implements the interface.
IMP013: Member with a pointer type¶
An imposter keeps the arguments and results of the members it impersonates, and matches arguments with Arg<T>. A pointer such as int*, or a function pointer such as delegate*<int, void>, can't be a type argument, so the imposter can't impersonate a method, property, indexer or event whose signature uses one, including as an array's element type. Nor can it forward a class's constructor that takes one: it leaves that constructor out, and reports IMP013 when every accessible constructor of the class takes one. IMP013 names the first such member and type, and no imposter is generated.
Change the member to take or return a type the imposter can store, such as nint or Span<T>, or register an interface without the member. On a class target, only virtual and abstract members are impersonated, so a non-virtual member with a pointer type doesn't cause IMP013.
IMPLOG001: Generator log¶
Log messages from the generator, written only when the IMPOSTER_LOG MSBuild property is true. They show the language version, whether the static Imposter() extensions are generated, and each generated imposter with its file name.
dotnet build shows these messages only at detailed verbosity: dotnet build -v:d -p:IMPOSTER_LOG=true. See the tip on Getting Started.
Retired diagnostics¶
- IMP001 (unsupported language) was removed. The generator only runs for C# projects, so it could never be reported.
Next steps