10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/Lex/Lexer.h"
19 void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) {
22 on(expr().bind(
"left")), callee(memberExpr().bind(
"reset_member")),
24 cxxMethodDecl(hasName(
"reset"),
25 ofClass(cxxRecordDecl(hasName(
"::std::unique_ptr"),
26 decl().bind(
"left_class"))))),
27 has(ignoringParenImpCasts(cxxMemberCallExpr(
28 on(expr().bind(
"right")),
29 callee(memberExpr().bind(
"release_member")),
32 ofClass(cxxRecordDecl(hasName(
"::std::unique_ptr"),
33 decl().bind(
"right_class")))))))))
39 const Type *getDeleterForUniquePtr(
const MatchFinder::MatchResult &Result,
42 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
45 auto DeleterArgument = Class->getTemplateArgs()[1];
48 return DeleterArgument.getAsType().getTypePtr();
51 bool areDeletersCompatible(
const MatchFinder::MatchResult &Result) {
52 const Type *LeftDeleterType = getDeleterForUniquePtr(Result,
"left_class");
53 const Type *RightDeleterType = getDeleterForUniquePtr(Result,
"right_class");
55 if (LeftDeleterType->getUnqualifiedDesugaredType() ==
56 RightDeleterType->getUnqualifiedDesugaredType()) {
62 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
63 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
64 if (!LeftDeleter || !RightDeleter)
67 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
72 const auto *LeftAsTemplate =
73 dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
74 const auto *RightAsTemplate =
75 dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
76 if (LeftAsTemplate && RightAsTemplate &&
77 LeftAsTemplate->getSpecializedTemplate() ==
78 RightAsTemplate->getSpecializedTemplate()) {
90 void UniqueptrResetReleaseCheck::check(
const MatchFinder::MatchResult &Result) {
91 if (!areDeletersCompatible(Result))
94 const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>(
"reset_member");
95 const auto *ReleaseMember =
96 Result.Nodes.getNodeAs<MemberExpr>(
"release_member");
97 const auto *Right = Result.Nodes.getNodeAs<Expr>(
"right");
98 const auto *Left = Result.Nodes.getNodeAs<Expr>(
"left");
99 const auto *ResetCall =
100 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"reset_call");
102 std::string LeftText = std::string(clang::Lexer::getSourceText(
103 CharSourceRange::getTokenRange(Left->getSourceRange()),
104 *Result.SourceManager, getLangOpts()));
105 std::string RightText = std::string(clang::Lexer::getSourceText(
106 CharSourceRange::getTokenRange(Right->getSourceRange()),
107 *Result.SourceManager, getLangOpts()));
109 if (ResetMember->isArrow())
110 LeftText =
"*" + LeftText;
111 if (ReleaseMember->isArrow())
112 RightText =
"*" + RightText;
113 std::string DiagText;
115 if (!Right->isRValue() || ReleaseMember->isArrow()) {
116 RightText =
"std::move(" + RightText +
")";
117 DiagText =
"prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
120 "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
122 std::string NewText = LeftText +
" = " + RightText;
124 diag(ResetMember->getExprLoc(), DiagText) << FixItHint::CreateReplacement(
125 CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);