This is an archive of the discontinued LLVM Phabricator instance.

Enhance clang-tidy readability-simplify-boolean-expr to handle 'if (e) return true; return false;' and improve replacement expressions.
ClosedPublic

Authored by LegalizeAdulthood on May 16 2015, 10:10 PM.

Details

Summary

This changeset extends the simplify boolean expression check in clang-tidy to simplify if (e) return true; return false; to return e; (note the lack of an else clause on the if statement.) By default, chained conditional assignment is left unchanged, unless a configuration parameter is set to non-zero to override this behavior.

It also improves the handling of replacement expressions to apply static_cast<bool>(expr) when expr is not of type bool.

Diff Detail

Event Timeline

LegalizeAdulthood retitled this revision from to Enhance clang-tidy readability-simplify-boolean-expr to handle 'if (e) return true; return false;'.
LegalizeAdulthood updated this object.
LegalizeAdulthood edited the test plan for this revision. (Show Details)
LegalizeAdulthood added a reviewer: alexfh.
LegalizeAdulthood added a subscriber: Unknown Object (MLST).

It probably makes sense for me to rebase this on top of the other enhancement to simplify boolean after that is pushed.

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
224–239

I think this would make a good matcher, but I am unsure how to best express it using the matcher infrastructure. I could find no way in the existing matcher lexicon to say "match compound statements where this statement follows that statement"

Rebase on latest changes

Testing this on LLVM/clang indicates that it turns valid code into invalid code, so I will need to address this first. The problem appears that isFollowedBy is reporting true when it should not.

Testing this on LLVM/clang indicates that it turns valid code into invalid code, so I will need to address this first. The problem appears that isFollowedBy is reporting true when it should not.

This appears to be a regression somewhere outside of my changes.

alexfh edited edge metadata.May 18 2015, 6:16 AM

Testing this on LLVM/clang indicates that it turns valid code into invalid code, so I will need to address this first. The problem appears that isFollowedBy is reporting true when it should not.

This appears to be a regression somewhere outside of my changes.

So does LLVM/Clang compile and pass all tests after applying all fixes from this check?

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
78–80

Why not make the default value "ignored" and remove the ternary below?

Testing this on LLVM/clang indicates that it turns valid code into invalid code, so I will need to address this first. The problem appears that isFollowedBy is reporting true when it should not.

This appears to be a regression somewhere outside of my changes.

So does LLVM/Clang compile and pass all tests after applying all fixes from this check?

It all compiles, but manual testing with clang-query shows the problem. See https://llvm.org/bugs/show_bug.cgi?id=23552 for details.

It all compiles, but manual testing with clang-query shows the problem. See https://llvm.org/bugs/show_bug.cgi?id=23552 for details.

There's also the possibility that it's something I messed up in my tree. I'd like to see if anyone else can reproduce the clang-query session in the attachment on that bug.

It all compiles, but manual testing with clang-query shows the problem. See https://llvm.org/bugs/show_bug.cgi?id=23552 for details.

There's also the possibility that it's something I messed up in my tree. I'd like to see if anyone else can reproduce the clang-query session in the attachment on that bug.

I've replied on the bug.

LegalizeAdulthood edited edge metadata.

After sorting out some things with the clang tree for testing, I discovered that the code had errors. This diff corrects them.

Specifically compoundStmt(hasAnySubstatement(ifStmt())) will always match the first if statement in a compound statement. The matcher will not be invoked for each if statement in the block. So when a block contains chained if statements like:

if (i < 5) return false;
if (i > 10) return true;
return false;

The first if statement would have been matched, but then determined that it did not immediately precede the return statement and the entire block would be left alone. The consequence is that I can use a matcher that finds all compound statements containing both an if statement and a return statement, but I then have to iterate over the compound statement manually to locate the if immediately preceding the return. Test cases have been added to cover this situation. Running the check on clang/LLVM code now does the sensible thing.

This comment was removed by LegalizeAdulthood.
clang-tidy/readability/SimplifyBooleanExprCheck.cpp
78–80

Fixed.

When the replacement text is already a negated rexpression, !expr, and we're negating it again, use expr, not !!expr as the replacement.

I'd make an exception for chained conditional returns:

if (X)
  return true;
if (Y)
  return true;
if (Z)
  return true;
return false;

It's arguable whether replacing the last piece with return Z; is an improvement, so I'd better be conservative here.

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
268

Please use const auto * to make the constness explicit.

275

Instead of using the return value and additionally return a literal by reference, I'd suggest making the function return llvm::Optional<const CXXBoolLiteralExpr*>.

Same above.

281

Please use const auto * to make the constness explicit. Also two times below.

283

nit: No else after return please.

557

Please use const auto * to make the constness explicit.

test/clang-tidy/readability-simplify-bool-expr.cpp
704

How about making it a single regexp?

{{^  }$}}

Same applies for other similar lines.

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
268

Fixed.

275

Any reason to not return just the pointer and compare against nulltpr?

