Refactoring Support in Cevelop

In the last days, I’ve played around with Cevelop a bit, mainly interested in the refactoring capabilities it offers.

Of course, one of the main points of a modern IDE is the setup and configuration of a project (which I failed miserably at). Another is analyzing and providing quick help with our code.

Setting up the project

I used the “Commandline Videostore” project I also have used in the refactoring webinar I did together with Jetbrains/CLion. The reason is that I am familiar with the source and the necessary refactoring steps, so I could fully concentrate on the tooling.

Cevelop currently only supports makefile and SCons projects, so I had to port the CMake project to one of those. I followed the FAQ and managed to open the project and compile it once, but from there it was a little misery. From what I can tell, Cevelop is Eclipse CDT based and has, at least for my taste, an unmanageable amount of menus, settings, etc.

From the second build on, Cevelop demanded to be shown the installation directory of SCons which I don’t have. I could remedy the popups only by reinstalling Cevelop. The makefile build broke shortly after with linker errors I could not explain. I wanted to test the refactoring tooling instead of spending hours of experimenting with settings and/or searching through the Eclipse CDT documentation. So, I resorted to build and run the tests manually from command line.

Another annoyance was Symantec which always warned me about Cevelop being not known well enough.

Warnings and quick fixes

Cevelops provides a healthy amount of warnings about source code and some quick fixes to remedy them. In general, they seem to be reasonable, but some seemed rather nonsensical. For example, consider the following two lines:

double totalAmount = 0;
int frequentRenterPoints = 0;

The first line is OK by Cevelop’s standards. The second gets marked as “un- or ill-initialized variable found”.

Cevelop seems to have problems parsing complicated macro invocations, e.g. the macro in the test suite gets marked as an error that a method can not be resolved.

A missing include for vector will provide a quick fix, but only to create a “vector” class, not to include the header. The quick fix also won’t take into account qualified names, i.e. for std::vector it would still create a vector struct in the current (global) namespace instead of inside namespace std.

Having e.g. a std::vector<Movie>, where Movie is a type that does not exist, Cevelop will provide a quick fix to create it. However, that quick fix is somewhat hidden in the context menu of the error marker. Creating the new type in its own header and source will work but won’t add the necessary include to the file where it’s needed.

The refactorings

Cevelop has its own refactoring menu, which is also available as a context menu. However, some of the refactorings seem to be missing from the context menu, and they are in different order and sometimes have slightly different names. All refactorings appear to be available always and result in an error if selected in the wrong context.

For many refactorings that may modify larger quantities of code, Cevelop will show a diff view of everything it is about to change. This is a good thing, since some refactorings may change (and break) our code considerably, if we are not careful.

The basics

The following refactorings are the ones every good refactoring tool for any language should have. They are the bread and butter for any refactoring session.

Rename… works very well for classes, functions, variables etc. It even renames headers and sources if they have the same name as a contained class that gets renamed.

Extract Local Variable will introduce a new variable for the selected expression. It does, however, not use the extracted variable for all occurrences of the expression and has problems with type inference. FOr example, std::vector<std::string> v; v[1]; //extract v[1] can lead to the new variable having type __gnu_cxx:: __alloc_traits ::value_type. The counterpart, inline variable, is missing.

Extract Constant takes a literal expression and extracts it into a constant, which is very useful to get rid of magic numbers etc. The newly created constant will be a const variable in an anonymous namespace. Since only literal expressions are allowed I’d have expected to see a constexpr.

Extract function seems to work well and as expected. It takes a few lines of code and moves them into their own function with appropriate parameters. The original lines are replaced with a call to that function. The counterpart, inline function, however, is missing.
Toggle function seems to move functions from source file to the header and back but won’t analyze and add the necessary includes.
Hide method makes a method private. This functionality seems not to be available for data members, and I could not find counterparts, i.e. making a function public or protected.

Extract Interface… will add a new header with an interface class for the selected class. You can select which functions will be added as pure virtual functions to the interface class.

One additional refactoring I’ve been sorely missing is extract parameter, i.e. take an expression from inside a function and make it a parameter, moving it to the call sites.

C++ specifics

Elevate project seems to modify some variable initializations (but not all) in the project to use brace initialization. I could not find any documentation about the feature.

Extract Template… will take a class or function and make a template out of it. It will suggest a bunch of expressions whose type it will make template parameters. With larger functions or classes this can be a bit confusing, but otherwise it can be a handy feature.

Inline type alias will do just that: e.g. if you have using Foo = std::string; it would replace some occurrences of Foo with std::string. It won’t do this with e.g. a std::vector<Foo> and cannot handle alias templates.
Convert typedef to alias is obvious: it modernizes the code to use the C++11 type alias syntax instead of a typedef.

Extract (Namespace) Using Declaration… is supposed to extract a single using declaration, e.g. using std::string, and use the unqualified name after that. In my case, it did so in the middle of my larger function, leaving earlier occurrences of std::string untouched.
Extract Using Namespace Directive… should, in theory, add something like using namespace std; and use unqualified names after that. I only got an exception in Cevelop when I tried it on my code.
Inline Using… is the counterpart of the two and works for both using declarations and using directives.
Qualify Unqualified Name… will put the std:: to your map.

Extract to New Header File will take a selected function or class and move it into a new header file. The new header will automatically be included in the current file.

Conclusion

Besides the fact that I was unable to get started properly using Cevelop in over two hours, its refactoring capabilities are promising, yet incomplete. Some of the most basic refactorings like inlining variables and functions are missing, others fail, and the handling and documentation are somewhat less than perfect. On the other hand, there are some interesting choices like “Extract Template” which, albeit still lacking some usability, could come handy for developers of template-heavy libraries.

Previous Post
Next Post

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *