Agent Ralph In Action
Wednesday, 5 August 2009
I’ve been yack yack yacking about clone detection and Agent Ralph. It’s time to put up or shut up. This post is some screen shots of Agent Ralph in action.
Agent Ralph‘s front end is a Resharper plug-in. Any clones detected are passed up to the plug-in which presents them to the user as highlights and quick fixes. This is how we achieve the automated repair that a modern clone tool needs. The backend scans source files handed to it by the front end, using the techniques I’ve been blogging about. Specifically, clones are identified by comparing abstract syntax trees of methods. The ASTs may be modified by the application of safe refactorings (refactorings that do not change the inputs, outputs, or side effects). If an AST can be safely coerced until it matches another then we can consider the originals functionally equivalent clones. This technique will detect clones that would otherwise be overlooked by text based clone finders.
So, here’s the basic case. Two identical methods:
Note the Resharper squigglies telling us something is up. Passing the mouse over either method name brings up a tooltip identifying the method as a clone of the other.
Placing the cursor on the method name prompts you with a quick fix…
…and invoking it…
…replaces the body of the clone with a call to the original. That’s automated clone repair! An inline method applied to Test1 will complete the removal.
The next methods are identical, but only if a rename local variable refactoring is applied. And indeed you can see that it is, indicated by the highlighting and quickfix offering.
The last example is one I am particularly proud of. Here we are detecting a clone that is a block within a larger method. Methods EmbeddedClone1 and EmbeddedClone2 both contain clones of Test2.
Thus far I’ve restricted myself to using methods as the only unit of comparison. Doing so made it easier to reason and implement as I worked through ideas. At some point I realized that I could use an extract method refactoring to create provisional methods from indiscriminate code blocks on the fly. If the provisional method is a clone then it follows that the original code block is a clone. In this way I can continue to think and code in terms of methods, yet rely on the extract method refactoring to apply my algorithms to sub-units of method (aka, arbitrary blocks and statements).