terryyin / lizard

A simple code complexity analyser without caring about the C/C++ header files or Java imports, supports most of the popular languages.
Other
1.85k stars 250 forks source link

Ignoring compile-time const expressions: C++ non-type template arguments #152

Open rakhimov opened 8 years ago

rakhimov commented 8 years ago

C++ can have expressions evaluated at compilation time. These expressions are not part of the control flow of the function at run-time. Consider the following somewhat contrived example:

void foo() {
   std::conditional<const_expression1 && const_expression2, int32_t, int64_t>::type var{};
   // do something
}

The code is evaluated at compile time, so you may have several versions of foo depending on your constant expressions, but none of them will have and expression evaluated at runtime. The CCN of all them going to be 1.

Currently, Lizard doesn't consider template arguments separately. I guess properly supporting them is going to add a lot of code. Maybe, there are some hacks to compensate or approximate for this failure case.

As being discussed in #150 , we could accept this case as a trade-off and keep the current approach, which is to count them into the function complexity. The current approach yields conservative estimate, so even if the result is not accurate, genuinely complex functions won't be omitted or ignored.

Some may argue that if you use template metaprogramming, you deserve it (analysis tools or even compilers may choke on it ;-)

Some may argue that the template code is contributing to the complexity of the function, so the increased complexity is just reflecting that. You have to provide more tests (e.g. compile time static_asserts) to make sure it works for all cases of the compilation time results.

I would argue that templates are a big part of C++ (Awkward functional language that can compute anything with 0 run time cost, they are Turing complete), so treating its complexity separately would be more appropriate approach. There are projects computing CCN of functional languages, for example, Haskell. Why not to have Cyclomatic Template Complexity metric? (Think of it, CTC, sounds awesome.)

It would be out of scope to consider implementing such metric in Lizard (extensions?). Maybe clang-based tool would be up to the task?

In conclusion, I want to note that it's about to get even messier with C++17 constexpr if or static if:

int foo() {
  constexpr if(const_expr) {
    // do something
      return 42;
    } else {
      // do something else
      return 0;
    }
}

Only one of the branches is going to be in the function definition (ala MACRO, but at compile-time). This is going to break a lot of code analysis tools, including Lizard :angry: .