| Severity | Warning |
| Reported by | Metalama.Extensions.Architecture |
| Message | The type assigned to the ExclusionPredicateType property must have a public constructor accepting a single parameter of type ReferencePredicateBuilder. |
Cause
The usage validation attributes, such as CanOnlyBeUsedFromAttribute and CannotBeUsedFromAttribute, and the DerivedTypesMustRespectRegexNamingConventionAttribute attribute have an ExclusionPredicateType property. It lets you exclude some references from the rule with a custom predicate: Metalama instantiates the given type and doesn't report any violation for the references that match the predicate.
Metalama creates the predicate by calling a public constructor with a single parameter of type ReferencePredicateBuilder. A default constructor isn't enough, and neither is a constructor with additional parameters. Metalama reports LAMA0908 on the target of the attribute when the type, although derived from ReferencePredicate, has no such constructor. The attribute is then skipped: although LAMA0908 is only a warning, the whole architecture rule isn't enforced.
If the type isn't derived from ReferencePredicate, Metalama reports LAMA0907 instead.
In the following example, the constructor of ExcludeTestTypesPredicate has a second parameter.
1using Metalama.Extensions.Architecture.Predicates;
2using Metalama.Extensions.Validation;
3using Metalama.Framework.Aspects;
4
5namespace Doc.LAMA0908.Error;
6
7[CompileTime]
8internal class ExcludeTestTypesPredicate : ReferencePredicate
9{
10 private readonly string _suffix;
11
12 // The constructor has a second parameter, so Metalama cannot instantiate the predicate.
13 public ExcludeTestTypesPredicate( ReferencePredicateBuilder builder, string suffix ) : base( builder )
14 {
15 this._suffix = suffix;
16 }
17
18 protected override bool IsMatchCore( ReferenceValidationContext context ) => context.Origin.Type.Name.EndsWith( this._suffix );
19
20 protected override ReferenceGranularity GetGranularity() => ReferenceGranularity.Type;
21}
22
1using Metalama.Extensions.Architecture.Aspects;
2
3namespace Doc.LAMA0908.Error;
4
5[CanOnlyBeUsedFrom( Types = [typeof( OrderFactory )], ExclusionPredicateType = typeof( ExcludeTestTypesPredicate ) )]
Warning LAMA0908: The type assigned to the ExclusionPredicateType property must have a public constructor accepting a single parameter of type ReferencePredicateBuilder.
6public class Order
7{
8 public decimal Amount { get; set; }
9}
10
11public class OrderFactory
12{
13 public Order Create( decimal amount ) => new Order { Amount = amount };
14}
15
16public class OrderTests
17{
18 public void CanCreateOrder()
19 {
20 var order = new Order();
21 _ = order;
22 }
23}
24
Resolution
Add a public constructor whose only parameter is a ReferencePredicateBuilder and pass it to the base constructor. If the predicate needs additional values, hard-code them or read them from another source, because Metalama can't provide them.
In the following example, the predicate excludes the references from types whose name ends with Tests, so OrderTests can use the Order class.
1using Metalama.Extensions.Architecture.Predicates;
2using Metalama.Extensions.Validation;
3using Metalama.Framework.Aspects;
4
5namespace Doc.LAMA0908.Fixed;
6
7// The constructor accepts only a ReferencePredicateBuilder.
8[CompileTime]
9internal class ExcludeTestTypesPredicate : ReferencePredicate
10{
11 public ExcludeTestTypesPredicate( ReferencePredicateBuilder builder ) : base( builder ) { }
12
13 protected override bool IsMatchCore( ReferenceValidationContext context ) => context.Origin.Type.Name.EndsWith( "Tests" );
14
15 protected override ReferenceGranularity GetGranularity() => ReferenceGranularity.Type;
16}
17
1using Metalama.Extensions.Architecture.Aspects;
2
3namespace Doc.LAMA0908.Fixed;
4
5[CanOnlyBeUsedFrom( Types = [typeof( OrderFactory )], ExclusionPredicateType = typeof( ExcludeTestTypesPredicate ) )]
6public class Order
7{
8 public decimal Amount { get; set; }
9}
10
11public class OrderFactory
12{
13 public Order Create( decimal amount ) => new Order { Amount = amount };
14}
15
16public class OrderTests
17{
18 public void CanCreateOrder()
19 {
20 var order = new Order();
21 _ = order;
22 }
23}
24