Getting started
editInstall the package
Add the Meziantou.Framework.TaggedValues package to your project:
dotnet package add Meziantou.Framework.TaggedValuesThe package contains the [ValueTag] attribute, and a Roslyn analyzer with its code fixes for C#. The analyzer runs during the build and in the IDE: there is nothing to register.
The examples of this documentation omit the using directives: they need using Meziantou.Framework.TaggedValues;.
Tag your ids
Add [ValueTag] to the properties, fields and parameters that hold ids. The analyzer then reports the places where a value with one tag meets a value with another tag:
public sealed class Order
{
[ValueTag("OrderId")] public Guid Id { get; set; }
[ValueTag("ProjectId")] public Guid ProjectId { get; set; }
}
public sealed class OrderService
{
public Order Load([ValueTag("OrderId")] Guid orderId) => new() { Id = orderId };
public void M(Order order)
{
_ = order.Id == order.ProjectId; // MFTV0001: values with different tags are compared
Load(order.ProjectId); // MFTV0002: a ProjectId flows to a parameter that expects an OrderId
}
}Idis tagged"OrderId", andProjectIdis tagged"ProjectId". Both stayGuidproperties.- Comparing an order id with a project id is reported by MFTV0001.
- Passing a project id to a parameter that expects an order id is reported by MFTV0002.
Values that are not tagged are never reported, so tagging one property or one parameter is enough to start.
Fix the warnings
The fix is usually to use the right value:
_ = order.ProjectId == other.ProjectId; // ok, both are ProjectIds
Load(order.Id); // ok, an OrderId flows to an OrderIdSometimes the value is right and a declaration is tagged incorrectly. The code fix of the warning then changes the tag of the declaration the value flows to. Every message names both declarations and their tags, for instance:
property 'Order.ProjectId' is [ValueTag("ProjectId")] and flows to parameter 'orderId' of 'OrderService.Load' (line 12), which is [ValueTag("OrderId")]Let the tags flow
You do not need to tag every declaration. A local variable takes the tag of the value it is initialized with, and the tag follows the value through collections, LINQ, await and arithmetic:
var id = order.Id; // id is an OrderId
Load(id); // ok
id = order.ProjectId; // MFTV0002
var pair = (order.Id, order.ProjectId);
Load(pair.Item1); // ok
Load(pair.Item2); // MFTV0002When a method returns tagged values but its return value is not tagged, the callers lose the tag. The analyzer suggests the missing attribute (MFTV0008).
Next steps
- Tagging values: every place a tag can be declared.
- Naming conventions: let the analyzer infer the tags of
Id,OrderIdandprojectIdfrom their names. - Strict mode: also report tagged values mixed with untagged values.
- Analyzer rules: what each rule reports and how to configure it.