Custom Metrics
Warning
This feature is not completed and does not work as documented.
Creating a custom metric
Step 1. Create the projects in the solution
Create the scaffolding as described in Creating a Metalama SDK solution structure.
Step 2. Create the metric public API
In the public project, create a struct
that implements the IMetric<T> generic interface, where the type parameter is the type of declaration to which the metric applies (e.g. IMethod
or IField
). Your metric struct
can implement several generic instances of the IMetric<T> interface.
Your metric struct
will typically have at least one public property. Additionally, it will have internal members to update the values, which will be used by the metric implementation.
Example
The following example is a single-value metric.
public struct SyntaxNodeNumberMetric : IMetric<IMethodBase>, IMetric<INamedType>, IMetric<INamespace>, IMetric<ICompilation>
{
public int Value { get; internal set; }
internal void Add( in SyntaxNodeNumberMetric other ) => this.Value += other.Value;
}
Step 3. Create the metric implementation
A metric requires several implementation classes. All must be contained in the weaver project created in Step 1.
Create a visitor class that derives from CSharpSyntaxVisitor<TResult>, where
T
is the metric type created above. Override the relevantVisit
methods in this class. This is the actual implementation of the metric. The visitor should recursively compute the metric for each syntax node in the syntax tree. The visitor is invoked by the metric provider (described below) for each member, i.e. the visitor should not implement aggregation at type or namespace level.Create a public class that derives from SyntaxMetricProvider<T>, where
T
is again the same metric type. In the class constructor, pass an instance of the visitor created in the previous step.Implement the <xref:<xref:Metalama.Framework.Engine.Metrics.SyntaxMetricProvider`1.Aggregate*?text=SyntaxMetricProvider.Aggregate> method. This method is used to aggregate the metric from the level of members to the level of types, namespaces, or the whole project.
Annotate this class with the MetalamaPlugInAttribute custom attribute.
Example
The following example implements a metric that counts the number of nodes in a member.
[MetalamaPlugIn]
public partial class SyntaxNodeNumberMetricProvider : SyntaxMetricProvider<SyntaxNodeNumberMetric>
{
public SyntaxNodeNumberMetricProvider() : base( Visitor.Instance ) { }
protected override void Aggregate( ref SyntaxNodeNumberMetric aggregate, in SyntaxNodeNumberMetric newValue )
=> aggregate.Add( newValue );
private class Visitor : CSharpSyntaxVisitor<SyntaxNodeNumberMetric>
{
public static readonly Visitor Instance = new();
public override SyntaxNodeNumberMetric DefaultVisit( SyntaxNode node )
{
var metric = new SyntaxNodeNumberMetric { Value = 1 };
foreach ( var child in node.ChildNodes() )
{
metric.Add( this.Visit( child ) );
}
return metric;
}
}
}
Consuming a custom metric
Custom metrics can be consumed just as usual.
Testing a custom metric
TODO