Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 66 additions & 57 deletions clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,70 @@ static auto getNameMatcher(llvm::ArrayRef<StringRef> InvalidationFunctions) {
matchers::matchesAnyListedName(InvalidationFunctions));
}

static StatementMatcher
makeReinitMatcher(const ValueDecl *MovedVariable,
llvm::ArrayRef<StringRef> InvalidationFunctions) {
const auto DeclRefMatcher =
declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind("declref");

const auto StandardContainerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
"::std::basic_string", "::std::vector", "::std::deque",
"::std::forward_list", "::std::list", "::std::set", "::std::map",
"::std::multiset", "::std::multimap", "::std::unordered_set",
"::std::unordered_map", "::std::unordered_multiset",
"::std::unordered_multimap"))))));

const auto StandardResettableOwnerTypeMatcher = hasType(
hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
hasAnyName("::std::unique_ptr", "::std::shared_ptr",
"::std::weak_ptr", "::std::optional", "::std::any"))))));

// Matches different types of reinitialization.
return stmt(
anyOf(
// Assignment. In addition to the overloaded assignment
// operator, test for built-in assignment as well, since
// template functions may be instantiated to use std::move() on
// built-in types.
binaryOperation(hasOperatorName("="), hasLHS(DeclRefMatcher)),
// Declaration. We treat this as a type of reinitialization
// too, so we don't need to treat it separately.
declStmt(hasDescendant(equalsNode(MovedVariable))),
// clear() and assign() on standard containers.
cxxMemberCallExpr(
on(expr(DeclRefMatcher, StandardContainerTypeMatcher)),
// To keep the matcher simple, we check for assign() calls
// on all standard containers, even though only vector,
// deque, forward_list and list have assign(). If assign()
// is called on any of the other containers, this will be
// flagged by a compile error anyway.
callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
// reset() on standard smart pointers.
cxxMemberCallExpr(on(expr(DeclRefMatcher,
StandardResettableOwnerTypeMatcher)),
callee(cxxMethodDecl(hasName("reset")))),
// Methods that have the [[clang::reinitializes]] attribute.
cxxMemberCallExpr(on(DeclRefMatcher),
callee(cxxMethodDecl(
hasAttr(clang::attr::Reinitializes)))),
// Passing variable to a function as a non-const pointer.
callExpr(forEachArgumentWithParam(
unaryOperator(hasOperatorName("&"),
hasUnaryOperand(DeclRefMatcher)),
unless(
parmVarDecl(hasType(pointsTo(isConstQualified())))))),
// Passing variable to a function as a non-const lvalue
// reference (unless that function is std::move()).
callExpr(forEachArgumentWithParam(
traverse(TK_AsIs, DeclRefMatcher),
unless(parmVarDecl(hasType(
references(qualType(isConstQualified())))))),
unless(callee(functionDecl(
getNameMatcher(InvalidationFunctions)))))))
.bind("reinit");
}

// Matches nodes that are
// - Part of a decltype argument or class template argument (we check this by
// seeing if they are children of a TypeLoc), or
Expand Down Expand Up @@ -313,63 +377,8 @@ void UseAfterMoveFinder::getReinits(
const CFGBlock *Block, const ValueDecl *MovedVariable,
llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
auto DeclRefMatcher =
declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind("declref");

auto StandardContainerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
"::std::basic_string", "::std::vector", "::std::deque",
"::std::forward_list", "::std::list", "::std::set", "::std::map",
"::std::multiset", "::std::multimap", "::std::unordered_set",
"::std::unordered_map", "::std::unordered_multiset",
"::std::unordered_multimap"))))));

auto StandardResettableOwnerTypeMatcher = hasType(
hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
hasAnyName("::std::unique_ptr", "::std::shared_ptr",
"::std::weak_ptr", "::std::optional", "::std::any"))))));

// Matches different types of reinitialization.
auto ReinitMatcher =
stmt(anyOf(
// Assignment. In addition to the overloaded assignment operator,
// test for built-in assignment as well, since template functions
// may be instantiated to use std::move() on built-in types.
binaryOperation(hasOperatorName("="), hasLHS(DeclRefMatcher)),
// Declaration. We treat this as a type of reinitialization too,
// so we don't need to treat it separately.
declStmt(hasDescendant(equalsNode(MovedVariable))),
// clear() and assign() on standard containers.
cxxMemberCallExpr(
on(expr(DeclRefMatcher, StandardContainerTypeMatcher)),
// To keep the matcher simple, we check for assign() calls
// on all standard containers, even though only vector,
// deque, forward_list and list have assign(). If assign()
// is called on any of the other containers, this will be
// flagged by a compile error anyway.
callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
// reset() on standard smart pointers.
cxxMemberCallExpr(
on(expr(DeclRefMatcher, StandardResettableOwnerTypeMatcher)),
callee(cxxMethodDecl(hasName("reset")))),
// Methods that have the [[clang::reinitializes]] attribute.
cxxMemberCallExpr(
on(DeclRefMatcher),
callee(cxxMethodDecl(hasAttr(clang::attr::Reinitializes)))),
// Passing variable to a function as a non-const pointer.
callExpr(forEachArgumentWithParam(
unaryOperator(hasOperatorName("&"),
hasUnaryOperand(DeclRefMatcher)),
unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
// Passing variable to a function as a non-const lvalue reference
// (unless that function is std::move()).
callExpr(forEachArgumentWithParam(
traverse(TK_AsIs, DeclRefMatcher),
unless(parmVarDecl(hasType(
references(qualType(isConstQualified())))))),
unless(callee(functionDecl(
getNameMatcher(InvalidationFunctions)))))))
.bind("reinit");
const auto ReinitMatcher =
makeReinitMatcher(MovedVariable, InvalidationFunctions);

Stmts->clear();
DeclRefs->clear();
Expand Down
Loading