I'm using Visual Studio 2013. I'm reworking some horrible code left by someone else, which uses almost EXCLUSIVELY global variables, and trying to clean that up so I can have each function be properly encapsulated and not cause effects outside of what is passed into (or returned from) it.
Is there any way to easily check the whole project for variables which are defined outside the scope in which they are being used?
I know I can click on a variable, and SHIFT+F12
will find me usages for that individual variable, but I'd like to find all such usages in the whole project, because the problem is REALLY bad... it's not just one or two global variables, I'm talking dozens. Trying to understand the flow of this program and the state of it is enough to make you drink, heavily!
Local variables cannot be accessed outside the function declaration. Global variable and local variable can have same name without affecting each other.
Global Variable: A variable that is declared outside the function or block is called a global variable. It is declared at the start of the program. It is available for all functions.
Nothing physical happens. A typical implementation will allocate enough space in the program stack to store all variables at the deepest level of block nesting in the current function. This space is typically allocated in the stack in one shot at the function startup and released back at the function exit.
In the C programming language, an external variable is a variable defined outside any function block. On the other hand, a local (automatic) variable is a variable defined inside a function block.
There should be a tool that already does this in a tree format, but none of the usual suspects seem to have it without the Find Usages dance.
So, here's an attempt using the Reflection APIs by finding all the member variables, then going through the methods and figuring out whether they touch them.
You're basically looking at IL at this point, so unless you want to figure the parsing rules yourself you'll want to use a disassembler like Cecil. I'm using this deprecated single file implementation cause it's easy. Something like a Roslyn analyzer or JustDecompile plugin would be another route, but I have no experience there.
There's bound to be edge cases, and if you're looking for an entire dependency graph the code will be a lot more complex - but for quick and dirty analysis, it should at least get you an overview.
So, basically you use Reflection plus an IL reader to make a map from variable -> methods (you could go the other way too, but that's probably less valuable):
var variables = typeof(SmallBallOfMud).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var methods = typeof(SmallBallOfMud).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
var d = new Dictionary<string, List<string>>();
foreach (var v in variables) d.Add(v.Name, new List<string>());
// this particular disassembler chokes on externally implemented methods
foreach (var m in methods.Where(m => (m.MethodImplementationFlags | MethodImplAttributes.IL) == 0)) {
var instructions = MethodBodyReader.GetInstructions(m);
foreach (var i in instructions) {
// we'll only check for direct field access here
var f = i.Operand as FieldInfo;
if (f == null) continue;
d[f.Name].Add(m.Name);
}
}
Our result will be like:
state1: Method1 (1), Method2 (1)
state2: Method2 (2)
state3: Method1 (1), Method2 (2)
which is read as "state3" is used by "Method1" once, and "Method2" twice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With