This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] Misc redundant expressions checker updated for overloaded operators
ClosedPublic

Authored by barancsuk on Oct 24 2017, 7:34 AM.

Details

Summary

Redundant Expression Checker is updated to be able to match redundant expressions that contain binary overloaded operators.

Two basic cases are checked:

1.) Operators that take two instances of the same class/struct as arguments.

Example:

struct MyStruct {
  int x;  
  bool operator==(const MyStruct& rhs) const {return this->x == rhs.x; }
};

void testOperators(){
 MyStruct S;
 if(S == S) return; // Sides are equivalent.
}

2.) Overloaded operators that compare an instance of a class/struct with an integer constant expression.

Example:

struct S {
  S(){x = 1;}
  int x;
bool operator==(const int &i) const {return x == i;}
bool operator!=(int i) const {return x != i;}
};

void testOperators(){
  S s;
  if (s == 1 && s != 1) return; // Expression is always false.
}

It is checked, whether the arguments can be modified by the operator, filtering some of the dangerous side-effects.

Limitations:

  • The checker is only able to recognize comparison ( ==, !=, >, <, >=, <= ) operators, and does not check for any other operator kind. This is because these operators are considered to be implemented to behave properly and conventionally in most cases.
  • However, it is not checked whether the overloaded functions actually compare their arguments using the corresponding binary operator, or behave completely different from how one expects based on their name.

Diff Detail

Event Timeline

barancsuk created this revision.Oct 24 2017, 7:34 AM
Eugene.Zelenko added inline comments.
clang-tidy/misc/RedundantExpressionCheck.cpp
58

Unnecessary empty line

142

Unnecessary empty line

515

Unnecessary empty line

536

Unnecessary empty line

920

Unnecessary empty line

barancsuk updated this revision to Diff 120209.Oct 25 2017, 1:45 AM
barancsuk marked 5 inline comments as done.

Address review comments: remove unnecessary lines from under function headers, if statements and from above else statements.

xazax.hun added inline comments.Oct 25 2017, 6:52 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
141

Shouldn't you check in the body that you actually look at a copy constructor? Wouldn't other kinds of constructors interfere with your code? Or in case you want to skip constructors that acts as user defined conversions the function name or the documentation should reflect this.

475

Why do you need both the last two arguments?

480

You could do OperatorDecl->getType()->getAs<FunctionType>()->isConst() which is slightly shorter.

494
barancsuk updated this revision to Diff 120945.Oct 31 2017, 1:20 AM
barancsuk marked 4 inline comments as done.

Address review comments

clang-tidy/misc/RedundantExpressionCheck.cpp
141

The copy constructor is called when an instance of an object is passed by value to an operator. Since it is rather unlikely that an operator accepts values instead of references, I think there is no need to check for these cases in the current version.

475

You are right, I think ParamsToCheckCount is enough to set the count of the arguments that need checking. I also inserted an assert to ensure that non-existent parameters are not being accessed in the function.

xazax.hun added inline comments.Nov 2 2017, 8:50 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
473

If the second parameter of this function only responsible for binary information (whether the second parameters should be checked), maybe it would be better to have the type bool and rename it accordingly.

474

Use unsigned rather than uint.

520

You could use OverloadedDecl here. Also, you should use dyn_cast since you later check whether the cast succeeded.

925

Could use OverloadedDecl and use dyn_cast instead of cast here.

barancsuk updated this revision to Diff 121496.Nov 3 2017, 9:56 AM
barancsuk marked 4 inline comments as done.

Address latest review comments

xazax.hun added inline comments.Nov 6 2017, 6:04 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
311

Do you actually need this matcher? Can't you just use the same matcher as above instead of this one? They seem to do the same thing.

460

I would omit the Param from the name of this function, since it has nothing specific to the parameters.

924

The result of the dyn_casts are now not checked. In case you think the cast will never fail, use cast and do not check the results. If the cast might fail, use dyn_cast and check the result.

925

I woud move this check above the cast.

barancsuk updated this revision to Diff 121742.Nov 6 2017, 8:35 AM
barancsuk marked 4 inline comments as done.

Address latest review comments

xazax.hun added inline comments.Nov 7 2017, 5:07 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
522

You could use dyn_cast_or_null here and elide the null check above (and also even eliminate the OverloadedDecl variable).

920

Do you need al these checks both here and in retrieveRelationalIntegerConstantExpr? Or is there a way to factor this out?

927

You can use the same dyn_cast_or_null trick here as above.

barancsuk updated this revision to Diff 122096.Nov 8 2017, 8:09 AM
barancsuk marked 3 inline comments as done.

Address latest review comments

clang-tidy/misc/RedundantExpressionCheck.cpp
920

I think this can be easily eliminated, since these expressions are matched as overloaded operators with an operator kind that always accepts two arguments.

xazax.hun accepted this revision.Nov 21 2017, 3:37 AM

Other than two nits it looks good to me. Wait one more reviewer just in case.

clang-tidy/misc/RedundantExpressionCheck.cpp
919

The check is already done in retrieveRelationalIntegerConstantExpr as far as I understand. So in fact here you only check which diagnostic should be emitted based on the bindings from AST matchers.

924

Do you need this check after retrieveRelationalIntegerConstantExpr is invoked above?

This revision is now accepted and ready to land.Nov 21 2017, 3:37 AM
barancsuk marked an inline comment as done.Nov 21 2017, 9:22 AM
barancsuk added inline comments.
clang-tidy/misc/RedundantExpressionCheck.cpp
924

In fact, retrieveRelationalIntegerConstantExpr is not invoked before this check.

The part of the code that calls retrieveRelationalIntegerConstantExpr, is the method checkRelationalExpr.
checkRelationalExpr is independent from this part of the code and even uses a distinct AST matcher.
It checks for redundant logical operator expressions with both operands being relational operator expressions, for example: (s1 == 1 && s1 != 1).

Here, overloaded operator expressions with two identical sides (e.g.: S == S) are checked.

These two cases are handled separately in the code, thus the method canOverloadedOperatorArgsBeModified is called twice at two different places: here and in retrieveRelationalIntegerConstantExpr.

aaron.ballman added inline comments.Nov 21 2017, 11:58 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
475

For fun, can you add a test case where you have a malformed overloaded operator to ensure this assert does not trigger? e.g.,

struct S {
  bool operator==();
};
test/clang-tidy/misc-redundant-expression.cpp
238

Spurious line removal.

252

Spurious line removal.

barancsuk updated this revision to Diff 123950.Nov 22 2017, 8:09 AM
barancsuk marked 3 inline comments as done.

Address latest review comments

barancsuk added inline comments.Nov 22 2017, 8:09 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
475

According to the C++ standard, 'it is not possible to change the number of operands of [overloaded] operators'.
Therefore I think the best solution here is to drop the assert itself, since it is impossible to define such a malformed overloaded operator (that compiles).

aaron.ballman added inline comments.Nov 22 2017, 8:26 AM
clang-tidy/misc/RedundantExpressionCheck.cpp
475

Ah, good point about that being ill-formed. Yeah, I think the assert can go away then. Thanks!

This revision was automatically updated to reflect the committed changes.