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 ();
@@ -389,8 +398,9 @@ void UseAfterMoveFinder::getReinits(
389398}
390399
391400enum class MoveType {
392- Move, // std::move
393- Forward, // std::forward
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,55 @@ 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 );
421+ const int Kind = static_cast < int > (Type);
412422
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" ,
423+ Check->diag (UseLoc,
424+ " '%0' used after it was %select{forwarded|moved|invalidated}1" )
425+ << MoveArg->getDecl ()->getName () << Kind;
426+ Check->diag (MoveLoc, " %select{forward|move|invalidation}0 occurred here" ,
416427 DiagnosticIDs::Note)
417- << IsMove ;
428+ << Kind ;
418429 if (Use.EvaluationOrderUndefined ) {
419430 Check->diag (
420431 UseLoc,
421- " the use and %select{forward|move}0 are unsequenced, i.e. "
432+ " the use and %select{forward|move|invalidation }0 are unsequenced, i.e. "
422433 " there is no guarantee about the order in which they are evaluated" ,
423434 DiagnosticIDs::Note)
424- << IsMove ;
435+ << Kind ;
425436 } else if (Use.UseHappensInLaterLoopIteration ) {
426437 Check->diag (UseLoc,
427438 " the use happens in a later loop iteration than the "
428- " %select{forward|move}0" ,
439+ " %select{forward|move|invalidation }0" ,
429440 DiagnosticIDs::Note)
430- << IsMove ;
441+ << Kind ;
431442 }
432443}
433444
445+ UseAfterMoveCheck::UseAfterMoveCheck (StringRef Name, ClangTidyContext *Context)
446+ : ClangTidyCheck(Name, Context),
447+ InvalidationFunctions (utils::options::parseStringList(
448+ Options.get(" InvalidationFunctions" , " " ))) {}
449+
450+ void UseAfterMoveCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
451+ Options.store (Opts, " InvalidationFunctions" ,
452+ utils::options::serializeStringList (InvalidationFunctions));
453+ }
454+
434455void UseAfterMoveCheck::registerMatchers (MatchFinder *Finder) {
435456 // try_emplace is a common maybe-moving function that returns a
436457 // bool to tell callers whether it moved. Ignore std::move inside
437458 // try_emplace to avoid false positives as we don't track uses of
438459 // the bool.
439460 auto TryEmplaceMatcher =
440461 cxxMemberCallExpr (callee (cxxMethodDecl (hasName (" try_emplace" ))));
462+ auto Arg = declRefExpr ().bind (" arg" );
463+ auto IsMemberCallee = callee (functionDecl (unless (isStaticStorageClass ())));
441464 auto CallMoveMatcher =
442- callExpr (argumentCountIs (1 ),
443- callee (functionDecl (hasAnyName (" ::std::move" , " ::std::forward" ))
465+ callExpr (callee (functionDecl (getNameMatcher (InvalidationFunctions))
444466 .bind (" move-decl" )),
445- hasArgument (0 , declRefExpr ().bind (" arg" )),
467+ anyOf (cxxMemberCallExpr (IsMemberCallee, on (Arg)),
468+ callExpr (unless (cxxMemberCallExpr (IsMemberCallee)),
469+ hasArgument (0 , Arg))),
446470 unless (inDecltypeOrTemplateArg ()),
447471 unless (hasParent (TryEmplaceMatcher)), expr ().bind (" call-move" ),
448472 anyOf (hasAncestor (compoundStmt (
@@ -521,7 +545,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
521545 }
522546
523547 for (Stmt *CodeBlock : CodeBlocks) {
524- UseAfterMoveFinder Finder (Result.Context );
548+ UseAfterMoveFinder Finder (Result.Context , InvalidationFunctions );
525549 if (auto Use = Finder.find (CodeBlock, MovingCall, Arg))
526550 emitDiagnostic (MovingCall, Arg, *Use, this , Result.Context ,
527551 determineMoveType (MoveDecl));
0 commit comments