FAQ: How do I get the SourceContext for a local?


I want to fire on the naming of a local, however, whenever I pass the local to the Problem constructor, the source information for the method is always used. How do I get FxCop/Code Analysis to use the source information for the local instead?


Because the declaration of a local is not associated with an executable instruction, a local does not contain any source information within the pdb. However, any usages of the local (such as an assignment) does.


The following example shows when source information is provided and not provided:


    public void Bar()
    {
        int value1;                // Does not have source information
       
value1 = 10;               // Has source information
       
int value2 = 20;           // Has source information

       
Console.WriteLine(value1); // Has source information
       
Console.WriteLine(value2); // Has source information
   
}


As most locals are assigned at their time of declaration (such as in the case of value2 in the above example), a simple trick is use the source context of the first usage of the local in place of its declaration. This works in most situations.


The following example shows this (using the code from FAQ: How do I access the locals of a method in a custom rules?):


    public override ProblemCollection Check(Member member)
    {
        Method method = member as Method
;
        if (method == null
)
            return null;


        if (method.Instructions.Length == 0)
           
return null;


        LocalList locals = method.Instructions[0].Value as LocalList;
       
if (locals == null)
           
return null;


        for (int i = 0; i < locals.Length; i++)
        {
            Local
local = locals[i];

            
if
(someCondition)
            {   // Fire on the local

                
Resolution resolution = new Resolution(“Fix local {0}”, local.Name.Name);


                SourceContext sourceContext = FindSourceContext(local, method.Instructions);


                Problem problem = new Problem(resolution, sourceContext);
                Problems.Add(problem);
             }
        }

        return Problems;
    }


    private static SourceContext FindSourceContext(Local local, InstructionList instructions)
    {
       
foreach (Instruction instruction in
instructions)
        {
            if
(instruction.Value == local)
            {   // Found its first usage
               
return
instruction.SourceContext;
            }
        }

       
return new SourceContext
();
    }

Note: If the local is only declared and is never assigned to, or its usage is only as the argument for an out parameter, then the source context of the method will be used.

Comments (5)

  1. Poushali says:

    local.Name.Name returns me local0 or local1… It is not returning me the exact name of the local variable. How do I get the exact name

  2. You need to make sure that the symbols (*.pdb) are alongside the assembly you are analyzing.

  3. Scott Wagner says:

    I have written a rule that looks for any variables of a type (or derived from that type), and it works just fine when I run it against a debug version of my assembly.  However, when I run the code analysis against the release version, it does not detect the problem at all.

    Stepping into the rule with the debugger, I can see that Instructions[0] is still a LocalList, but the collection is empty.  Does this mean this rule can only be run when compiling for debug mode?

  4. davkean says:

    Scott,

    It depends, the compiler could be removing the local if it is unused. Is this the case in your example?

    Regards

    David

  5. Scott Wagner says:

    Ah-ha!  Yes, that was it exactly.  When writing the test case for the rule, I didn’t think of the fact that it would get optimized away in release mode.

    Thanks!