1919
2020#include " ../utils/ExprSequence.h"
2121#include " ../utils/Matchers.h"
22+ #include " ../utils/OptionsUtils.h"
2223#include < optional>
2324
2425using namespace clang ::ast_matchers;
@@ -48,7 +49,8 @@ struct UseAfterMove {
4849// / various internal helper functions).
4950class UseAfterMoveFinder {
5051public:
51- UseAfterMoveFinder (ASTContext *TheContext);
52+ UseAfterMoveFinder (ASTContext *TheContext,
53+ llvm::ArrayRef<StringRef> InvalidationFunctions);
5254
5355 // Within the given code block, finds the first use of 'MovedVariable' that
5456 // occurs after 'MovingCall' (the expression that performs the move). If a
@@ -71,13 +73,19 @@ class UseAfterMoveFinder {
7173 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
7274
7375 ASTContext *Context;
76+ llvm::ArrayRef<StringRef> InvalidationFunctions;
7477 std::unique_ptr<ExprSequence> Sequence;
7578 std::unique_ptr<StmtToBlockMap> BlockMap;
7679 llvm::SmallPtrSet<const CFGBlock *, 8 > Visited;
7780};
7881
7982} // namespace
8083
84+ static auto getNameMatcher (llvm::ArrayRef<StringRef> InvalidationFunctions) {
85+ return anyOf (hasAnyName (" ::std::move" , " ::std::forward" ),
86+ matchers::matchesAnyListedName (InvalidationFunctions));
87+ }
88+
8189// Matches nodes that are
8290// - Part of a decltype argument or class template argument (we check this by
8391// seeing if they are children of a TypeLoc), or
@@ -92,8 +100,9 @@ static StatementMatcher inDecltypeOrTemplateArg() {
92100 hasAncestor (expr (hasUnevaluatedContext ())));
93101}
94102
95- UseAfterMoveFinder::UseAfterMoveFinder (ASTContext *TheContext)
96- : Context(TheContext) {}
103+ UseAfterMoveFinder::UseAfterMoveFinder (
104+ ASTContext *TheContext, llvm::ArrayRef<StringRef> InvalidationFunctions)
105+ : Context(TheContext), InvalidationFunctions(InvalidationFunctions) {}
97106
98107std::optional<UseAfterMove>
99108UseAfterMoveFinder::find (Stmt *CodeBlock, const Expr *MovingCall,
@@ -359,7 +368,7 @@ void UseAfterMoveFinder::getReinits(
359368 unless (parmVarDecl (hasType (
360369 references (qualType (isConstQualified ())))))),
361370 unless (callee (functionDecl (
362- hasAnyName ( " ::std::move " , " ::std::forward " )))))))
371+ getNameMatcher (InvalidationFunctions )))))))
363372 .bind (" reinit" );
364373
365374 Stmts->clear ();
@@ -388,9 +397,10 @@ void UseAfterMoveFinder::getReinits(
388397 }
389398}
390399
391- enum class MoveType {
392- Move, // std::move
393- Forward, // std::forward
400+ enum MoveType {
401+ Forward, // std::forward
402+ Move, // std::move
403+ Invalidation, // other
394404};
395405
396406static MoveType determineMoveType (const FunctionDecl *FuncDecl) {
@@ -399,7 +409,7 @@ static MoveType determineMoveType(const FunctionDecl *FuncDecl) {
399409 if (FuncDecl->getName () == " forward" )
400410 return MoveType::Forward;
401411
402- llvm_unreachable ( " Invalid move type " ) ;
412+ return MoveType::Invalidation ;
403413}
404414
405415static void emitDiagnostic (const Expr *MovingCall, const DeclRefExpr *MoveArg,
@@ -408,41 +418,53 @@ static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg,
408418 const SourceLocation UseLoc = Use.DeclRef ->getExprLoc ();
409419 const SourceLocation MoveLoc = MovingCall->getExprLoc ();
410420
411- const bool IsMove = (Type == MoveType::Move);
412-
413- Check->diag (UseLoc, " '%0' used after it was %select{forwarded|moved}1" )
414- << MoveArg->getDecl ()->getName () << IsMove;
415- Check->diag (MoveLoc, " %select{forward|move}0 occurred here" ,
421+ Check->diag (UseLoc,
422+ " '%0' used after it was %select{forwarded|moved|invalidated}1" )
423+ << MoveArg->getDecl ()->getName () << Type;
424+ Check->diag (MoveLoc, " %select{forward|move|invalidation}0 occurred here" ,
416425 DiagnosticIDs::Note)
417- << IsMove ;
426+ << Type ;
418427 if (Use.EvaluationOrderUndefined ) {
419428 Check->diag (
420429 UseLoc,
421- " the use and %select{forward|move}0 are unsequenced, i.e. "
430+ " the use and %select{forward|move|invalidation }0 are unsequenced, i.e. "
422431 " there is no guarantee about the order in which they are evaluated" ,
423432 DiagnosticIDs::Note)
424- << IsMove ;
433+ << Type ;
425434 } else if (Use.UseHappensInLaterLoopIteration ) {
426435 Check->diag (UseLoc,
427436 " the use happens in a later loop iteration than the "
428- " %select{forward|move}0" ,
437+ " %select{forward|move|invalidation }0" ,
429438 DiagnosticIDs::Note)
430- << IsMove ;
439+ << Type ;
431440 }
432441}
433442
443+ UseAfterMoveCheck::UseAfterMoveCheck (StringRef Name, ClangTidyContext *Context)
444+ : ClangTidyCheck(Name, Context),
445+ InvalidationFunctions (utils::options::parseStringList(
446+ Options.get(" InvalidationFunctions" , " " ))) {}
447+
448+ void UseAfterMoveCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
449+ Options.store (Opts, " InvalidationFunctions" ,
450+ utils::options::serializeStringList (InvalidationFunctions));
451+ }
452+
434453void UseAfterMoveCheck::registerMatchers (MatchFinder *Finder) {
435454 // try_emplace is a common maybe-moving function that returns a
436455 // bool to tell callers whether it moved. Ignore std::move inside
437456 // try_emplace to avoid false positives as we don't track uses of
438457 // the bool.
439458 auto TryEmplaceMatcher =
440459 cxxMemberCallExpr (callee (cxxMethodDecl (hasName (" try_emplace" ))));
460+ auto Arg = declRefExpr ().bind (" arg" );
461+ auto IsMemberCallee = callee (functionDecl (unless (isStaticStorageClass ())));
441462 auto CallMoveMatcher =
442- callExpr (argumentCountIs (1 ),
443- callee (functionDecl (hasAnyName (" ::std::move" , " ::std::forward" ))
463+ callExpr (callee (functionDecl (getNameMatcher (InvalidationFunctions))
444464 .bind (" move-decl" )),
445- hasArgument (0 , declRefExpr ().bind (" arg" )),
465+ anyOf (cxxMemberCallExpr (IsMemberCallee, on (Arg)),
466+ callExpr (unless (cxxMemberCallExpr (IsMemberCallee)),
467+ hasArgument (0 , Arg))),
446468 unless (inDecltypeOrTemplateArg ()),
447469 unless (hasParent (TryEmplaceMatcher)), expr ().bind (" call-move" ),
448470 anyOf (hasAncestor (compoundStmt (
@@ -521,7 +543,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
521543 }
522544
523545 for (Stmt *CodeBlock : CodeBlocks) {
524- UseAfterMoveFinder Finder (Result.Context );
546+ UseAfterMoveFinder Finder (Result.Context , InvalidationFunctions );
525547 if (auto Use = Finder.find (CodeBlock, MovingCall, Arg))
526548 emitDiagnostic (MovingCall, Arg, *Use, this , Result.Context ,
527549 determineMoveType (MoveDecl));
0 commit comments