Skip to content

[clang-tidy]bugprone-unused-return-value ignore ++ and -- operator overloading #84922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

HerrCai0907
Copy link
Contributor

@HerrCai0907 HerrCai0907 commented Mar 12, 2024

Fixes: #84705
Further fix for #84489

@llvmbot
Copy link
Member

llvmbot commented Mar 12, 2024

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

Fixes: #84705


Full diff: https://github.com/llvm/llvm-project/pull/84922.diff

2 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp (+14-14)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp (+8)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
index 243fe47c2036b6..83b332fba1e2da 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
@@ -165,20 +165,20 @@ void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
 
 void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
   auto MatchedDirectCallExpr = expr(
-      callExpr(
-          callee(functionDecl(
-              // Don't match void overloads of checked functions.
-              unless(returns(voidType())),
-              // Don't match copy or move assignment operator.
-              unless(cxxMethodDecl(isOperatorOverloading(
-                  {OO_Equal, OO_PlusEqual, OO_MinusEqual, OO_StarEqual,
-                   OO_SlashEqual, OO_PercentEqual, OO_CaretEqual, OO_AmpEqual,
-                   OO_PipeEqual, OO_LessLessEqual, OO_GreaterGreaterEqual}))),
-              anyOf(
-                  isInstantiatedFrom(
-                      matchers::matchesAnyListedName(CheckedFunctions)),
-                  returns(hasCanonicalType(hasDeclaration(namedDecl(
-                      matchers::matchesAnyListedName(CheckedReturnTypes)))))))))
+      callExpr(callee(functionDecl(
+                   // Don't match void overloads of checked functions.
+                   unless(returns(voidType())),
+                   // Don't match copy or move assignment operator.
+                   unless(cxxMethodDecl(isOperatorOverloading(
+                       {OO_Equal, OO_PlusEqual, OO_MinusEqual, OO_StarEqual,
+                        OO_SlashEqual, OO_PercentEqual, OO_CaretEqual,
+                        OO_AmpEqual, OO_PipeEqual, OO_LessLessEqual,
+                        OO_GreaterGreaterEqual, OO_PlusPlus, OO_MinusMinus}))),
+                   anyOf(isInstantiatedFrom(
+                             matchers::matchesAnyListedName(CheckedFunctions)),
+                         returns(hasCanonicalType(hasDeclaration(
+                             namedDecl(matchers::matchesAnyListedName(
+                                 CheckedReturnTypes)))))))))
           .bind("match"));
 
   auto CheckCastToVoid =
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp
index b4a41004adf894..5809da9ec27b38 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp
@@ -10,6 +10,10 @@ struct S {
   S &operator=(S const &);
   S &operator=(S &&);
   S &operator+=(S);
+  S &operator++();
+  S &operator++(int);
+  S &operator--();
+  S &operator--(int);
 };
 
 S returnValue();
@@ -27,4 +31,8 @@ void bar() {
   a.operator=(returnRef());
 
   a += returnRef();
+  a++;
+  ++a;
+  a--;
+  --a;
 }

@EugeneZelenko
Copy link
Contributor

Please mention changes in Release Notes.

@HerrCai0907
Copy link
Contributor Author

Please mention changes in Release Notes.

release notes already includes this change.

Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall change is fine.
There is only one usage of isOperatorOverloading, so you could consider changing this into:
isOverloadAssigmentOperator and put all those operator kinds in a matcher instead of messing with SmallVector.

// Don't match void overloads of checked functions.
unless(returns(voidType())),
// Don't match copy or move assignment operator.
unless(cxxMethodDecl(isOperatorOverloading(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curios, what about operators outside class, like:

struct S {
  S(){};
  S(S const &);
  S(S &&);
};

  S &operator+=(S& s, S s2);

void test()
{
    S s;
    S s2;

    s += s2;

    operator+=(s, s2);
}

in AST those are functions, not methods:

-FunctionDecl <line:7:3, col:27> col:6 used operator+= 'S &(S &, S)'
| |-ParmVarDecl <col:17, col:20> col:20 s 'S &'
| `-ParmVarDecl <col:23, col:25> col:25 s2 'S':'S'

@HerrCai0907 HerrCai0907 requested a review from PiotrZSL March 13, 2024 06:33
Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few more tests would be also nice, with free standing operators.

Node.getOperator());
}
AST_MATCHER(CXXMethodDecl, isAssignmentOverloadedOperatorMethod) {
return llvm::is_contained(AssignmentOverloadedOperatorKinds,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getOverloadedOperator belongs to FunctionDecl, change this from Method to Function, and same in calee, in such case you will also cover calls to free standing operators called directly.

Comment on lines 189 to 190
unless(cxxOperatorCallExpr(isAssignmentOverloadedOperatorCall())),
unless(callee(cxxMethodDecl(isAssignmentOverloadedOperatorMethod()))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try changing this code in a way so callee woudn't be checked if cxxOperatorCallExpr were already checked.
you could do it like this:

anyOf(cxxOperatorCallExpr(unless(isAssignmentOverloadedOperatorCall()),
           unless(callee(functionDecl(isAssignmentOverloadedOperatorFunction()))))

You may also consider moving this before all those matchers::matchesAnyListedName, so names woudn't be checked for operators at all.

Copy link
Contributor

@5chmidti 5chmidti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check really ignore postfix increment/decrement? Or maybe this check ignores it and a new check diagnoses it++ vs ++it (where possible). What are your thoughts on this?

@HerrCai0907
Copy link
Contributor Author

Should this check really ignore postfix increment/decrement? Or maybe this check ignores it and a new check diagnoses it++ vs ++it (where possible). What are your thoughts on this?

@5chmidti What does a new check diagnoses it++vs++it (where possible) mean?

@5chmidti
Copy link
Contributor

@5chmidti What does a new check diagnoses it++vs++it (where possible) mean?

I mean this pattern:

void foo(std::string str) {
  auto iter = str.begin();
  iter++; // prefer ++iter;
  ++iter;

  sink(iter++);
  sink(++iter);

  for (auto i = str.begin(); i != str.end(); ++i) {}
  for (auto i = str.begin(); i != str.end(); i++) {} // prefer ++i
}

Where people write i++ but they really mean ++i. My question is if this check should ignore both of these cases, or if another check should detect this.

@HerrCai0907
Copy link
Contributor Author

It should be another check in maybe performance, since this check wants to detect unused return instead of iterator.

Copy link
Contributor

@5chmidti 5chmidti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM from my side, please wait for @PiotrZSL to reconfirm approval/finish review

@HerrCai0907 HerrCai0907 merged commit 12b802a into llvm:main Mar 18, 2024
@HerrCai0907 HerrCai0907 deleted the 84705-bugprone-unused-return-value-reported-on-iterator-increment branch March 18, 2024 15:56
chencha3 pushed a commit to chencha3/llvm-project that referenced this pull request Mar 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bugprone-unused-return-value reported on iterator increment
5 participants