PostSharp version 6.7.12 for both tooling and libraries. I have a few custom attributes defined:
[PSerializable]
public sealed class CheckInvariantAttribute : OnMethodBoundaryAspect
{
// Snip - build-time logic here...
public override void OnExit(MethodExecutionArgs args)
{
// Check stuff...
}
}
[Serializable]
public sealed class NotDefaultAttribute : LocationContractAttribute,
ILocationValidationAspect<Guid>,
ILocationValidationAspect<DateTime>,
IValidableAnnotation
{
// Snip - build-time logic here...
public Exception ValidateValue(
DateTime value,
string locationName,
LocationKind locationKind,
LocationValidationContext contect)
{
// Snip, do stuff...
}
public Exception ValidateValue(
Guid value,
string locationName,
LocationKind locationKind,
LocationValidationContext contect)
{
// Snip, do stuff...
}
}
...and I'm also using other available code contract attribute. I'm applying both of these to the same method:
[CheckInvariant]
public virtual void DoSomething(
[NotNull] SomeObjectType inst,
[NotDefault] DateTime someVal,
[StrictlyPositive] Decimal someAmt)
{
// Snip, do stuff...
}
When I do, I get a compile-time warning that states Conflicting aspects on "My.Namespace.MyClass.DoSomething( /* insert parameters here */ )": transformations "My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal' and "My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit" are not commutative, but they are not strongly ordered. Their order of execution is undeterministic.
First of all, I would expect these should be deterministic - one applies an aspect to a parameter, while the other is applied upon exit of the method. There is a natural order here. That aside, I've tried flagging them as Commutative, I've tried ordering them by role or by priority, and in all cases failed. (I may have done it incorrectly.)
How do I get rid of these warnings?
Here's the build output for this example location:
1>C:\MyProject\SomeClass.cs(159,23,159,27): warning PS0114: Conflicting aspects on "My.Namespace.MyClass.DoSomething(SometObjectType, System.DateTime, System.Decimal)": transformations "My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal'" and "My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit" are not commutative, but they are not strongly ordered. Their order of execution is undeterministic.
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Detail of dependencies for the previous warnings:
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Marker BEFORE (54):
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: (no dependency)
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: PostSharp.Patterns.Contracts.NotNullAttribute: Validates the value passed to parameter 'inst':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit:
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Action=Order, Position=After, Condition={equals "My.Namespace.CheckInvariantAttribute: Marker BEFORE (54)"}
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Action=Order, Position=Before, Condition={equals "My.Namespace.CheckInvariantAttribute: Marker AFTER (54)"}
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: PostSharp.Patterns.Contracts.StrictlyPositiveAttribute: Validates the value passed to parameter 'someAmt':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Marker AFTER (54):
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: (no dependency)
While the attribute [NotDefault]
is applied to a parameter, the actual transformation is applied to a method body. And it's basically the same OnMethodBoundary transformation as with the [CheckInvariant]
aspect. You can imagine the transformed method body to have the following structure:
NotDefaultOnEntry
try
{
CheckInvariantOnEntry
try
{
// original method body
CheckInvariantOnSuccess
}
catch
{
CheckInvariantOnException
}
finally
{
CheckInvariantOnExit
}
NotDefaultOnSuccess
}
catch
{
NotDefaultOnException
}
finally
{
NotDefaultOnExit
}
As you can see the ordering of the aspects will affect the order in which the method body wrapping will be applied. Of course in your particular case the aspects do not provide all the possible advices and the final method body structure is simpler. PostSharp still has to emit a warning in this case to avoid surprises and unexpected behavior.
You can use aspect role dependencies to order these aspects. Apply [ProvideAspectRole]
to the NotDefaultAttribute
and [AspectRoleDependency]
to the CheckInvariantAttribute
as shown below.
[Serializable]
[ProvideAspectRole(StandardRoles.Validation)]
public sealed class NotDefaultAttribute : LocationContractAttribute,
ILocationValidationAspect<Guid>,
ILocationValidationAspect<DateTime>,
IValidableAnnotation
{
// ...
}
[PSerializable]
[AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, StandardRoles.Validation)]
public sealed class CheckInvariantAttribute : OnMethodBoundaryAspect
{
// ...
}