1

I'm looking at a project in which some if and for statements do not have their associated brackets, like so:

if(condition)
  single_line_statement;
for(int i=0;i<10;i++)
  single_line_statement;

I want to find these statements!

However, this is made challenging by the existence of code with two different bracketing styles:

if(condition){
   stuff;
}

and

if(condition(a) && condition(b))
{
  stuff;
}

as well as by complex statements such as (note the nested brackets):

for (auto const &x : y)
{
    for (auto const &m : ted.bob())
    {
        if (m.n(o) != 0)
        {
            p[q] = true;
        }
        r["s"].push_back(rn.t(u));
    }
}

How can I find the if and for statements without brackets?

Richard
  • 56,349
  • 34
  • 180
  • 251

2 Answers2

3

To solve this problem, I created a handy dandy script:

#!/bin/bash
find . -name '*.h' -o -name '*.cpp' | #Find all C++ files
  xargs pcregrep -A 1 -nHM '\b(?:if|for)\s*(\(([^()]++|(?1))*\))(?!.*{|.*\n.*{)'

This finds all the C++ files and then examines the contents of each file as a single, long line using a Perl Compatible Regular Expression. This expression uses recursion (?1) to extract the (condition) following the if and for statements and the . character to match characters which are not newlines and the \n constant to match new lines. Negative-lookahead (?!...) is used to find the lines without braces.

The regular expression can be tested online here.

The output looks like this:

./dir1/file1.cpp:845:        if (!a)
./dir1/file1.cpp-846-            std::cout << "a not found!" << std::endl;
--
./dir2/file2.cpp:20:    if (b == NULL)
./dir2/file2.cpp-21-        throw std::runtime_error("b was NULL");

Since the script does not, in fact, parse C++, there are situations where it will misbehave. For instance, it will miss this:

if (condition) // An example {
  statement;

This answer leverages clang-tidy to provide a complete check, at the cost of having to incorporate clang-tidy into one's build system.

Richard
  • 56,349
  • 34
  • 180
  • 251
  • 1
    You can shorten that `find` to: `find -name '*.h' -o -name '*.cpp'` – Ted Lyngmo Aug 01 '19 at 23:48
  • Unfortunately it is quite easy to find example where your regexp would not work. Though this could be enough in simple case. – Slava Aug 01 '19 at 23:55
  • The regex yields false negatives if the brackets are part of a comment following the condition – foobar Aug 01 '19 at 23:57
  • @Richard I do not need to, unfortunately regex cannot parse C++ grammar. As I said this could be enough in simple case, but I am afraid it is not possible to make this work in 100% of cases. – Slava Aug 01 '19 at 23:58
  • I did not say anything otherwise, just do not want some people to have impression that your script will work in all cases (or it is possible to make it work fixing your regex) – Slava Aug 02 '19 at 00:03
  • @Slava: I've edited the answer to point out this important point. – Richard Aug 02 '19 at 00:12
0

I use vera++ to validate that code in my project conforms to specific style rules.

Vera: https://bitbucket.org/verateam/vera/wiki/Rules  
Rule: T019 Control structures should have complete curly-braced block of code
Martin York
  • 257,169
  • 86
  • 333
  • 562