| Severity | Warning |
| Reported by | Metalama.Extensions.Architecture |
| Message | The type assigned to the ExclusionPredicateType property must be derived from ReferencePredicate. |
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.
To be instantiated, the type must be derived from ReferencePredicate. Metalama reports LAMA0907 on the target of the attribute when this isn't the case. The attribute is then skipped: although LAMA0907 is only a warning, the whole architecture rule isn't enforced.
If the type is derived from ReferencePredicate but doesn't have the required constructor, Metalama reports LAMA0908 instead.
In the following example, the ExcludeTestTypesPredicate class doesn't derive from ReferencePredicate.
1using Metalama.Extensions.Validation;
2using Metalama.Framework.Aspects;
3
4namespace Doc.LAMA0907.Error;
5
6// This class does not derive from ReferencePredicate.
7[CompileTime]
8internal class ExcludeTestTypesPredicate
9{
10 public bool IsMatch( ReferenceValidationContext context ) => context.Origin.Type.Name.EndsWith( "Tests" );
11}
12
1using Metalama.Extensions.Architecture.Aspects;
2
3namespace Doc.LAMA0907.Error;
4
5[CanOnlyBeUsedFrom( Types = [typeof( OrderFactory )], ExclusionPredicateType = typeof( ExcludeTestTypesPredicate ) )]
Warning LAMA0907: The type assigned to the ExclusionPredicateType property must be derived from ReferencePredicate.
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
Make the exclusion predicate a valid reference predicate:
- Derive the class from ReferencePredicate and mark it with [CompileTime].
- Add a public constructor that accepts a ReferencePredicateBuilder and passes it to the base constructor.
- Implement the IsMatchCore and GetGranularity methods.
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.LAMA0907.Fixed;
6
7// This class derives from ReferencePredicate and has a constructor accepting 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.LAMA0907.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