[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-07-05 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@5chmidti @PiotrZSL Hi Julian and Piotr, I hope this message finds you well. 
I'm following up on my previous comment regarding the PR I submitted two weeks 
ago. I understand you both might be busy, but I wanted to check if there's been 
any progress or if we are expecting more inputs from other reviewers to move 
forward with the merge.
If that's not the case, could you please help merge this change?

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-07-07 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@HerrCai0907 Hello Congcong, thank you for your message. I appreciate your 
willingness to help. As I don't have write access to the repository, I would be 
grateful if you could review and merge this pull request for me. Please let me 
know if you need any additional information or if you have any questions about 
the changes.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits


@@ -175,6 +218,10 @@ bool UseAfterMoveFinder::findInternal(const CFGBlock 
*Block,
 MovingCall != nullptr &&
 Sequence->potentiallyAfter(MovingCall, Use);
 
+// We default to false here and change this to true if required in
+// find().
+TheUseAfterMove->UseHappensInLaterLoopIteration = false;
+

tchaikov wrote:

agreed. but practically, we reference `EvaluationOrderUndefined` only if 
`UseAfterMoveFinder::find()` returns `true`, which indicates a use-after-move 
is identified. see 
https://github.com/llvm/llvm-project/blob/4d95850d052336a785651030eafa0b24651801a0/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp#L498-L501,
 and the only two places where we return `true` in 
`UseAfterMoveFinder::findInternal()` are

1. 
https://github.com/llvm/llvm-project/blob/4d95850d052336a785651030eafa0b24651801a0/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp#L178
2. 
https://github.com/llvm/llvm-project/blob/4d95850d052336a785651030eafa0b24651801a0/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp#L188

the 2nd case is a recursive call to `UseAfterMoveFinder::findInternal()`. and 
the 1st case is where we set `UseHappensInLaterLoopIteration` to `false`. and 
we may set this member variable to `true` later on.

probably a more readable implementation is to return an 
`optional` from `UseAfterMoveFinder::find()`, so that it's 
obvious that `UseAfterMove` is always initialized and returned if we find a 
use-after-move. but that'd be a cleanup, and not directly related to this PR. 
if it acceptable to piggy back it into this PR, i will do it.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits


@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const CFGBlock *Succ : Current->succs()) {
+  if (Succ && !Visited.contains(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+

tchaikov wrote:

yeah, it's equivalent to `reaches()`. will use it instead in the next revision.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

tchaikov wrote:

v3:

- trade `reaches()` helper for `CFGReverseBlockReachabilityAnalysis`. less 
repeating this way.
- replace `argsContain()` helper with `llvm::is_contained()`. less repeating 
this way.
- s/call/Call/. more consistent with the naming convention in LLVM.
- initialize `EvaluationOrderUndefined` in-class

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits


@@ -175,6 +218,10 @@ bool UseAfterMoveFinder::findInternal(const CFGBlock 
*Block,
 MovingCall != nullptr &&
 Sequence->potentiallyAfter(MovingCall, Use);
 
+// We default to false here and change this to true if required in
+// find().
+TheUseAfterMove->UseHappensInLaterLoopIteration = false;
+

tchaikov wrote:

after a second thought, i am removing this initialization, and just set the 
default value in the `UseAfterMove`. and move the accompanied comment there.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From cb1dfa3c776e6c1c327acc6ec7f02c4bceb64069 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 39 ---
 .../clang-tidy/utils/ExprSequence.cpp | 68 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  5 +-
 .../checkers/bugprone/use-after-move.cpp  | 50 +-
 4 files changed, 146 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..0130f4f17f5cc 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -11,9 +11,11 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +37,11 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  //
+  // We default to false and change it to true if required in find().
+  bool UseHappensInLaterLoopIteration = false;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +55,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -89,7 +96,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -108,17 +115,33 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
 
   Sequence = std::make_unique(TheCFG.get(), CodeBlock, Context);
   BlockMap = std::make_unique(TheCFG.get(), Context);
+  auto CFA = std::make_unique(*TheCFG);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
+TheUseAfterMove);
+
+  if (Found) {
+if (const CFGBlock *UseBlock =
+BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef))

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@5chmidti Thanks for your thoughtful review and suggestions!
 I've incorporated them into the latest revision, which I'd appreciate you 
taking another look at.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-07 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From 00df70151da03f9a3d3c6ae3ee8078fd6ff654f0 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 40 ---
 .../clang-tidy/utils/ExprSequence.cpp | 68 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  5 +-
 .../checkers/bugprone/use-after-move.cpp  | 50 +-
 4 files changed, 147 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..3072c709b3120 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -11,9 +11,11 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +37,11 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  //
+  // We default to false and change it to true if required in find().
+  bool UseHappensInLaterLoopIteration = false;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +55,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -89,7 +96,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +117,32 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
+TheUseAfterMove);
+
+  if (Found) {
+if (const CFGBlock *UseBlock =
+BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef)) {
+  // Does the use happen in a later loop iteration than the move?
+  // - If they are in the same 

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits


@@ -35,6 +37,11 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;

tchaikov wrote:

sure. but for the reason explained at 
https://github.com/llvm/llvm-project/pull/93623#discussion_r1631614642. but 
please note, it's just for the sake of readability / consistency, not for the 
correctness.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits


@@ -110,15 +117,32 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
+TheUseAfterMove);
+
+  if (Found) {
+if (const CFGBlock *UseBlock =
+BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef)) {
+  // Does the use happen in a later loop iteration than the move?
+  // - If they are in the same CFG block, we know the use happened in a
+  //   later iteration if we visited that block a second time.
+  // - Otherwise, we know the use happened in a later iteration if the
+  //   move is reachable from the use.
+  auto CFA = 
std::make_unique(*TheCFG);
+  TheUseAfterMove->UseHappensInLaterLoopIteration =
+  UseBlock == MoveBlock ? Visited.contains(UseBlock)
+: CFA->isReachable(UseBlock, MoveBlock);

tchaikov wrote:

`CFGReverseBlockReachabilityAnalysis` comes with two member variables of 
`llvm::BitVector` and `llvm::DenseMap<>`, which could be potentially too large 
to put on the stack, i thought. that's why i allocated it on heap. but if you 
believe it's safe to do so. will allocate it on stack.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From e07681e21f01c56a9e2705f6380838047886598a Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 42 +---
 .../clang-tidy/utils/ExprSequence.cpp | 68 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  5 +-
 .../checkers/bugprone/use-after-move.cpp  | 50 +-
 4 files changed, 148 insertions(+), 17 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..c90c92b5f660a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -11,9 +11,11 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -34,7 +36,12 @@ struct UseAfterMove {
   const DeclRefExpr *DeclRef;
 
   // Is the order in which the move and the use are evaluated undefined?
-  bool EvaluationOrderUndefined;
+  bool EvaluationOrderUndefined = false;
+
+  // Does the use happen in a later loop iteration than the move?
+  //
+  // We default to false and change it to true if required in find().
+  bool UseHappensInLaterLoopIteration = false;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +55,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -89,7 +96,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +117,32 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
+TheUseAfterMove);
+
+  if (Found) {
+if (const CFGBlock *UseBlock =
+BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef)) {
+  // Does the use happen in 

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

tchaikov wrote:

v3:

- allocate `CFGReverseBlockReachabilityAnalysis` on stack not on heap, as it's 
small enough and can be fit in the stack.
- initialize `EvaluationOrderUndefined` in-class to be more consistent. please 
note, before this change, it's always initialized if it's going to be 
referenced.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@5chmidti hi Julian, thank you for your review, suggestions and approval. 
updated accordingly.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-11 Thread Kefu Chai via cfe-commits


@@ -110,15 +117,32 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
+TheUseAfterMove);
+
+  if (Found) {
+if (const CFGBlock *UseBlock =
+BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef)) {
+  // Does the use happen in a later loop iteration than the move?
+  // - If they are in the same CFG block, we know the use happened in a
+  //   later iteration if we visited that block a second time.
+  // - Otherwise, we know the use happened in a later iteration if the
+  //   move is reachable from the use.
+  auto CFA = 
std::make_unique(*TheCFG);
+  TheUseAfterMove->UseHappensInLaterLoopIteration =
+  UseBlock == MoveBlock ? Visited.contains(UseBlock)
+: CFA->isReachable(UseBlock, MoveBlock);

tchaikov wrote:

thank you Martin! for the detailed explanation. i concur with you and Julian 
now.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-13 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@PiotrZSL @SimplyDanny and @HerrCai0907 Hello, gentlemen. Would you be 
available to take a look at this at your earliest convenience?

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov created 
https://github.com/llvm/llvm-project/pull/93623

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```c++
a.bar(consumeA(std::move(a));
```
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).

Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

>From d787a496e2f18f32a8b90f762f7d8f649714f6e7 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH 1/2] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 64 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  6 ++
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 178 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..b8d2f1ea65d2c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const auto &Succ : Current->succs()) {
+  if (!Visited.count(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAft

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From 5fc21145abde56096b607cf123f529f97b458252 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH 1/2] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 64 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  6 ++
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 178 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..b8d2f1ea65d2c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const auto &Succ : Current->succs()) {
+  if (!Visited.count(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext 
*TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +138,31 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return find

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@martinboehme hello Martin, I resurrected your change at 
https://reviews.llvm.org/D145581?id=503330#inline-1406063 and posted here in 
hope that we can continue your efforts and finally land the change in main 
branch. Hope you don't mind that I created this without your permission. we are 
also using clang-tidy in our project, since we are using the chained expression 
like `v.foo(x, y).then(std::move(x)).then(std::move(v))` a lot, it's a little 
bit annoying that clang-tidy warns at seeing this. this reduces the 
signal-to-noise ratio of its output. 

On top of your change, I made following changes

- rebase onto the latest main HEAD
- s/const auto &Succ/const CFGBlock *Succ/, so it is more explicit that the 
element type is a pointer.
- use `Visited.Contains(e)` instead of `!Visited.count(e)`, for better 
readability
- rename `found` to `Found`, to be more consistent with the naming convention 
in LLVM
- check for null before pushing a new successor node to `Stack`, otherwise we 
could be iterating a "null" node's successors.

these changes are collected in a separate commit in this PR, if you are good 
with them, I will fold it into the first commit.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From 5fc21145abde56096b607cf123f529f97b458252 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH 1/2] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 64 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  6 ++
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 178 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..b8d2f1ea65d2c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const auto &Succ : Current->succs()) {
+  if (!Visited.count(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext 
*TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +138,31 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-  return find

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From 5cbb966128b63212dcf9f2284b9f58fbcd12cee6 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH 1/2] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 64 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  6 ++
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 178 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..b8d2f1ea65d2c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const auto &Succ : Current->succs()) {
+  if (!Visited.count(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext 
*TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +138,31 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry();
   }
 
-

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-28 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov edited 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-29 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From 3f1ef816ae2bfca3ec253f0aad5b4bb69984d60d Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 63 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  6 ++
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 177 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..f357b21b3a967 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const CFGBlock *Succ : Current->succs()) {
+  if (Succ && !Visited.contains(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext 
*TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +138,30 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry(

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-29 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov ready_for_review 
https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-29 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@martinboehme thank you! from now on, i will try to address the upcoming 
comments from reviewers if this PR is fortunate enough to get more attentions, 
and to follow it up.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-29 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From e9e03e8c07c7b8deaba7ac11a593dfb63b4c9354 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 63 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  5 +-
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 175 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..f357b21b3a967 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const CFGBlock *Succ : Current->succs()) {
+  if (Succ && !Visited.contains(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext 
*TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +138,30 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry(

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-29 Thread Kefu Chai via cfe-commits

tchaikov wrote:

v2:

- s/auto *Member/const auto *Member/
- merge into the existing ReleaseNote entry of the same check, instead of 
creating another one

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-05-30 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/93623

>From 14106f8d990c068f9f75e1ea6a1f10c4be5930f6 Mon Sep 17 00:00:00 2001
From: martinboehme 
Date: Wed, 29 May 2024 07:23:35 +0800
Subject: [PATCH] [clang-tidy] Let bugprone-use-after-move ignore the moved
 variable in callee

In C++17, callee is guaranteed to be sequenced before arguments.

This eliminates false positives in bugprone-use-after-move where a variable
is used in the callee and moved from in the arguments.

We introduce one special case: If the callee is a MemberExpr with a DeclRefExpr 
as its base, we consider it to be sequenced after the arguments. This is 
because the variable referenced in the base will only actually be accessed when 
the call happens, i.e. once all of the arguments have been evaluated. This has 
no basis in the C++ standard, but it reflects actual behavior that is relevant 
to a use-after-move scenario:
```
a.bar(consumeA(std::move(a));
In this example, we end up accessing a after it has been moved from, even 
though nominally the callee a.bar is evaluated before the argument 
consumeA(std::move(a)).
```
Treating this scenario correctly has required rewriting the logic in 
bugprone-use-after-move that governs whether the use happens in a later loop 
iteration than the move. This was previously based on an unsound heuristic 
(does the use come lexically before the move?); we now use a more rigourous 
criterion based on reachability in the CFG.

Fixes #57758
Fixes #59612
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 63 ++--
 .../clang-tidy/utils/ExprSequence.cpp | 73 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  5 +-
 .../checkers/bugprone/use-after-move.cpp  | 50 -
 4 files changed, 175 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..f357b21b3a967 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 #include "../utils/ExprSequence.h"
 #include "../utils/Matchers.h"
@@ -35,6 +36,9 @@ struct UseAfterMove {
 
   // Is the order in which the move and the use are evaluated undefined?
   bool EvaluationOrderUndefined;
+
+  // Does the use happen in a later loop iteration than the move?
+  bool UseHappensInLaterLoopIteration;
 };
 
 /// Finds uses of a variable after a move (and maintains state required by the
@@ -48,7 +52,7 @@ class UseAfterMoveFinder {
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
   bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
 
 private:
   bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
@@ -69,6 +73,30 @@ class UseAfterMoveFinder {
   llvm::SmallPtrSet Visited;
 };
 
+/// Returns whether the `Before` block can reach the `After` block.
+bool reaches(const CFGBlock *Before, const CFGBlock *After) {
+  llvm::SmallVector Stack;
+  llvm::SmallPtrSet Visited;
+
+  Stack.push_back(Before);
+  while (!Stack.empty()) {
+const CFGBlock *Current = Stack.back();
+Stack.pop_back();
+
+if (Current == After)
+  return true;
+
+Visited.insert(Current);
+
+for (const CFGBlock *Succ : Current->succs()) {
+  if (Succ && !Visited.contains(Succ))
+Stack.push_back(Succ);
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 // Matches nodes that are
@@ -89,7 +117,7 @@ UseAfterMoveFinder::UseAfterMoveFinder(ASTContext 
*TheContext)
 : Context(TheContext) {}
 
 bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
+  const DeclRefExpr *MovedVariable,
   UseAfterMove *TheUseAfterMove) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
@@ -110,15 +138,30 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   BlockMap = std::make_unique(TheCFG.get(), Context);
   Visited.clear();
 
-  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
-  if (!Block) {
+  const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
+  if (!MoveBlock) {
 // This can happen if MovingCall is in a constructor initializer, which is
 // not included in the CFG because the CFG is built only from the function
 // body.
-Block = &TheCFG->getEntry();
+MoveBlock = &TheCFG->getEntry(

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-07-08 Thread Kefu Chai via cfe-commits

tchaikov wrote:

thank you Piotr for merging this change.

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] let UseAfterMoveFinder::find() return an optional (PR #98100)

2024-07-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov created 
https://github.com/llvm/llvm-project/pull/98100

before this change, we use an output parameter so
`UseAfterMoveFinder::find()` can return the found `UseAfterMove`, and addition 
to it, `UseAfterMoveFinder::find()` return a bool, so we can tell if a 
use-after-free is identified. this arrangement could be confusing when one 
needs to understand when the each member variable of the returned 
`UseAfterMove` instance is initialized.

in this change, we return an `std::optional` instead of a bool, 
so that it's more obvious on when/where the returned `UseAfterMove` is 
initialized.

this change is a cleanup. so it does not change the behavior of this check.

>From 3537fe828bac5678970e106ac89306e465446163 Mon Sep 17 00:00:00 2001
From: Kefu Chai 
Date: Tue, 9 Jul 2024 07:58:23 +0800
Subject: [PATCH] [clang-tidy] let UseAfterMoveFinder::find() return an
 optional

before this change, we use an output parameter so
`UseAfterMoveFinder::find()` can return the found `UseAfterMove`, and
addition to it, `UseAfterMoveFinder::find()` return a bool, so we can
tell if a use-after-free is identified. this arrangement could be
confusing when one needs to understand when the each member variable of
the returned `UseAfterMove` instance is initialized.

in this change, we return an `std::optional` instead of
a bool, so that it's more obvious on when/where the returned
`UseAfterMove` is initialized.

this change is a cleanup. so it does not change the behavior of this
check.

Signed-off-by: Kefu Chai 
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 53 +--
 1 file changed, 26 insertions(+), 27 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index c90c92b5f660a..a740b602af12b 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -54,13 +54,12 @@ class UseAfterMoveFinder {
   // occurs after 'MovingCall' (the expression that performs the move). If a
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
-  bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
+  std::optional find(Stmt *CodeBlock, const Expr *MovingCall,
+   const DeclRefExpr *MovedVariable);
 
 private:
-  bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
-const ValueDecl *MovedVariable,
-UseAfterMove *TheUseAfterMove);
+  std::optional findInternal(const CFGBlock *Block, const Expr 
*MovingCall,
+   const ValueDecl *MovedVariable);
   void getUsesAndReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
  llvm::SmallVectorImpl *Uses,
  llvm::SmallPtrSetImpl *Reinits);
@@ -95,9 +94,8 @@ static StatementMatcher inDecltypeOrTemplateArg() {
 UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
 : Context(TheContext) {}
 
-bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const DeclRefExpr *MovedVariable,
-  UseAfterMove *TheUseAfterMove) {
+std::optional UseAfterMoveFinder::find(Stmt *CodeBlock, const 
Expr *MovingCall,
+ const DeclRefExpr 
*MovedVariable) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
   // lambda.
@@ -111,7 +109,7 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   std::unique_ptr TheCFG =
   CFG::buildCFG(nullptr, CodeBlock, Context, Options);
   if (!TheCFG)
-return false;
+return std::nullopt;
 
   Sequence = std::make_unique(TheCFG.get(), CodeBlock, Context);
   BlockMap = std::make_unique(TheCFG.get(), Context);
@@ -125,10 +123,9 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
 MoveBlock = &TheCFG->getEntry();
   }
 
-  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
-TheUseAfterMove);
+  auto TheUseAfterMove = findInternal(MoveBlock, MovingCall, 
MovedVariable->getDecl());
 
-  if (Found) {
+  if (TheUseAfterMove) {
 if (const CFGBlock *UseBlock =
 BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef)) {
   // Does the use happen in a later loop iteration than the move?
@@ -142,15 +139,14 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
 : CFA.isReachable(UseBlock, MoveBlock);
 }
   }
-  return Found;
+  return TheUseAfterMove;
 }
 
-bool UseAfterMoveFinder::findInternal(const CFGBlock *Block,
-   

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-07-08 Thread Kefu Chai via cfe-commits


@@ -175,6 +218,10 @@ bool UseAfterMoveFinder::findInternal(const CFGBlock 
*Block,
 MovingCall != nullptr &&
 Sequence->potentiallyAfter(MovingCall, Use);
 
+// We default to false here and change this to true if required in
+// find().
+TheUseAfterMove->UseHappensInLaterLoopIteration = false;
+

tchaikov wrote:

> for better readability. but the `optional<>` refactor is still a 
> nice-to-have, IMHO.

created #98100

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] let UseAfterMoveFinder::find() return an optional (PR #98100)

2024-07-08 Thread Kefu Chai via cfe-commits

https://github.com/tchaikov updated 
https://github.com/llvm/llvm-project/pull/98100

>From 1a2d88917db32253514fc7a533f5cb39d465a9c7 Mon Sep 17 00:00:00 2001
From: Kefu Chai 
Date: Tue, 9 Jul 2024 07:58:23 +0800
Subject: [PATCH] [clang-tidy] let UseAfterMoveFinder::find() return an
 optional

before this change, we use an output parameter so
`UseAfterMoveFinder::find()` can return the found `UseAfterMove`, and
addition to it, `UseAfterMoveFinder::find()` return a bool, so we can
tell if a use-after-free is identified. this arrangement could be
confusing when one needs to understand when the each member variable of
the returned `UseAfterMove` instance is initialized.

in this change, we return an `std::optional` instead of
a bool, so that it's more obvious on when/where the returned
`UseAfterMove` is initialized.

this change is a cleanup. so it does not change the behavior of this
check.

Signed-off-by: Kefu Chai 
---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 56 ++-
 1 file changed, 29 insertions(+), 27 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index c90c92b5f660a..8f4b5e8092dda 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -54,13 +54,13 @@ class UseAfterMoveFinder {
   // occurs after 'MovingCall' (the expression that performs the move). If a
   // use-after-move is found, writes information about it to 'TheUseAfterMove'.
   // Returns whether a use-after-move was found.
-  bool find(Stmt *CodeBlock, const Expr *MovingCall,
-const DeclRefExpr *MovedVariable, UseAfterMove *TheUseAfterMove);
+  std::optional find(Stmt *CodeBlock, const Expr *MovingCall,
+   const DeclRefExpr *MovedVariable);
 
 private:
-  bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
-const ValueDecl *MovedVariable,
-UseAfterMove *TheUseAfterMove);
+  std::optional findInternal(const CFGBlock *Block,
+   const Expr *MovingCall,
+   const ValueDecl *MovedVariable);
   void getUsesAndReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
  llvm::SmallVectorImpl *Uses,
  llvm::SmallPtrSetImpl *Reinits);
@@ -95,9 +95,9 @@ static StatementMatcher inDecltypeOrTemplateArg() {
 UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
 : Context(TheContext) {}
 
-bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
-  const DeclRefExpr *MovedVariable,
-  UseAfterMove *TheUseAfterMove) {
+std::optional
+UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall,
+ const DeclRefExpr *MovedVariable) {
   // Generate the CFG manually instead of through an AnalysisDeclContext 
because
   // it seems the latter can't be used to generate a CFG for the body of a
   // lambda.
@@ -111,7 +111,7 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
   std::unique_ptr TheCFG =
   CFG::buildCFG(nullptr, CodeBlock, Context, Options);
   if (!TheCFG)
-return false;
+return std::nullopt;
 
   Sequence = std::make_unique(TheCFG.get(), CodeBlock, Context);
   BlockMap = std::make_unique(TheCFG.get(), Context);
@@ -125,10 +125,10 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
 MoveBlock = &TheCFG->getEntry();
   }
 
-  bool Found = findInternal(MoveBlock, MovingCall, MovedVariable->getDecl(),
-TheUseAfterMove);
+  auto TheUseAfterMove =
+  findInternal(MoveBlock, MovingCall, MovedVariable->getDecl());
 
-  if (Found) {
+  if (TheUseAfterMove) {
 if (const CFGBlock *UseBlock =
 BlockMap->blockContainingStmt(TheUseAfterMove->DeclRef)) {
   // Does the use happen in a later loop iteration than the move?
@@ -142,15 +142,14 @@ bool UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr 
*MovingCall,
 : CFA.isReachable(UseBlock, MoveBlock);
 }
   }
-  return Found;
+  return TheUseAfterMove;
 }
 
-bool UseAfterMoveFinder::findInternal(const CFGBlock *Block,
-  const Expr *MovingCall,
-  const ValueDecl *MovedVariable,
-  UseAfterMove *TheUseAfterMove) {
+std::optional
+UseAfterMoveFinder::findInternal(const CFGBlock *Block, const Expr *MovingCall,
+ const ValueDecl *MovedVariable) {
   if (Visited.count(Block))
-return false;
+return std::nullopt;
 
   // Mark the block as visited (except if this is the block containing the
   // std::move() and it's being visited the first time).
@@ -189,17 +188,18 @@ bool Us

[clang-tools-extra] [clang-tidy] In C++17, callee is guaranteed to be sequenced before arguments. (PR #93623)

2024-06-24 Thread Kefu Chai via cfe-commits

tchaikov wrote:

@5chmidti @PiotrZSL Hi Julian and Piotr, thanks for taking the time to review 
my PR. I'm eager to get it merged. Is there anything else I can do to help 
facilitate the merge process? Or we can merge it now?

https://github.com/llvm/llvm-project/pull/93623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits