diff --git a/src/DependencyModules.SourceGenerator.Impl/DependencyModules.SourceGenerator.Impl.csproj b/src/DependencyModules.SourceGenerator.Impl/DependencyModules.SourceGenerator.Impl.csproj
index 0880cc0..729a1ac 100644
--- a/src/DependencyModules.SourceGenerator.Impl/DependencyModules.SourceGenerator.Impl.csproj
+++ b/src/DependencyModules.SourceGenerator.Impl/DependencyModules.SourceGenerator.Impl.csproj
@@ -9,7 +9,7 @@
True
enable
DependencyModules.SourceGenerator.Impl
- The source code of the DependencyModules generator, for the source generator of a framework. Set PackageDependencyModuleIncludeSource to true to compile the source code into your generator. The source code also uses the CSharpAuthor package. Add CSharpAuthor 2.0.0 and set PackageCSharpAuthorIncludeSource to true. For usual projects, use DependencyModules.SourceGenerator.
+ The source code of the DependencyModules generator, for the source generator of a framework. Set PackageDependencyModuleIncludeSource to true to compile the source code into your generator. The package also contains the source code of CSharpAuthor, which the generator source code uses. For usual projects, use DependencyModules.SourceGenerator.
true
false
diff --git a/website/guide/aot.md b/website/guide/aot.md
index 540159e..e3525fb 100644
--- a/website/guide/aot.md
+++ b/website/guide/aot.md
@@ -56,7 +56,7 @@ The generator does not use `private` constructors. It gets each constructor para
The generator does not write factories for generic classes. It also does not write factories for the service types that a class with `[Intercept]` registers.
-If the generator writes factories, `[Decorator(Implementation = ...)]` cannot find the implementation. The generator then gives the warning DM0022. For more information, refer to [Decorate one implementation](./decorators.md#decorate-one-implementation).
+Each factory that the generator writes returns its class. Thus a decorator that sets `Implementation` finds the implementation of the registration. For more information, refer to [Decorate one implementation](./decorators.md#decorate-one-implementation).
## Test packages
diff --git a/website/guide/conventions.md b/website/guide/conventions.md
index 83dc04c..da87233 100644
--- a/website/guide/conventions.md
+++ b/website/guide/conventions.md
@@ -50,7 +50,7 @@ The generator reads the `Conventions` method when you compile. The method does n
If the generator cannot read a statement, it gives the error DM0009 and does not use the statement.
-For `WithName`, `WithoutName`, and the environment calls, the generator does not always find an argument that is not a compile-time value. If the call has a different argument that is a compile-time value, the generator ignores the argument that is not a compile-time value. It gives no diagnostic. For example, the generator reads `IfEnvironmentValue(Keys.Feature, "on")` as `IfEnvironmentValue("on")` if `Keys.Feature` is a `static readonly` field. Use string literals or `const` fields for these arguments.
+If one argument of a call is not a value that the compiler knows, the generator gives DM0009 for the statement. For example, `IfEnvironmentValue(Keys.Feature, "on")` gives DM0009 if `Keys.Feature` is a `static readonly` field. Use string literals or `const` fields for these arguments.
You can implement the method as a public method or as an explicit interface implementation: `void IConventionModule.Conventions(IConventionDefinitions conventions)`. If a type has the two methods, the generator reads the explicit interface implementation.
@@ -84,14 +84,14 @@ A convention examines only the classes of the project that contains the module.
- It is a class, a record class, or a record struct.
- It is not `static` and not `abstract`.
-- It is not `private` and not `protected`. An `internal` class is a candidate.
+- The generated code can use it. Thus it is not `private`, `protected`, or `file`, and it is not in a `private` or `protected` class. An `internal` class and a `protected internal` class are candidates.
- It does not have a service attribute or `[Decorator]`.
A class with a service attribute keeps the registration from its attribute. The convention does not register this class again.
-A nested class can be a candidate. Write an access modifier on a nested class. A nested class without an access modifier is private, but the generator examines only the modifiers that you write. Thus the convention selects this class, and the generated code does not compile.
+A nested class can be a candidate. A nested class without an access modifier is `private`. Thus the convention does not select it.
-A selected class must have a `public` constructor, or no declared constructor. The service provider uses only `public` constructors. If the class has only `private` or `protected` constructors, the generator gives the warning DM0006 and does not register the class. If the class has only `internal` constructors, the generator registers the class and gives no diagnostic. The service provider then cannot make the service, unless the module uses [generated factories](./aot.md#generated-factories).
+A selected class must have a `public` constructor, or no declared constructor. The service provider uses only `public` constructors. If the module uses [generated factories](./aot.md#generated-factories), the generated code calls the constructor. Then an `internal` or `protected internal` constructor is also correct. This is not true for a class with `[Intercept]`, because the generator does not write a factory for it. If the class has no constructor that its registration can use, the generator gives the warning DM0006 and does not register the class.
## Lifetime
@@ -120,9 +120,7 @@ For `AsSelfWithInterfaces()`, the interfaces of the class are the interfaces in
If a convention calls `AsSelfWithInterfaces()` or `AlsoAsSelf()`, the interface registrations get the instance from the registration of the class type. Thus, for the `Singleton` and `Scoped` lifetimes, all these registrations give the same instance in a scope. If `AsSelfWithInterfaces()` finds no interface, it registers only the class type.
-::: warning
-Do not use `AlsoAsSelf()` or `AsSelfWithInterfaces()` for generic classes or with `WithKey`. In these conditions, the generated code does not compile.
-:::
+The generator cannot cross-wire a generic class. If a convention with `AlsoAsSelf()` or `AsSelfWithInterfaces()` selects a generic class, the generator gives the warning DM0014 and does not register that class. Select the generic classes with a different convention that does not use these calls.
Use only one of `AsSelf()`, `AsSelfWithInterfaces()`, and `AlsoAsSelf()` in a convention. If you use more than one, the generator gives the error DM0009.
@@ -280,19 +278,15 @@ In a referenced assembly, a class is a candidate when all these conditions are t
A selected class must have a `public` constructor.
-The generator ignores the environment attributes of a class from a referenced assembly. The conditions of the convention are applicable.
+The generator reads the environment attributes of a class from a referenced assembly. The conditions of the class and the conditions of the convention are applicable. If a condition of the class has no name or no key, the generator gives the warning DM0012 at the convention statement.
A class from a referenced assembly has no location in your source code. Thus the generator shows its diagnostics, for example DM0010, at the convention statement.
## Keys and registration type
-`WithKey(key)` registers each class as a keyed service. The generator writes the key into the generated code without changes.
+`WithKey(key)` registers each class as a keyed service. The generator writes the key into the generated code without changes. With `AlsoAsSelf()` or `AsSelfWithInterfaces()`, all registrations of a class use the key.
-::: warning
-Do not use `WithKey` with `AlsoAsSelf()` or `AsSelfWithInterfaces()`. The generated code does not compile.
-:::
-
-`Using(RegistrationType.Try)` sets the registration type. For the values, refer to [Registration type](./services.md#registration-type). Write the argument as `RegistrationType.Try`. If you write the full name of the enum or use a constant, the generator uses `Add` and gives no diagnostic.
+`Using(RegistrationType.Try)` sets the registration type. For the values, refer to [Registration type](./services.md#registration-type). The generator reads the value of the argument. Thus you can also write the full name of the enum member or use a constant. If the argument is not a value that the compiler knows, the generator gives the error DM0009.
```csharp
conventions.RegisterAll().WithKey("archive").Using(RegistrationType.Try).AsScoped();
@@ -311,7 +305,7 @@ A convention can register its classes only in some environments. Use these calls
| `IfNotEnvironmentValue("FEATURE_X")` | The environment has no value for the key. |
| `IfNotEnvironmentValue("FEATURE_X", "on")` | The value for the key is not equal to the given value. |
-If a class in the project has environment attributes, for example `[IfEnvironment("Development")]`, the conditions of the class are also applicable. All conditions must be true. For more information, refer to [Environments](./environments.md).
+If a selected class has environment attributes, for example `[IfEnvironment("Development")]`, the conditions of the class are also applicable. This is also true for a class from a referenced assembly. All conditions must be true. For more information, refer to [Environments](./environments.md).
## Diagnostics
@@ -319,9 +313,11 @@ If a class in the project has environment attributes, for example `[IfEnvironmen
| --- | --- | --- |
| DM0004 | Error | Two conventions in one module register the same class as the same service type. The generator does not write these two registrations. |
| DM0005 | Warning | A convention selects no classes. |
-| DM0006 | Warning | A selected class has no constructor that the generated code can use. |
+| DM0006 | Warning | A selected class has no constructor that its registration can use. |
| DM0009 | Error | The generator cannot read a convention statement. |
| DM0010 | Info | The generator registered a class from a convention. The message shows the service type and the module. |
+| DM0012 | Warning | An environment condition of a selected class has no name or no key. |
+| DM0014 | Warning | A convention with `AlsoAsSelf()` or `AsSelfWithInterfaces()` selects a generic class. |
If two conventions select one class for two different service types, this is not an error. The class then gets two registrations. For example, a class that implements `IFirstRole` and `ISecondRole` can have a singleton registration as `IFirstRole` and a scoped registration as `ISecondRole`.
diff --git a/website/guide/decorators.md b/website/guide/decorators.md
index df96bb1..f77a09b 100644
--- a/website/guide/decorators.md
+++ b/website/guide/decorators.md
@@ -46,11 +46,11 @@ public class AuditedOrderService(IOrderService inner, IAuditLog log) : IOrderSer
When you get `IOrderService`, the service provider gives `AuditedOrderService`. `AuditedOrderService` gets `OrderService` in the `inner` parameter.
-The generator finds the service type from the constructor. The service type is the first constructor parameter with a type that the class declaration of the decorator contains. The generator does not examine the interfaces of base classes or the interfaces that an interface derives from. If the generator finds no service type, it ignores the decorator and gives no diagnostic. The `Service` property can also set the service type, for example `[Decorator(Service = typeof(IOrderService))]`.
+The generator finds the service type from the constructor. The service type is the first constructor parameter with a type that the decorator implements. The generator examines the base classes of the decorator and all its interfaces, also the interfaces that an interface derives from. If the generator finds no service type, it gives the warning DM0025 and does not apply the decorator. The `Service` property can also set the service type, for example `[Decorator(Service = typeof(IOrderService))]`.
The generator does not register the decorator class as a service. The service provider gives the other constructor parameters, for example `IAuditLog` in the example.
-Give the decorator a `public` constructor. The generated code calls this constructor.
+The generated code calls the constructor of the decorator. Thus the constructor must be `public`, `internal`, or `protected internal`. If the decorator has no such constructor, the generator gives the warning DM0025 and does not apply the decorator.
## Which registrations a decorator changes
@@ -101,7 +101,7 @@ In this example, `Describe()` gives `outer(inner(price))`.
The decorators of all modules and the interceptors are in one sequence. The default `Order` value is 0. The source code recommends values from 0 to 999 for packages and values of 1000 and more for application code. Then the decorators of the application are the outer decorators. The generator does not examine these ranges.
-Write `Order` as a number, for example `Order = 1000`. The generator reads the text of the value. If you use a constant or `1_000`, the `Order` value is 0, and the generator gives no diagnostic. `[Decorate]` also accepts a constant.
+The generator reads the value of `Order`, not its text. Thus you can also use a constant or digit separators, for example `Order = 1_000`.
If two decorators in one module have the same service type and the same `Order` value, the generator gives the error DM0007.
@@ -159,7 +159,7 @@ The generator makes a closed decorator for each closed registration of the servi
These conditions are applicable to a generic decorator:
-- The decorator must have the same type parameters as the service type, in the same sequence.
+- The decorator must have the same type parameters as the service type, in the same sequence. If it does not, the generator gives the warning DM0025.
- The generator uses only the closed service types that the same project registers.
- If a closed service type does not agree with the constraints of the decorator, the generator does not use the decorator for that type.
@@ -225,15 +225,15 @@ public class CardPaymentCheck(IPaymentMethod inner) : IPaymentMethod
`CardPaymentCheck` decorates only the registration of `CardPayment`.
-`Implementation` has an effect only on type registrations. In an instance registration or a factory registration, the decorator does not know the implementation type. Thus the decorator changes the registration.
+The decorator finds the implementation of a registration from its type, from its instance, or from the return type of its factory. If a factory returns `object` or the service type, the registration does not show its implementation. The decorator does not change that registration.
-If a module writes [generated factories](./aot.md#generated-factories), its registrations are factory registrations. The generator then gives the warning DM0022, because the decorator changes all registrations of the service type. If `[DependencyModule]` sets `GenerateFactories`, the generator uses this value for the module. It does not use the `DependencyModules_GenerateFactories` MSBuild property.
+The factories that the generator writes return their class. Thus `Implementation` also operates in a module that uses [generated factories](./aot.md#generated-factories).
## Environment conditions
A `[Decorator]` class can have environment attributes, for example `[IfEnvironment("Development")]`. The decorator then changes the registrations only when the conditions are true. If the conditions are false, the other decorators keep their positions in the sequence. For the attributes, refer to [Environments](./environments.md).
-The generator ignores the environment attributes of a decorator that `[Decorate]` adds.
+The generator also reads the environment attributes of a decorator that `[Decorate]` adds. If a condition has no name or no key, the generator gives the warning DM0012.
## Realms
diff --git a/website/guide/environments.md b/website/guide/environments.md
index 08ca0df..eac9f1b 100644
--- a/website/guide/environments.md
+++ b/website/guide/environments.md
@@ -141,9 +141,9 @@ The generator writes an `if` statement around the registrations of the class. Th
## Conditions on decorators and conventions
-You can put the same attributes on a `[Decorator]` class. The decorator then changes the registrations only when the conditions are true. The generator ignores these attributes on a decorator that `[Decorate]` adds. For more information, refer to [Decorators](./decorators.md#environment-conditions).
+You can put the same attributes on a `[Decorator]` class. The decorator then changes the registrations only when the conditions are true. The generator also reads these attributes on a decorator that `[Decorate]` adds. For more information, refer to [Decorators](./decorators.md#environment-conditions).
-A convention can also have conditions. For more information, refer to [Conventions](./conventions.md#environment-conditions).
+A convention can also have conditions. The conditions of a class that a convention selects are also applicable, also for a class from a referenced assembly. For more information, refer to [Conventions](./conventions.md#environment-conditions).
## Read the environment in a module
@@ -175,7 +175,7 @@ public partial class MailOptionsModule : IEnvironmentServiceCollectionConfigurat
| DM0011 | Info | A service has conditions. The message shows the conditions. |
| DM0012 | Warning | A condition has no environment name or no key. The generator ignores this condition. |
-The generator gives these diagnostics only for classes with a service attribute. A condition attribute can have no name or no key on a `[Decorator]` class or on a class that a convention selects. The generator then ignores the condition and gives no diagnostic. On a convention statement, a condition call without a name or a key gives the error DM0009.
+The generator gives DM0011 only for classes with a service attribute. It gives DM0012 also for a `[Decorator]` class, for a decorator that `[Decorate]` adds, and for a class that a convention selects. For a decorator that `[Decorate]` adds, DM0012 is at the module. For a class from a referenced assembly, DM0012 is at the convention statement. On a convention statement, a condition call without a name or a key gives the error DM0009.
## Environments in tests
diff --git a/website/guide/extending.md b/website/guide/extending.md
index 1ec4cba..16d0424 100644
--- a/website/guide/extending.md
+++ b/website/guide/extending.md
@@ -105,9 +105,8 @@ Do these steps:
1. Make a class library that has the target framework `netstandard2.0`.
2. Add the `DependencyModules.SourceGenerator.Impl` package.
-3. Add the `CSharpAuthor` package, version 2.0.0. The generator source code uses this package.
-4. Set `PackageDependencyModuleIncludeSource` and `PackageCSharpAuthorIncludeSource` to `true`.
-5. Write a class that derives from `BaseSourceGenerator`.
+3. Set `PackageDependencyModuleIncludeSource` to `true`.
+4. Write a class that derives from `BaseSourceGenerator`.
```xml
@@ -119,15 +118,17 @@ Do these steps:
true
true
true
- true
-
-
+
```
+The generator source code uses the `CSharpAuthor` package. The `DependencyModules.SourceGenerator.Impl` package contains the source code of `CSharpAuthor`, and the project compiles it with the generator source code. If the project sets `PackageCSharpAuthorIncludeSource` to `true`, the project compiles `CSharpAuthor` from its own `CSharpAuthor` package. The project then does not compile the copy.
+
+Version 1.5.0 and the versions before it do not contain the source code of `CSharpAuthor`. For these versions, also add the `CSharpAuthor` package, version 2.0.0, with `IncludeAssets="build"`. Then set `PackageCSharpAuthorIncludeSource` to `true`.
+
```csharp
using CSharpAuthor;
using DependencyModules.Conventions;
@@ -178,7 +179,7 @@ To write registrations for attributes that you declare, derive a part from `Base
`DependencyFileWriter` writes the registration code for a list of `ServiceModel` values. Its `Write` method has a `uniqueId` parameter. The name of the generated method is `uniqueId` and the suffix `Dependencies`. Thus each part that adds registrations to a module must use a different `uniqueId`.
-The constructor of `DependencyFileWriter` has a `coverageAttributeOnMethod` parameter. If the value is `true`, `DependencyFileWriter` puts `[ExcludeFromCodeCoverage]` on the generated method and not on the class. Only one part of a partial class can have this attribute on the class. If two parts have it, the compiler gives the error CS0579.
+`DependencyFileWriter` puts `[ExcludeFromCodeCoverage]` on the members that it writes, and not on the class. Use the constructor that takes only the logger. The constructor with the `coverageAttributeOnMethod` parameter is obsolete. It ignores the value of the parameter.
The generator source code declares the DM diagnostics. The compiler then gives the warning RS2008 for each diagnostic, because your project has no analyzer release tracking.
diff --git a/website/guide/getting-started.md b/website/guide/getting-started.md
index 020533e..7ca7790 100644
--- a/website/guide/getting-started.md
+++ b/website/guide/getting-started.md
@@ -66,7 +66,7 @@ namespace Shop;
public partial class ShopModule;
```
-The class must be `partial`. If the class is not partial, the generator gives the error DM0003. If the project also has services, the compiler gives the error CS0260.
+The class must be `partial`. If the class is not partial, the generator gives the error DM0003 and writes no code for the module.
## Load the module
diff --git a/website/guide/interception.md b/website/guide/interception.md
index 01f8a18..05b084f 100644
--- a/website/guide/interception.md
+++ b/website/guide/interception.md
@@ -114,7 +114,13 @@ The generator gives the warning DM0008 in these conditions:
- `Service` is an interface that the class does not implement.
- The service type has no members.
-The interception is applicable only to the registration of the class. The wrapper does not change a registration of a different implementation type. It changes a registration of the service type that has no known implementation type, for example an instance registration or a factory registration.
+The interception is applicable only to the registrations of the class. The wrapper changes a registration only if the implementation of the registration is the class. These registrations have the class as their implementation:
+
+- A registration with the class type
+- An instance of the class
+- A factory that has the class as its return type
+
+The wrapper does not change a factory that has `object` or the service type as its return type, also if the factory makes the class. For example, the wrapper does not change `services.AddSingleton(_ => new StockService())`.
## Intercepted members
@@ -131,7 +137,7 @@ public class MethodsOnlyStockService : IStockService
}
```
-The values of `InterceptedMembers` are `Methods`, `Properties`, `Indexers`, `Events`, and `All`. You can use more than one value with the `|` operator. The generator reads the text of the `Members` value. Write the values of `InterceptedMembers` directly. If you use a constant, the wrapper intercepts all members. The wrapper sends the calls to the other members directly to the service.
+The values of `InterceptedMembers` are `Methods`, `Properties`, `Indexers`, `Events`, and `All`. You can use more than one value with the `|` operator. The generator reads the value of `Members`, not its text. Thus you can also use a constant. The wrapper sends the calls to the other members directly to the service.
An interceptor intercepts only the members that its interfaces can intercept. For example, an interceptor that implements only `IInterceptor` does not intercept a method that has the return type `Task`. The generator then gives the warning DM0015 with the names of these members.
@@ -143,7 +149,7 @@ If the service collection has a registration for the interceptor class type, the
## Sequence with decorators
-The interception occurs at the same time as the decorators, after all modules add their services. The `Order` property of `[Intercept]` sets the position of the interception in the decorator sequence. Write `Order` as a number, for example `Order = 1000`. If you use a constant or `1_000`, the `Order` value is 0. For more information, refer to [Decorator sequence](./decorators.md#decorator-sequence).
+The interception occurs at the same time as the decorators, after all modules add their services. The `Order` property of `[Intercept]` sets the position of the interception in the decorator sequence. The value can be a number or a constant. For more information, refer to [Decorator sequence](./decorators.md#decorator-sequence).
## Realms
@@ -161,6 +167,7 @@ The generator does not intercept a service type that has one of these members:
- A static member
- A method or a property that has a `ref` return value
+- A method that has a `ref struct` return type
- A parameter with `ref`, `out`, or `in`
- A parameter or a property of a `ref struct` type
- A property with an `init` accessor
@@ -168,7 +175,7 @@ The generator does not intercept a service type that has one of these members:
In these conditions, the generator gives the warning DM0008 and writes no wrapper. The diagnostic gives the name of one member. Other members can have the same problem. If you move these members to a different interface, the generator can intercept the service type. You can also use a [decorator](./decorators.md).
-The generator does not give DM0008 for a method that has a `ref struct` return type. For such a method, the generated wrapper does not compile.
+The generator cannot find a type parameter with the `allows ref struct` constraint. If a return type or a parameter type is such a type parameter, the generator gives no DM0008. The generated wrapper then does not compile.
## Generated code
diff --git a/website/guide/modules.md b/website/guide/modules.md
index 5048491..67e8f96 100644
--- a/website/guide/modules.md
+++ b/website/guide/modules.md
@@ -11,7 +11,7 @@ namespace Catalog;
public partial class CatalogModule;
```
-A module must be `partial`. If it is not partial, the generator gives the error DM0003. A module must be at the namespace level. If you declare a module in a different class, the generator gives the error DM0017 and does not write the other part of the module.
+A module must be `partial`. If it is not partial, the generator gives the error DM0003. A module must be at the namespace level. If you declare a module in a different class, the generator gives the error DM0017. In the two conditions, the generator writes no code for the module.
The generator reads the attributes of a module only from the declaration that has `[DependencyModule]`. If the module has more than one partial declaration, put the module attributes and the `IDependencyModuleFeature` interfaces on that declaration.
@@ -210,9 +210,7 @@ public partial class MailModule : IServiceCollectionConfiguration
public partial class NotificationModule;
```
-The generator puts the `public`, `internal`, and `protected internal` properties that have a `set` accessor on the attribute. It does not put `static` properties on the attribute.
-
-The generator examines only the modifiers of the property. It does not examine the modifiers of the `set` accessor. If the `set` accessor is `private`, the generated attribute does not compile. The generator also examines the properties of nested classes in the module. If a nested class has a property that the generator puts on the attribute, the generated attribute does not compile.
+The generator puts the `public`, `internal`, and `protected internal` properties that have a `set` accessor on the attribute. The `set` accessor must also be `public`, `internal`, or `protected internal`. Thus a property with a `private set` accessor is not on the attribute. The generator does not put `static` properties or the properties of nested classes on the attribute.
When the attribute does not set a property, the result is as follows:
@@ -224,11 +222,11 @@ When the attribute does not set a property, the result is as follows:
A module class that you declare gets a generated `Equals` method and a generated `GetHashCode` method. The generated `Equals` method compares only the module type. Thus, two instances of the same module type are the same module.
-If the module declares a method with the name `Equals`, the generator does not write these methods. The generator examines only the declaration that has `[DependencyModule]`. Declare `Equals` and `GetHashCode` in that declaration. If you declare `Equals` in a different partial declaration, the compiler gives the error CS0111.
+If the module declares `Equals(object)`, the generator does not write these methods. The generator examines all partial declarations of the module. If the module declares `GetHashCode` but not `Equals(object)`, the generator writes only `Equals`.
-The load operation calls `Equals(object)`. If the module declares only a different `Equals` method, for example `IEquatable.Equals(T)`, two instances are always two modules.
+The load operation calls `Equals(object)`. If the module declares only `Equals` for its own type, for example `IEquatable.Equals(T)`, the generated `Equals(object)` calls that method. Thus your method compares the modules.
-If a module has properties that the generator puts on the attribute and no `Equals` method, the generator gives the warning DM0018. Two instances with different values are then one module. Only the first instance loads. If you declare `Equals` and `GetHashCode`, two instances with different values can load, as shown in the `MailModule` example.
+If a module has properties that the generator puts on the attribute and declares no `Equals` method, the generator gives the warning DM0018. Two instances with different values are then one module. Only the first instance loads. If you declare `Equals` and `GetHashCode`, two instances with different values can load, as shown in the `MailModule` example.
The generated `ApplicationModule` does not get these methods.
@@ -333,7 +331,7 @@ app.Run();
The generator reads assembly-level module attributes only from `Program.cs`. If you put one in a different file of an application project, the generator gives the error DM0019 and ignores the attribute. An assembly-level attribute must also have a `using` directive for its namespace. If the directive is missing, the generator gives the warning DM0016.
-These two diagnostics are only for an attribute without its namespace, for example `[assembly: CatalogModule]`. A `using` directive is not necessary for an attribute with its full name, for example `[assembly: Catalog.CatalogModule]`. In a file that is not `Program.cs`, the generator ignores this attribute and gives no diagnostic.
+A `using` directive is not necessary for an attribute with its full name, for example `[assembly: Catalog.CatalogModule]`. Thus DM0016 is only for an attribute without its namespace. DM0019 is also for an attribute with its full name.
The top-level statements of `Program.cs` can also call a static method of a module. `ApplicationModule` then also loads that module. These conditions are necessary:
@@ -343,7 +341,7 @@ The top-level statements of `Program.cs` can also call a static method of a modu
The generated `ApplicationModule` is a partial class. To add registration code to it, declare `partial class ApplicationModule` in the root namespace without `[DependencyModule]`. Then implement `IServiceCollectionConfiguration`.
-If you declare a module with the name `ApplicationModule` and `[DependencyModule]` in the root namespace, the generator uses your module. It does not write a different `ApplicationModule`. The generator then ignores the assembly-level module attributes and the static calls in `Program.cs`, and it gives no diagnostic.
+If you declare a module with the name `ApplicationModule` and `[DependencyModule]` in the root namespace, the generator uses your module. It does not write a different `ApplicationModule`. Your module also loads the modules that the assembly-level module attributes and the static calls in `Program.cs` identify.
## Records as modules
diff --git a/website/guide/services.md b/website/guide/services.md
index e221cb7..393c54c 100644
--- a/website/guide/services.md
+++ b/website/guide/services.md
@@ -47,7 +47,7 @@ The generator does not use a capability interface as the service type. These are
- `ISerializable`
- `INotifyPropertyChanged`, `INotifyPropertyChanging`, and `INotifyCollectionChanged`
-If you set `As` to a capability interface, the generator uses it. `[CrossWireService]` does not use this list. It registers all interfaces in the declaration.
+If you set `As` to a capability interface, the generator uses it. `[CrossWireService]` does not use this list. It registers all interfaces that the class declares.
Each attribute registers one service type. To register a class as two service types, put two attributes on the class:
@@ -158,7 +158,7 @@ You can set the registration type at three levels. The generator uses the first
If no level sets a value, the registration type is `Add`.
-The generator reads the text of the `Using` value. Write the value as `RegistrationType.Try`, or as `Try` with a `using static` directive. If you write the full name of the enum or use a constant, the generator uses `Add` and gives no diagnostic.
+The generator reads the value of `Using`, not its text. Thus you can also write the full name of the enum member or use a constant.
## Registration sequence
@@ -200,13 +200,13 @@ public class SaleDiscount : IDiscountRule
In this example, `GetService()` gives `SaleDiscount`.
-Write `Order` as a number, for example `Order = 1000`. The generator reads the text of the value. If you use a constant or `1_000`, the `Order` value is 0, and the generator gives no diagnostic.
+The generator reads the value of `Order`, not its text. Thus you can also use a constant or digit separators, for example `Order = 1_000`.
For the sequence of registrations from more than one module, refer to [Module load sequence](./modules.md#module-load-sequence).
## Cross-wired services
-`[CrossWireService]` registers a class as each interface in its declaration. It also registers the class as the class type. The interface registrations get the instance from the registration of the class type. Thus, for the `Singleton` and `Scoped` lifetimes, all these registrations give the same instance in a scope.
+`[CrossWireService]` registers a class as each interface that the class declares. It also registers the class as the class type. The interface registrations get the instance from the registration of the class type. Thus, for the `Singleton` and `Scoped` lifetimes, all these registrations give the same instance in a scope.
```csharp
using DependencyModules.Runtime.Attributes;
@@ -234,20 +234,18 @@ public class Inventory : IInventoryReader, IInventoryWriter
}
```
-The class declaration must contain one or more interfaces. If the class gets its interfaces only from a base class, the generator writes no registration and gives no diagnostic.
+The generator reads the interfaces from all partial declarations of the class. It does not cross-wire the interfaces of a base class. If the class declares no interface, the generator registers only the class type. If the class then gets interfaces from a base class, the generator also gives the warning DM0024. To cross-wire these interfaces, write them in the declaration of the class.
The default lifetime is `Singleton`. To set a different lifetime, set the `Lifetime` property, for example `[CrossWireService(Lifetime = ServiceLifetime.Scoped)]`.
-`[CrossWireService]` also has the `Realm`, `Using`, and `Key` properties. It has no `As` property and no `Order` property. `Realm` has the same function as on the other service attributes.
+`[CrossWireService]` also has the `Realm`, `Using`, and `Key` properties. It has no `As` property and no `Order` property. `Realm` has the same function as on the other service attributes. If you set `Key`, all registrations of the class use the key.
-If you set `Using` on the attribute, all registrations of the class use this value. If you do not set `Using`, the interface registrations use the value of the module or of the MSBuild property. The registration of the class type then uses `Add`.
-
-::: warning
-Set `Using` on `[CrossWireService]` only to `Add` or `Replace`. If `Using` is `Try` or `TryEnumerable`, the generated code does not compile. If the module or the MSBuild property sets `TryEnumerable`, `AddModule` throws an `ArgumentException`. Do not set `Key` on `[CrossWireService]`. If you set `Key`, the generated code does not compile.
-:::
+The registration of the class type and the interface registrations use the same registration type. The generator selects it in the sequence of [Registration type](#registration-type). For `TryEnumerable`, the registration of the class type uses `TryAdd`. `TryAddEnumerable` cannot add a registration that has its service type as its implementation type.
The generator cannot cross-wire a generic class. It gives the warning DM0014 and does not register the class. For a generic class, you can use one of the other service attributes.
+You can also put `[CrossWireService]` on a static factory method. The generator then registers the return type of the method, and it cross-wires each interface that the return type declares.
+
## Factory methods
You can put a service attribute on a static method. The generator calls the method to make the instance. The return type of the method is the service type.
@@ -276,7 +274,7 @@ public static class TaxFactories
The generator gets each parameter of the method from the service provider. If the method has one parameter of type `IServiceProvider`, the method gets the service provider.
-The method must be `static`, and it must be `public` or `internal`. Write the access modifier. The generator ignores a `private` method, a `protected` method, and a method that is not `static`. It gives no diagnostic for these methods.
+The method must be `static`, and the generated code must be able to call it. Thus the method must be `public` or `internal`. A method without an access modifier is `private`. The classes that contain the method must not be `private` or `protected`. If the generated code cannot call the method, the generator gives the warning DM0023 and does not register the method.
## Generic services
@@ -311,7 +309,7 @@ The generator cannot make an instance of an abstract class or a static class. If
A service can be a record class. A service can also be a nested class.
-If a service class has more than one partial declaration, the generator reads only the declaration that has the service attribute. It reads the attributes, the base types, and the constructors from this declaration. Put the interfaces and the [environment attributes](./environments.md) on that declaration.
+If a service class has more than one partial declaration, the generator reads only the declaration that has the service attribute. It reads the attributes, the base types, and the constructors from this declaration. Put the interfaces and the [environment attributes](./environments.md) on that declaration. `[CrossWireService]` is different. It reads the interfaces of all partial declarations.
## JSON serializer contexts
diff --git a/website/guide/testing-container-source.md b/website/guide/testing-container-source.md
index 7fee24d..754b209 100644
--- a/website/guide/testing-container-source.md
+++ b/website/guide/testing-container-source.md
@@ -92,9 +92,10 @@ At the first `CreateAsync()` call, the test package gets the instances of all sh
- For a singleton service, the service provider of the test and all new service providers give the same instance.
- For a transient service, all new service providers give one instance. This instance is not the instance of the test parameter. The service provider of the test continues to make new instances.
- The test package makes an instance of each shared service at the first `CreateAsync()` call, also if the test does not use the service.
-- If the service provider of the test cannot make a shared service, the test package does not share this service. It gives no message.
+- If the service provider of the test cannot make a service that an attribute shares, `CreateAsync()` throws an `InvalidOperationException`. An example is a `[TestExport]` with `Shared = true`. The message gives the service type, and the inner exception gives the cause.
+- If the service provider of the test cannot make the type of a parameter, the test package does not share this type. It gives no message.
- A parameter type without a registration is not in the new service providers. For example, a class that the test package makes with `ActivatorUtilities` is not in the new service providers.
-- If a shared type has registrations with a key and without a key, the new service providers get only the instances without a key.
+- The new service providers keep the registrations with a key of a shared type. Each new service provider makes its own instances of these registrations. Thus a `[FromKeyedServices]` parameter does not get the same instance as a new service provider.
A `[TestExport]` with `Shared = true` is shared, also for a transient lifetime. A `[TestExport]` without `Shared = true` is also shared if the test has a parameter of that type. If the export is not shared, each new service provider makes new instances of the export.
diff --git a/website/guide/testing-mocking.md b/website/guide/testing-mocking.md
index 5a19d50..3f1fbe0 100644
--- a/website/guide/testing-mocking.md
+++ b/website/guide/testing-mocking.md
@@ -115,7 +115,7 @@ The test package registers a `[Mock]` parameter after `[TestExport]`. Thus the m
For Moq, the test package registers a `Mock` parameter before `[TestExport]`. Thus a `[TestExport]` for `T` replaces the registration of `mock.Object`. The `Mock` parameter gets its mock. But the other services get the `[TestExport]` service, not `mock.Object`. If the test also has a `[Mock] T` parameter, this parameter also gets the `[TestExport]` service.
-If the test project references `DependencyModules.SourceGenerator`, the generator examines the test methods. If a test method has `[TestExport]` and a `[Mock]` parameter for the same service type, the generator gives the warning DM0021. The generator also gives DM0021 for the two exceptions in this section.
+If the test project references `DependencyModules.SourceGenerator`, the generator examines the test methods. If a `[Mock]` parameter replaces a `[TestExport]` registration on the same test method, the generator gives the warning DM0021. It gives no DM0021 for the two exceptions in this section, because the `[TestExport]` registration stays in use.
To set a default for many tests, put `[TestExport]` on the class or on the assembly. A `[Mock]` parameter can then replace the default in one test.
diff --git a/website/guide/testing-nunit.md b/website/guide/testing-nunit.md
index 734cdbf..d80c994 100644
--- a/website/guide/testing-nunit.md
+++ b/website/guide/testing-nunit.md
@@ -58,13 +58,13 @@ public class RowTests
}
```
-`TestName` sets the name of the row. Without `TestName`, the name is the method name and the values, for example `RowAndService(2, 4)`.
-
-The test package gives the names to the rows by their position in the list of all rows. If the method also has a different row source, the test package can give a name to an incorrect row. Do not use `TestName` on such a method.
+`TestName` sets the name of the row of its `[ModuleTestCase]`. Without `TestName`, the name is the method name and the values, for example `RowAndService(2, 4)`. The rows from a different row source also get this default name.
The number of values in a row can be less than the number of parameters. A row with more values than parameters does not run. NUnit then shows the row as `NotRunnable` and gives the cause.
-Do not use `[TestCase]`, `[TestCaseSource]`, `[Values]`, or `[Range]` with `[ModuleTest]`. NUnit makes more tests from these attributes. The test package does not give their values to the parameters. Thus these tests can fail.
+Do not use `[TestCase]`, `[TestCaseSource]`, `[Values]`, or `[Range]` with `[ModuleTest]`. NUnit makes more tests from these attributes, and the test package cannot give their values to the parameters. Thus the test package does not run these tests. NUnit shows each of them as `NotRunnable`, and the message gives the name of the attribute. This is also true for the other NUnit data attributes, for example `[Random]` and `[ValueSource]`.
+
+NUnit makes some of these tests `NotRunnable` before the test package can examine them. An example is a `[TestCaseSource]` row whose number of values is less than the number of parameters. For such a test, NUnit shows its own message.
To get rows from a different source, write an attribute that implements `IModuleTestDataAttribute`. Its `GetRows(MethodInfo method)` method gives the rows. Put the attribute on the test method.
diff --git a/website/guide/testing-xunit.md b/website/guide/testing-xunit.md
index d0f2ea8..d0e62e9 100644
--- a/website/guide/testing-xunit.md
+++ b/website/guide/testing-xunit.md
@@ -101,7 +101,7 @@ public class TypedRowTests
A `TheoryDataRow` can set its `Skip`, `SkipType`, `SkipUnless`, `SkipWhen`, `Timeout`, `Traits`, `TestDisplayName`, and `Label`. These values are applicable only to that row.
-The test of a data row gets only the traits of the row, for example from the `Traits` property of `[InlineData]`. It does not get the traits of the method or the class.
+The test of a data row gets the traits of the class, of the method, and of the row. The traits of the row are, for example, from the `Traits` property of `[InlineData]`. A `[Theory]` row gets its traits in the same way.
## Lifetime of the service provider
diff --git a/website/guide/troubleshooting.md b/website/guide/troubleshooting.md
index 7fd5e66..f41cef6 100644
--- a/website/guide/troubleshooting.md
+++ b/website/guide/troubleshooting.md
@@ -12,7 +12,7 @@ Set `EmitCompilerGeneratedFiles` to `true` in the project file. The compiler the
Most IDEs also show the generated files below the analyzers of the project.
-The generator writes these files for each module. The name of each file starts with the namespace of the module and the module name. The generator removes the root namespace of the project from the start of the name.
+The generator writes these files for each module. The name of each file starts with the namespace of the module and the module name. The generator removes the root namespace of the project from the start of the name. If the module is not in the root namespace, the name starts with `global-` and the full namespace, for example `global-Sub.FooModule.Module.g.cs`.
| File | Contents |
| --- | --- |
@@ -33,9 +33,9 @@ Set `DependencyModules_LogOutputDirectory` to a folder. The generator then write
```
-Three parts of the generator write log files: the service part, the interceptor part, and the convention part. The name of each file is the name of the part, the time in milliseconds, and the extension `.txt`. Each part writes two files each time that the generator runs. If the two files get the same name, the last file replaces the first file. Thus a log file can be empty.
+Each part of the generator writes a log file each time that the generator runs. The name of each file is the name of the part, the time in milliseconds, eight random characters, and the extension `.txt`. Thus no log file replaces a different log file. The service part, the interceptor part, and the convention part write two files. The name of the second file has the suffix `.Diagnostics` after the name of the part. A log file can be empty.
-The log of `ServiceSourceGenerator` shows the configuration, the modules, and the services that the generator found. The logs also show the errors. If a log is empty, build the project again with `dotnet build --no-incremental`. The usual build does not run the generator when no file changed.
+The log of `ServiceSourceGenerator` shows the configuration, the modules, and the services that the generator found. The logs also show the errors and the exceptions. If the folder has no new log files, build the project again with `dotnet build --no-incremental`. The usual build does not run the generator when no file changed.
## A service has no registration
@@ -44,13 +44,14 @@ Examine these causes:
1. The module is not partial. The generator gives the error DM0003.
2. The module is in a different class. The generator gives the error DM0017.
3. The class is abstract or static. The generator gives the warning DM0002.
-4. The attributes must be from `DependencyModules.Runtime.Attributes`.
-5. The service sets `Realm` to a different module.
-6. The module has `OnlyRealm = true`, and the service does not set `Realm` to this module.
-7. The service has environment conditions that are false. For each class with a service attribute and conditions, the generator gives DM0011, which shows the conditions. DM0011 has the severity Info. Thus the build output does not show it at the usual verbosity.
-8. The service type of the registration is not the type that you use to get the service. For more information, refer to [Service type](./services.md#service-type).
-9. The application must call `AddModule` or `AddModules`, or load a module that has the module attribute.
-10. The service is in a different project. The application must load the module of that project. For more information, refer to [Module dependencies](./modules.md#module-dependencies).
+4. The service is a static factory method that the generated code cannot call. The generator gives the warning DM0023.
+5. The attributes must be from `DependencyModules.Runtime.Attributes`.
+6. The service sets `Realm` to a different module.
+7. The module has `OnlyRealm = true`, and the service does not set `Realm` to this module.
+8. The service has environment conditions that are false. For each class with a service attribute and conditions, the generator gives DM0011, which shows the conditions. DM0011 has the severity Info. Thus the build output does not show it at the usual verbosity.
+9. The service type of the registration is not the type that you use to get the service. For more information, refer to [Service type](./services.md#service-type).
+10. The application must call `AddModule` or `AddModules`, or load a module that has the module attribute.
+11. The service is in a different project. The application must load the module of that project. For more information, refer to [Module dependencies](./modules.md#module-dependencies).
If the generator does not write the other part of a module, the module does not implement `IDependencyModule`. Then `AddModule()` gives the error CS0311.
@@ -90,11 +91,9 @@ The `DependencyModules.SourceGenerator` package declares the MSBuild properties
## The generator failed
-If an exception occurs when the service part, the interceptor part, or the convention part writes code, the generator gives the error DM0001. Some registrations of the project can then be missing.
+If an exception occurs in the generator, the generator gives the error DM0001. Some registrations of the project can then be missing. If the exception occurs for one module, the generator writes the code of the other modules.
-If an exception occurs in a different step of the generator, the compiler gives the warning CS8785. The generator then writes no code, and `AddModule()` gives the error CS0311.
-
-In the two conditions, do these steps:
+Do these steps:
1. Set `DependencyModules_LogOutputDirectory` to a folder.
2. Build the project again.
diff --git a/website/reference/api.md b/website/reference/api.md
index 041a11c..f72c6f0 100644
--- a/website/reference/api.md
+++ b/website/reference/api.md
@@ -52,7 +52,7 @@ For the attributes, refer to [Attributes](./attributes.md).
| `IModuleEnvironment` | `EnvironmentName`, `Value(string)` | The environment. |
| `IModuleEnvironmentProvider` | `ProvideEnvironment(MethodInfo testMethod)` | Gives the environment for a test. |
-`IDependencyModule` also has members with the `Internal` prefix. The generated code uses them. Do not call these members from your code.
+`IDependencyModule` also has members with the `Internal` prefix. The generated code uses them. Do not call these members from your code. They have `[EditorBrowsable(EditorBrowsableState.Never)]`. Thus IntelliSense does not show them in a project that references the package.
## `DependencyModules.Runtime.Features`
diff --git a/website/reference/attributes.md b/website/reference/attributes.md
index eca7938..37a292e 100644
--- a/website/reference/attributes.md
+++ b/website/reference/attributes.md
@@ -39,13 +39,13 @@ Guide: [Services](../guide/services.md).
### `[CrossWireService]`
-Registers a class as each interface in its declaration and as the class type. All the registrations use the instance of the class registration. If the class declaration contains no interface, the generator writes no registration. Targets: class, method. On a method, the generator writes no registration.
+Registers a class as each interface that it declares and as the class type. All the registrations use the instance of the class registration. If the class declares no interface, the generator registers only the class type. Targets: class, method. On a static method, the generator registers the return type and each interface that the return type declares.
| Property | Type | Default | Function |
| --- | --- | --- | --- |
| `Lifetime` | `ServiceLifetime` | `Singleton` | The lifetime of the registrations. |
-| `Key` | `object?` | `null` | Do not set it. If you set `Key`, the generated code does not compile. |
-| `Using` | `RegistrationType` | Not set | The registration type. Only `Add` and `Replace` are permitted. If it is not set, refer to [Cross-wired services](../guide/services.md#cross-wired-services). |
+| `Key` | `object?` | `null` | The key of all the registrations. |
+| `Using` | `RegistrationType` | Not set | The registration type of all the registrations. If it is not set, the generator uses the value of the module, then the MSBuild property, then `Add`. |
| `Realm` | `Type?` | `null` | The module that registers the service. |
Guide: [Cross-wired services](../guide/services.md#cross-wired-services).
@@ -56,7 +56,7 @@ Identifies a class as a decorator. Targets: class.
| Property | Type | Default | Function |
| --- | --- | --- | --- |
-| `Service` | `Type?` | `null` | The service type to decorate. Without `Service`, the generator uses the first constructor parameter with a type that the class declaration contains. |
+| `Service` | `Type?` | `null` | The service type to decorate. Without `Service`, the generator uses the first constructor parameter with a type that the class implements, also through a base class. |
| `Order` | `int` | `0` | The position of the decorator. If the value of decorator A is less than the value of decorator B, B is the outer decorator. |
| `Realm` | `Type?` | `null` | The module that uses the decorator. |
| `Implementation` | `Type?` | `null` | The implementation to decorate. Without `Implementation`, the decorator changes all registrations of the service type. |
@@ -92,7 +92,7 @@ Guide: [Interception](../guide/interception.md).
### Environment attributes
-Targets: class. Put them on a service class or on a `[Decorator]` class. You can also put them on a class in the project that a convention selects. The generator ignores them on a decorator that `[Decorate]` adds and on a class from a referenced assembly.
+Targets: class. Put them on a service class, on a `[Decorator]` class, or on a decorator that `[Decorate]` adds. You can also put them on a class that a convention selects, also in a referenced assembly.
| Attribute | Constructor | More than one on a class |
| --- | --- | --- |
@@ -122,7 +122,7 @@ For each module, the generator writes an attribute with the name of the module a
- Targets: class, assembly, method, parameter. You can put more than one on an item.
- The constructor has the constructor parameters of the module.
-- The properties are the `public`, `internal`, and `protected internal` properties of the module that have a `set` accessor.
+- The properties are the `public`, `internal`, and `protected internal` properties of the module that have a `set` accessor. The `set` accessor must also be `public`, `internal`, or `protected internal`. The properties of a nested class are not included.
- The attribute implements `IDependencyModuleProvider`.
Guide: [Module dependencies](../guide/modules.md#module-dependencies).
diff --git a/website/reference/diagnostics.md b/website/reference/diagnostics.md
index 2eb6d57..6838f5d 100644
--- a/website/reference/diagnostics.md
+++ b/website/reference/diagnostics.md
@@ -25,7 +25,10 @@ The generator gives these diagnostics when you compile. Their category is `Depen
| [DM0019](#dm0019) | Error | Assembly-level module attribute is not composed |
| [DM0020](#dm0020) | Warning | Interception is applied by no module |
| [DM0021](#dm0021) | Warning | [Mock] and [TestExport] name one service on the same method |
-| [DM0022](#dm0022) | Warning | Decorator names an implementation while factories are generated |
+| [DM0022](#dm0022) | Removed | Decorator names an implementation while factories are generated |
+| [DM0023](#dm0023) | Warning | Factory method cannot be called |
+| [DM0024](#dm0024) | Warning | Cross-wired class declares no interface |
+| [DM0025](#dm0025) | Warning | Decorator is not applied |
The table shows the title of each diagnostic. The compiler shows a message with more information, for example the names of the types.
@@ -41,9 +44,9 @@ Info diagnostics show in the IDE and in a SARIF log. The `dotnet build` output d
## DM0001
-An exception occurred when the service part, the interceptor part, or the convention part of the generator wrote code. Some registrations can be missing.
+An exception occurred in the generator. Some registrations can be missing. The message gives the type and the message of the exception.
-An exception in a different step of the generator gives the compiler warning CS8785 and not DM0001. The generator then writes no code, and `AddModule()` gives the error CS0311.
+If the exception occurred for one module, the generator writes the code of the other modules. The log files also record the exception.
To correct the problem:
@@ -60,7 +63,7 @@ To correct the problem, put the attribute on a class that is not abstract. You c
## DM0003
-A class with `[DependencyModule]` is not partial. The generator does not write the other part of the module. If the project has services, the generator writes the registrations as a partial class. Thus the compiler also gives the error CS0260.
+A class with `[DependencyModule]` is not partial. The generator writes no code for the module.
To correct the problem, add the `partial` modifier to the class.
@@ -86,9 +89,11 @@ To correct the problem:
## DM0006
-A convention selects a class that has no constructor that the generated code can use. The generator does not register the class.
+A convention selects a class that has no constructor that its registration can use. The generator does not register the class.
-In the project, the generator gives DM0006 if the class has only `private` or `protected` constructors. In a referenced assembly, it gives DM0006 if the class has no `public` constructor. The service provider uses only `public` constructors. Thus a class with only `internal` constructors gets no DM0006, but the service provider cannot make it, unless the module uses generated factories.
+The service provider uses only `public` constructors. Thus the generator gives DM0006 if the class has no `public` constructor. A class from a referenced assembly must also have a `public` constructor.
+
+If the module uses [generated factories](../guide/aot.md#generated-factories), the generated code calls the constructor. Then an `internal` or `protected internal` constructor is also correct. This is not true for a class with `[Intercept]`, because the generator does not write a factory for it. The message tells which constructors are correct.
To correct the problem, add a `public` constructor to the class. You can also add a filter that removes the class.
@@ -117,7 +122,7 @@ The generator cannot read a convention statement. The message gives the cause an
- The `Conventions` method has no statement body, or a statement is not a chain of calls on the parameter.
- A chain does not start with `RegisterAll`.
- A call is not a convention call.
-- An argument is not a value that the compiler knows.
+- An argument is not a value that the compiler knows. The message gives the argument.
- The convention has no lifetime or more than one lifetime.
- The convention has more than one of `AsSelf()`, `AsSelfWithInterfaces()`, and `AlsoAsSelf()`.
- `RegisterAll()` without a service type has no registration shape or no filter that selects.
@@ -143,7 +148,14 @@ This diagnostic gives information only.
An environment condition has no environment name or no key. Thus it does not examine the environment. The generator ignores the condition. The generator ignores arguments that are not string constants. Thus a condition with only such arguments, for example an array, also gives DM0012.
-The generator gives DM0012 only for classes with a service attribute. On a `[Decorator]` class or on a class that a convention selects, it ignores the condition and gives no diagnostic. On a convention statement, a condition call without a name or a key gives DM0009.
+The generator gives DM0012 for these classes:
+
+- A class with a service attribute
+- A `[Decorator]` class
+- A decorator that `[Decorate]` adds. The diagnostic is at the module.
+- A class that a convention selects. For a class from a referenced assembly, the diagnostic is at the convention statement.
+
+On a convention statement, a condition call without a name or a key gives DM0009.
To correct the problem, give a name or a key. You can also remove the attribute.
@@ -157,9 +169,14 @@ To correct the problem, register closed types of the service. For example, decla
## DM0014
-`[CrossWireService]` is on a generic class. The generator cannot cross-wire a generic class and does not register it.
+The generator cannot cross-wire a generic class. It gives DM0014 in these conditions, and it does not register the class:
+
+- `[CrossWireService]` is on a generic class.
+- A convention with `AlsoAsSelf()` or `AsSelfWithInterfaces()` selects a generic class.
+
+To correct the problem for `[CrossWireService]`, use `[SingletonService]`, `[ScopedService]`, or `[TransientService]`. To register the class as more than one interface, use one attribute for each interface.
-To correct the problem, use `[SingletonService]`, `[ScopedService]`, or `[TransientService]`. To register the class as more than one interface, use one attribute for each interface.
+To correct the problem for a convention, remove `AlsoAsSelf()` or `AsSelfWithInterfaces()`. You can also select the generic classes with a different convention.
## DM0015
@@ -183,7 +200,9 @@ To correct the problem, move the module to the namespace level.
## DM0018
-A module has properties with a `set` accessor, but it does not declare `Equals`. The generated `Equals` method compares only the module type. Thus, two instances with different values are one module. Only the first instance loads.
+A module has properties that the generator puts on the module attribute, but the module does not declare `Equals`. The generated `Equals` method compares only the module type. Thus, two instances with different values are one module. Only the first instance loads.
+
+The generator examines all partial declarations of the module. If the module declares `Equals` for its own type, for example `IEquatable.Equals(T)`, the generator gives no DM0018. The generated `Equals(object)` then calls that method.
To correct the problem, declare `Equals` and `GetHashCode` on the module. If each module of this type loads only one time, you can also suppress the warning with `NoWarn` or `.editorconfig`.
@@ -191,7 +210,7 @@ To correct the problem, declare `Equals` and `GetHashCode` on the module. If eac
An assembly-level module attribute is in a file that the generator does not use for `ApplicationModule`. The generator reads assembly-level module attributes only from `Program.cs`. Thus, it ignores this attribute. The module does not register its services.
-The generator gives DM0019 only for an attribute without its namespace. It ignores an attribute with its full name in a different file, and it gives no diagnostic. It examines the files only if the project has a module or a generated `ApplicationModule`.
+The generator gives DM0019 for an attribute without its namespace and for an attribute with its full name, for example `[assembly: Catalog.CatalogModule]`. It examines the files only if the project has a module or a generated `ApplicationModule`.
To correct the problem, move the attribute to `Program.cs`. You can also load the module with `AddModule`.
@@ -203,14 +222,46 @@ To correct the problem, set `Realm` on `[Intercept]` to the module that register
## DM0021
-A test method has `[TestExport]` for a service type and a `[Mock]` parameter of the same type. The test uses the mock and not the `[TestExport]` registration, with two exceptions. For the exceptions, refer to [Mocks and `[TestExport]`](../guide/testing-mocking.md#mocks-and-testexport). The generator gives DM0021 only if the test project references `DependencyModules.SourceGenerator`.
+A test method has `[TestExport]` for a service type and a `[Mock]` parameter of the same type. The mock replaces the `[TestExport]` registration. The generator gives DM0021 only if the test project references `DependencyModules.SourceGenerator`.
+
+The generator gives no DM0021 for the two exceptions, because the `[TestExport]` registration stays in use:
+
+- The `[Mock]` parameter has `[FromKeyedServices]`.
+- For Moq, the test also has a `Mock` parameter of the same type.
+
+For more information, refer to [Mocks and `[TestExport]`](../guide/testing-mocking.md#mocks-and-testexport).
To correct the problem, if the `[TestExport]` is a default, move it to the test class or to the assembly. You can also remove the attribute that you do not want.
## DM0022
-A decorator sets `Implementation`, and the module uses generated factories. If the generator writes factories, a registration does not show its implementation. Thus the decorator changes all registrations of the service type.
+The generator does not give DM0022 now. It gave DM0022 when a decorator set `Implementation` in a module with generated factories.
+
+Each generated factory now returns its class. Thus the decorator changes only the registration of the implementation that it names. For more information, refer to [Decorate one implementation](../guide/decorators.md#decorate-one-implementation).
+
+## DM0023
+
+A static factory method has a service attribute, but the generated code cannot call the method. The generator does not register the method. The message gives the cause:
+
+- The method is not `static`.
+- The method is `private`, `protected`, or `private protected`. A method without an access modifier is `private`.
+- A class that contains the method is `private` or `protected`.
+
+To correct the problem, make the method `static`, and `public` or `internal`. The classes that contain the method must not be `private` or `protected`.
+
+## DM0024
+
+A class has `[CrossWireService]`, but it declares no interface. It gets one or more interfaces from a base class. The generator registers only the class type. It does not cross-wire the interfaces of a base class.
+
+To correct the problem, write the interfaces in the declaration of the class. For example, write `class Store : ReaderBase, IReader`.
+
+## DM0025
+
+A class has `[Decorator]`, but the generator cannot apply the decorator. The generator does not write code for the decorator. The message gives the cause:
-The module uses generated factories if `[DependencyModule]` sets `GenerateFactories = true`. If the module does not set `GenerateFactories`, the `DependencyModules_GenerateFactories` MSBuild property sets it.
+- The class implements no type that one of its constructor parameters has. Thus the generator finds no service type.
+- The class has no constructor that the generated code can call. The constructor must be `public`, `internal`, or `protected internal`.
+- No constructor parameter has the service type. Thus the decorator cannot get the service.
+- The type parameters of a generic decorator are not the type arguments of the service type in the same sequence.
-To correct the problem, remove `Implementation` and decorate all registrations. You can also disable the generated factories for the module. Set `GenerateFactories = false` on the module, or set the MSBuild property to `false`.
+To correct the problem, correct the cause that the message gives. For example, set `Service` on the attribute, or give the class a `public` constructor.
diff --git a/website/reference/msbuild.md b/website/reference/msbuild.md
index ff7b3d7..c14cdef 100644
--- a/website/reference/msbuild.md
+++ b/website/reference/msbuild.md
@@ -16,12 +16,14 @@ Set these properties in the project file, in a `PropertyGroup`. They are applica
| `DependencyModules_AutoGenerateModule` | `true`, `false` | `true` | If the value is `false`, the generator does not write `ApplicationModule`. |
| `DependencyModules_RegisterGenerator` | `true`, `false` | `false` | If the value is `true`, all modules register the classes that have `[JsonSourceGenerationOptions]`. If a module sets `RegisterJsonSerializers`, the generator uses the module value. |
| `DependencyModules_LogOutputDirectory` | A folder | Empty | The folder for the generator log files. If the value is empty, the generator writes no log. |
-| `ExcludeGeneratedCodeFromCoverage` | `true`, `false` | `true` | If the value is `true`, the generated code puts `[ExcludeFromCodeCoverage]` on the module classes. |
+| `ExcludeGeneratedCodeFromCoverage` | `true`, `false` | `true` | If the value is `true`, the generator puts `[ExcludeFromCodeCoverage]` on the members that it writes. |
| `GeneratedCodeStyle` | `Allman`, `KandR` | `Allman` | The brace style of the generated code. The value `K&R` has the same effect as `KandR`. |
The values of the Boolean properties and of `GeneratedCodeStyle` are not case-sensitive. The values of `DependencyModules_RegistrationType` are case-sensitive. If the generator does not know the value of `DependencyModules_RegistrationType`, it uses `Add`. If the generator does not know the value of `GeneratedCodeStyle`, it uses `Allman`.
-If `ExcludeGeneratedCodeFromCoverage` is `true`, the file with the registrations puts `[ExcludeFromCodeCoverage]` on the module class. Thus the coverage tools also ignore the members that you write in the module, for example `ConfigureServices`. The generator writes this file for all modules of a project that has one or more services. The generated module attribute and the class of the `GenerateUseMethod` method do not get `[ExcludeFromCodeCoverage]`. The interceptor wrappers always have `[ExcludeFromCodeCoverage]`, also if the value is `false`.
+If `ExcludeGeneratedCodeFromCoverage` is `true`, the generator puts `[ExcludeFromCodeCoverage]` on each method, constructor, and property that it writes. It does not put the attribute on the module class. Thus the coverage tools measure the members that you write in the module, for example `ConfigureServices`. The members of the generated module attribute and of the `GenerateUseMethod` class also get the attribute.
+
+An interceptor wrapper is a generated class. Thus it gets the attribute on the class. If the value is `false`, the generated code has no `[ExcludeFromCodeCoverage]`.
`GeneratedCodeStyle` does not have the `DependencyModules_` prefix. Other source generators can read the same property.
@@ -37,6 +39,6 @@ The `DependencyModules.SourceGenerator.Impl` package reads one more property:
| Property | Values | Default | Function |
| --- | --- | --- | --- |
-| `PackageDependencyModuleIncludeSource` | `true`, `false` | Empty | If the value is `true`, the project compiles the source code of the package. |
+| `PackageDependencyModuleIncludeSource` | `true`, `false` | Empty | If the value is `true`, the project compiles the source code of the package. The package also contains the source code of `CSharpAuthor`, which the project then also compiles. |
For more information, refer to [Extending](../guide/extending.md#a-source-generator-for-different-framework-attributes).