281

Fixed.

283

Fixed.

557

Fixed.

test/clang-tidy/readability-simplify-bool-expr.cpp
704

Fixed.

Update from review comments and handle chained condition returns of the form

if (expr)
  return true;
if (expr2)
  return true;
return false;

and variants.

LegalizeAdulthood updated this object.

Improve handling of replacement expressions to apply explicit casts to bool where necessary.

LegalizeAdulthood retitled this revision from Enhance clang-tidy readability-simplify-boolean-expr to handle 'if (e) return true; return false;' to Enhance clang-tidy readability-simplify-boolean-expr to handle 'if (e) return true; return false;' and improve replacement expressions..

Document transformations applied to replacement expression.

Replace implicit pointer to boolean conversions with explicit comparison to nullptr.

Remove unnecessary includes.

alexfh added inline comments.Jun 3 2015, 8:55 AM
clang-tidy/readability/SimplifyBooleanExprCheck.cpp
111

Can we use braced initialization instead of std::make_pair ({{OO_EqualEqual, "=="}, ....})? Same below.

141

In clang-tidy code (and in LLVM in general) it's more common to omit braces around single-line if bodies. One exception is when any branch of the if-else chain uses braces, we put them to all branches.

Please fix here and in other places in this CL.

142

This will return a StringRef pointing to a memory consumed by a temporary string. Make the return type std::string to avoid the problem.

254

No else after return, please.

275

If it's enough, sure.

556

Can the body be empty? If not, I'd document this (e.g. using an assert()).

clang-tidy/readability/SimplifyBooleanExprCheck.h
41

nit: parentheses? I didn't find what "parenthesese" is.

53

ditto

54

Did you intentionally skip a number for this paragraph? Sam below after 3.

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
556

The body can't be empty because the matcher ensures that it must contain at least two statements:

  • A return statement returning a boolean literal false or true
  • An if statement with no else clause and a then clause that consists of a single return statement returning the opposite boolean literal true or false
clang-tidy/readability/SimplifyBooleanExprCheck.cpp
141

CERT coding standards require braces around all control structures, so I've routinely adopted that habit. Considering that existing security vulnerabilities were traced to the omission of the braces, I don't simply consider it a matter of style.

However, I'm not in charge of llvm code and if it is really that important, I can change it.

Sometimes these code reviews are simply exhausting because of all this little fiddling about with formatting instead of function.

alexfh added inline comments.Jun 8 2015, 8:05 AM
clang-tidy/readability/SimplifyBooleanExprCheck.cpp
141

CERT coding standards require braces around all control structures, so
I've routinely adopted that habit. Considering that existing security
vulnerabilities were traced to the omission of the braces, I don't simply
consider it a matter of style.

This concern doesn't apply to the code that is routinely automatically formatted, as there's little to no chance of a line with incorrect indentation being considered a part of a branch when it's actually outside.

... if it is really that important ...

Consistency and readability are *really* that important. IMO, braces around single-line bodies of conditions/loops don't hurt readability as long as they are used consistently. Given that the code in this file is going to be modified mostly by you, I'm fine with putting braces around single-line if/for/while bodies as long as you commit to follow this style consistently in the files authored by you (and only there).

Sometimes these code reviews are simply exhausting because of all this
little fiddling about with formatting instead of function.

Luckily, we have clang-format that takes care of the most of formatting. And what's left is not "little fiddling about with formatting" in most cases, but something more important. For example, here we're talking about a style preference "existing security vulnerabilities were traced to" in your own words. Also, I think, most style rules should be easy to internalize, at which point they won't require any special attention.

556

Thanks for the explanation! Would you mind putting an assert then?

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
111

Good idea. Fixed.

141

Fixed.

142

Fixed.

254

FIxed.

556

Fixed.

clang-tidy/readability/SimplifyBooleanExprCheck.h
41

Fixed.

53

Fixed.

54

This should have been associated with the paragraph giving examples of implicit conversion to bool. Fixed.

Update from comments

alexfh accepted this revision.Jul 1 2015, 5:36 AM
alexfh edited edge metadata.

A few minor issues, otherwise looks good.

I'm going to fix the code and commit the patch for you.

clang-tidy/readability/SimplifyBooleanExprCheck.cpp
52

This variable and the one below are not used.

141

Thanks for fixing this, but I was talking only about *single-line* bodies. Luckily, the braces that shouldn't have been removed can easily be added back by clang-tidy itself:

clang-tidy -fix -checks=readability-braces-around-statements \
  -config="{CheckOptions: [{key: 'readability-braces-around-statements.ShortStatementLines', value: 2}]}" \
  clang-tidy/readability/SimplifyBooleanExprCheck.cpp
test/clang-tidy/readability-simplify-bool-expr.cpp
604

nit: capitalization, trailing period

This revision is now accepted and ready to land.Jul 1 2015, 5:36 AM
alexfh closed this revision.Jul 1 2015, 5:40 AM

Committed revision 241155.