In programming we have to come up with names for lots of things on a day-to-day basis. Naming things is hard. Lots of times we come up with names that we’re not particularly happy with, but it may be the best name we could think of at the time. Later we get a better idea for the name but now we have to update all the locations in the code that used the old name and change them to use the new name. Sometimes, responsibilities change and the names we originally chose no longer match the new responsibilities.
Getting help from your development environment for renaming identifiers can be a huge productivity boost. It lets you quickly and easily improve the names of things in your code. In this post, I’ll review two automatic renaming tools for C++ in Visual Studio: Visual Studio 2017 Community Edition 15.5.7 and ReSharper for C++ 2017.3.2.
Rename is an example of refactoring: changing the structure of existing code without changing it’s functionality. In this case, giving something a better name makes the code easier to understand and reason about. However, renaming things can be tricky when we rely solely on a text editor that has no knowledge of the semantics of our programming language. Consider that in a scoped language like C++, names in an inner scope can hide names in an outer scope and there are many places where identifiers are used in C++ that you might want to rename: variables, types, functions, classes, methods, template classes, template functions, template arguments, namespaces and labels are just a few of the things with names that we might want to change.
A Test Suite for C++ Refactoring Tools
While using a now discontinued refactoring add-on for Visual Studio, I found that it got many things wrong when refactoring my code. When you use a tool to automatically modify your code, it needs to make modifications that are accurate or you come to distrust the tool. What good is a tool if you can’t trust it? In order to track problems with the tool and their resolution, I recreated a refactoring test suite for C++ refactoring tools. You can find it on github. I recently extended the test cases for Rename to cover new syntax introduced by C++11 as well as covering a few holes in C++98 syntax that I had missed. The end result is over 600 test cases for renaming identifiers from a wide variety of contexts.
Visual Studio 2017
Visual Studio (VS) has been steadily adding refactoring support over the years. They first started with automated refactoring support for C# and other .NET languages. Starting with Visual Studio 2015 they began adding automated refactoring support for native C++. Refactoring in VS depends on IntelliSense, so that must be turned on for any refactoring to be possible. The full test results for VS cover all relevant refactorings, but I will focus on Rename here. VS passes 457 out of 602 test cases for a passing percentage of about 76%.
Renaming in VS is a rather heavy-weight affair. It often pops up dialog boxes to ask the programmer to select which of the discovered identifiers are relevant. One of my general expectations of a refactoring tool is that it should be just as smart as a compiler in locating relevant identifier instances. A compiler will ignore occurrences of identifiers in comments and a good refactoring tool will suggest such occurrences should be renamed. Here a dialog box is warranted because no amount of syntactical analysis of source code can tell whether or not a comment is mentioning an identifier to be renamed, or just happens to contain the same word in a comment. However, the expectations of a refactoring tool should be no lower than the expectations we have of a compiler to correctly identify how names are used in context.
For instance, it is exceedingly common to name a parameter to a template class or function
T in C++ code. Renaming a template parameter in such an instance should not scour my entire code base looking for everything named
T, even if it only suggests template parameters, and asking me to scroll through a very large list looking for the relevant identifiers. Asking me to discriminate relevant usages in a very long scrolling list is poor usability, particularly when analysis of the source code should be able to narrow down the list considerably. In the case of a template parameter named
T, the relevant occurrences are just those occurrences of
T occurring within the template definition or declaration.
Unfortunately, the refactoring experience in VS often brings up such dialogs forcing the developer to act as a manual compiler and properly select disambiguating symbols or select usages from very long lists. When executing the test suite, we simply take the defaults offered at every turn, forcing the tool to do the work. This results in a long list of test case failures in VS where the tool selected too few of the relevant usages or selected unrelated occurrences of the same name.
Renaming in VS can cause “Please wait…” dialogs to appear that warn me that collecting relevant instances could take several minutes. Even on the small code base that is the test suite, there were noticeable delays in proceeding through the dialogs. When renaming in a large code base, I would expect the experience to be even more sluggish and error-prone. Another annoyance is that every Rename operation places the cursor in the Output pane at the end of the operation. This takes the keyboard focus out of the editor window and typing
ESC is necessary to restore focus.
VS can rename labels, which sets it apart from all the other refactoring tools. However, it can’t rename the arguments to a function-like macro.
ReSharper for C++
JetBrains extended their ReSharper add-on product to support C++ after having supported C# for a number of years, resulting in a product they call ReSharper for C++, which I’ll refer to as R++. R++ can integrate into Visual Studio 2010, 2012, 2013, 2015 and 2017. ReSharper is a general programmer productivity tool and encompasses areas such as code navigation, code formatting, editor quick fixes, refactoring and more. The full test results for ReSharper for C++ cover all the applicable refactoring test cases, but I will focus on Rename here. R++ passes 591 out of 602 test cases for a passing percentage of about 98%.
Most of the time the affected identifiers are highlighted directly in the editor window and are actively modified as I type the new identifier. R++’s handling of the syntax of C++ for identifying the correct instances of identifiers is the best of all the tools I have tested, giving them a very high accuracy for the Rename test cases. This gives R++ the highest passing percentage of any tool tested and it has consistently ranked the highest on every test run when compared to other tools.
There are a few things that R++ can’t rename yet: labels and arguments to function-like macros. Fortunately both of these identifier usages appear within a limited scope so that renaming them manually in the editor is reasonable. There are a couple of bugs: rename doesn’t work in conjunction with the C++11 sizeof… template pack expansion operator. When renaming a class by the occurrence of its name in a special member function (constructor, destructor, etc.),
the cursor may be left at the declaration of the class name. This is a minor annoyance, but doesn’t result in any incorrect test results.
R++ can look for occurrences of identifiers in comments and strings, but it can’t know which of these should be renamed. In this instance, a dialog box is shown displaying the relevant instances for you to select those that should be renamed. Double-clicking on these instances in the dialog box opens the relevant source file in the text editor and positions the cursor at the occurrence. This allows you to browse for more context to decide if the occurrence should be renamed. It would be useful to be able to select strings alone, comments alone, or both to search for extra occurrences, but that is not yet an option.
Having executed the rename test cases manually for both VS and R++, I definitely prefer the user experience of R++. It is faster, smoother (particularly when live renaming takes place) and more accurate. Fewer dialog boxes means there are less distractions from editing my code and nothing visually distracting to take me “out of the flow”. However, R++ is a commercial product with yearly maintenance fees if you expect to see improvements. A license allows you perpetual use of the last version supported by your license.
Visual Studio 2017 Community Edition is free for small teams or open source work and provides the same native C++ refactoring experience as the purchased editions. While its refactoring support is weaker than R++, it comes at no additional cost to you as a user of Visual Studio.