I wish to calculate the Access To Foreign Data metric for my methods. Furthermore, I would like to know from which objects my methods are accessing fields and properties to determine the existence of feature envy code smells.
For this example:
public class DoctorService
{
private List<Doctor> _doctors;
public Doctor FindAvailableDoctor(DateRange timeSpan)
{
foreach (Doctor d in _doctors)
{
foreach(DateRange holiday in d.HolidayDates)
{
d.Test = null;
if (!holiday.OverlapsWith(timeSpan)) return d;
}
}
return null;
}
}
and specifically the FindAvailableDoctor method, I would like to build a list of accessed fields that would contain the HolidayDates and Test property of the Doctor class. I would also like to know both these properties belong to the Doctor class (identified by the class name and its namespace).
I've made the following code to acomplish this:
var accessedFields = member.DescendantNodes().OfType<MemberAccessExpressionSyntax>();
foreach (var field in accessedFields)
{
var symbol = semanticModel.GetSymbolInfo(field.Expression).Symbol;
switch(symbol)
{
case ILocalSymbol local:
fields.Add(new CaDETMember { Name = local.Type + "|" + local.Name });
break;
case IPropertySymbol prop:
fields.Add(new CaDETMember { Name = prop.ContainingSymbol + "|" + field.ToString() });
break;
case IParameterSymbol param:
fields.Add(new CaDETMember { Name = param.Type + "|" + field.ToString() });
break;
};
}
and have started fiddling about with the fields of the Symbol API. However, this solution is both unclean and I am pretty sure will miss some edge cases for non-trivial code.
What would be a better way to extract the field and property names accessed by a method, so that I can also know which class the accessed fields and properties belong to?
Edit: Based on Jason's answer, I went with the following solution:
var accessedFields = semanticModel.GetOperation(member).Descendants().OfType<IMemberReferenceOperation>();
foreach (var field in accessedFields)
{
fields.Add(new CaDETMember {Name = field.Member.ToDisplayString()});
}
return fields;