13#include "clang/AST/Expr.h"
14#include "clang/AST/ExprCXX.h"
15#include "clang/ASTMatchers/ASTMatchers.h"
16#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
17#include "clang/Lex/Lexer.h"
18#include "llvm/ADT/DenseMap.h"
19#include "llvm/ADT/STLExtras.h"
26AST_MATCHER(CXXRecordDecl, hasAccessibleNonTrivialMoveAssignment) {
27 if (!Node.hasNonTrivialMoveAssignment())
29 for (
const auto *CM : Node.methods())
30 if (CM->isMoveAssignmentOperator())
31 return !CM->isDeleted() && CM->getAccess() == AS_public;
32 llvm_unreachable(
"Move Assignment Operator Not Found");
36 return Node->isLValueReferenceType();
39AST_MATCHER(DeclRefExpr, refersToEnclosingVariableOrCapture) {
40 return Node.refersToEnclosingVariableOrCapture();
43AST_MATCHER(CXXOperatorCallExpr, isCopyAssignmentOperator) {
44 if (
const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Node.getDirectCallee()))
45 return MD->isCopyAssignmentOperator();
51 AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl)) {
52 return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
59 auto AssignOperatorExpr =
61 isCopyAssignmentOperator(),
62 hasArgument(0, hasType(cxxRecordDecl(
63 hasAccessibleNonTrivialMoveAssignment()))),
68 hasType(qualType(unless(anyOf(
69 isLValueReferenceType(),
72 unless(refersToEnclosingVariableOrCapture()))
73 .bind(
"assign-value")),
74 forCallable(functionDecl().bind(
"within-func")), unless(isInMacro()))
76 Finder->addMatcher(AssignOperatorExpr,
this);
79const CFG *UseStdMoveCheck::getCFG(
const FunctionDecl *FD,
80 ASTContext *Context) {
81 std::unique_ptr<CFG> &TheCFG = CFGCache[FD];
83 const CFG::BuildOptions Options;
84 std::unique_ptr<CFG> FCFG =
85 CFG::buildCFG(
nullptr, FD->getBody(), Context, Options);
94 const auto *AssignExpr = Result.Nodes.getNodeAs<Expr>(
"assign");
95 const auto *AssignValue = Result.Nodes.getNodeAs<DeclRefExpr>(
"assign-value");
96 const auto *WithinFunctionDecl =
97 Result.Nodes.getNodeAs<FunctionDecl>(
"within-func");
99 const CFG *TheCFG = getCFG(WithinFunctionDecl, Result.Context);
119 unsigned RemainingSuccessors;
121 llvm::DenseMap<const CFGBlock *, BlockState> CFGState;
122 for (
const auto *B : *TheCFG)
123 CFGState.try_emplace(B, BlockState{
true, B->succ_size()});
125 const CFGBlock &TheExit = TheCFG->getExit();
126 std::vector<const CFGBlock *> WorkList = {&TheExit};
128 while (!WorkList.empty()) {
129 const CFGBlock *B = WorkList.back();
131 const BlockState &BS = CFGState.find(B)->second;
135 assert(BS.RemainingSuccessors == 0 &&
136 "All successors have been processed.");
137 bool ReferencesAssignedValue =
false;
138 for (
const CFGElement &Elt : llvm::reverse(*B)) {
139 if (Elt.getKind() != CFGElement::Kind::Statement)
142 const Stmt *EltStmt = Elt.castAs<CFGStmt>().getStmt();
143 if (EltStmt == AssignExpr) {
144 const StringRef AssignValueName = AssignValue->getDecl()->getName();
145 diag(AssignValue->getBeginLoc(),
"'%0' could be moved here")
147 << FixItHint::CreateReplacement(
148 AssignValue->getLocation(),
149 (
"std::move(" + AssignValueName +
")").str());
150 ReferencesAssignedValue =
true;
158 ReferencesAssignedValue =
true;
162 if (ReferencesAssignedValue) {
164 for (
const auto &S : B->preds()) {
165 if (!S.isReachable())
167 CFGState.find(&*S)->second.Ready =
false;
171 for (
const auto &S : B->preds()) {
172 if (!S.isReachable())
174 auto &W = CFGState.find(&*S)->second;
176 if (--W.RemainingSuccessors == 0)
177 WorkList.push_back(&*S);
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
AST_MATCHER(BinaryOperator, isRelationalOperator)
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to VarDecl within Stmt.