The purpose of the post is not to show how to create FxCop rules, a nice explanation can be found in this useful blog entry: FxCop This (also useful links are provided).
The rule that I'm going to show is used to detect assignments from a field to itself. For example:
public class AClass {
int aValue;
public void AMethod(int aValeu){
this.aValue = aValue;
...
}
...
}
In this example, an error typing the name of the first argument of
AMethod
leads to an incorrect assignment of field aValue
to itself. The C# compiler gives you a warning on this, however the code is generated because it is a valid program.In order to create an FxCop rule that detects this, we have to identify the IL code sequence that represents the assignment. By using ILDASM we can see this:
.method public hidebysig instance void AMethod(int32 aValeu) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldfld int32 AClass::aValue
IL_0008: stfld int32 AClass::aValue
...
} // end of method AClass::AMethod
So what we need to find is the simple pattern of a LDFLD opcode followed by a STFLD to the same field. The code for this rule looks like this:
type SameFieldAssignment = class
inherit BaseIntrospectionRule
new() = {inherit BaseIntrospectionRule("SameFieldAssignment","SameFieldAssignment",Assembly.GetExecutingAssembly());}
override x.Check(m : Member) =
match m with
| (:? Method as meth) ->
let instrs = instructions_to_list meth.Instructions
in
x.find_assignment instrs
| _ -> x.Problems
member x.find_assignment (instructions : Instruction list) =
match instructions with
| (load::store::r) when
(load.OpCode = OpCode.Ldfld &&
store.OpCode = OpCode.Stfld &&
load.Value = store.Value)
-> x.Problems.Add(new Problem(x.GetNamedResolution("SameFieldAssignment",[||]),store.SourceContext))
x.find_assignment r
| (_::r) -> x.find_assignment r
| _ -> x.Problems
end
The
find_assignment
method is used to search for the desired pattern. The IL code sequence is converted to a F# list (via the instructions_to_list
utility function), then a list pattern is the used to identify the desired pattern.We can improve this code by using active patterns. For example we can define the following patterns:
let (|Ldfld|_|) (i : Instruction) =
if (i.OpCode = OpCode.Ldfld) then
Some ((i.Value :?> Field) , i.SourceContext)
else
None
let (|Stfld|_|) (i : Instruction) =
if (i.OpCode = OpCode.Stfld) then
Some((i.Value :?> Field),i.SourceContext)
else
None
let (|MethodContent|_|) (m : Member) =
match m with
| (:? Method as meth) ->
Some (meth.FullName,
instructions_to_list meth.Instructions)
| _ -> None
Now we can write:
type SameFieldAssignmentAp = class
inherit BaseIntrospectionRule
new() = {inherit BaseIntrospectionRule("SameFieldAssignmentAp","SameFieldAssignmentAp",Assembly.GetExecutingAssembly());}
override x.Check(m : Member) =
match m with
| MethodContent(_,instrs) -> x.find_assignment instrs
| _ -> x.Problems
member x.find_assignment (instructions : Instruction list) =
match instructions with
| (Ldfld(lField,_)::Stfld(sField,source)::r) when (lField = sField)
-> let p = new Problem(x.GetNamedResolution("SameFieldAssignmentAp",[|lField.FullName|]),source) in
x.Problems.Add(p)
x.find_assignment r
| (_::r) -> x.find_assignment r
| _ -> x.Problems
end
Code for this experiment can be found here.