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 const CXXRecordDecl *ND = Node.getDefinition();
30 if (!ND->hasNonTrivialMoveAssignment())
32 for (
const CXXMethodDecl *CM : ND->methods())
33 if (CM->isMoveAssignmentOperator())
34 return !CM->isDeleted() && CM->getAccess() == AS_public;
35 llvm_unreachable(
"Move Assignment Operator Not Found");
39 return Node->isLValueReferenceType();
42AST_MATCHER(DeclRefExpr, refersToEnclosingVariableOrCapture) {
43 return Node.refersToEnclosingVariableOrCapture();
46AST_MATCHER(CXXOperatorCallExpr, isCopyAssignmentOperator) {
47 if (
const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Node.getDirectCallee()))
48 return MD->isCopyAssignmentOperator();
54 AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl)) {
55 return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
62 auto AssignOperatorExpr =
64 isCopyAssignmentOperator(),
65 hasArgument(0, hasType(cxxRecordDecl(
66 hasAccessibleNonTrivialMoveAssignment()))),
71 hasType(qualType(unless(anyOf(
72 isLValueReferenceType(),
75 unless(refersToEnclosingVariableOrCapture()))
76 .bind(
"assign-value")),
77 forCallable(functionDecl().bind(
"within-func")), unless(isInMacro()))
79 Finder->addMatcher(AssignOperatorExpr,
this);
82const CFG *UseStdMoveCheck::getCFG(
const FunctionDecl *FD,
83 ASTContext *Context) {
84 std::unique_ptr<CFG> &TheCFG = CFGCache[FD];
86 const CFG::BuildOptions Options;
87 std::unique_ptr<CFG> FCFG =
88 CFG::buildCFG(
nullptr, FD->getBody(), Context, Options);
97 const auto *AssignExpr = Result.Nodes.getNodeAs<Expr>(
"assign");
98 const auto *AssignValue = Result.Nodes.getNodeAs<DeclRefExpr>(
"assign-value");
99 const auto *WithinFunctionDecl =
100 Result.Nodes.getNodeAs<FunctionDecl>(
"within-func");
102 const CFG *TheCFG = getCFG(WithinFunctionDecl, Result.Context);
122 unsigned RemainingSuccessors;
124 llvm::DenseMap<const CFGBlock *, BlockState> CFGState;
125 for (
const auto *B : *TheCFG)
126 CFGState.try_emplace(B, BlockState{
true, B->succ_size()});
128 const CFGBlock &TheExit = TheCFG->getExit();
129 std::vector<const CFGBlock *> WorkList = {&TheExit};
131 while (!WorkList.empty()) {
132 const CFGBlock *B = WorkList.back();
134 const BlockState &BS = CFGState.find(B)->second;
138 assert(BS.RemainingSuccessors == 0 &&
139 "All successors have been processed.");
140 bool ReferencesAssignedValue =
false;
141 for (
const CFGElement &Elt : llvm::reverse(*B)) {
142 if (Elt.getKind() != CFGElement::Kind::Statement)
145 const Stmt *EltStmt = Elt.castAs<CFGStmt>().getStmt();
146 if (EltStmt == AssignExpr) {
147 const StringRef AssignValueName = AssignValue->getDecl()->getName();
148 diag(AssignValue->getBeginLoc(),
"'%0' could be moved here")
150 << FixItHint::CreateReplacement(
151 AssignValue->getLocation(),
152 (
"std::move(" + AssignValueName +
")").str());
153 ReferencesAssignedValue =
true;
161 ReferencesAssignedValue =
true;
165 if (ReferencesAssignedValue) {
167 for (
const auto &S : B->preds()) {
168 if (!S.isReachable())
170 CFGState.find(&*S)->second.Ready =
false;
174 for (
const auto &S : B->preds()) {
175 if (!S.isReachable())
177 auto &W = CFGState.find(&*S)->second;
179 if (--W.RemainingSuccessors == 0)
180 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.