11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
14 using namespace clang::ast_matchers;
20 void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *
Finder) {
23 if (!getLangOpts().CPlusPlus11)
28 on(expr().bind(
"left")), callee(memberExpr().bind(
"reset_member")),
30 cxxMethodDecl(hasName(
"reset"),
31 ofClass(cxxRecordDecl(hasName(
"::std::unique_ptr"),
32 decl().bind(
"left_class"))))),
33 has(cxxMemberCallExpr(
34 on(expr().bind(
"right")),
35 callee(memberExpr().bind(
"release_member")),
38 ofClass(cxxRecordDecl(hasName(
"::std::unique_ptr"),
39 decl().bind(
"right_class"))))))))
45 const Type *getDeleterForUniquePtr(
const MatchFinder::MatchResult &
Result,
48 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
51 auto DeleterArgument = Class->getTemplateArgs()[1];
52 if (DeleterArgument.getKind() != TemplateArgument::Type)
54 return DeleterArgument.getAsType().getTypePtr();
57 bool areDeletersCompatible(
const MatchFinder::MatchResult &Result) {
58 const Type *LeftDeleterType = getDeleterForUniquePtr(Result,
"left_class");
59 const Type *RightDeleterType = getDeleterForUniquePtr(Result,
"right_class");
61 if (LeftDeleterType->getUnqualifiedDesugaredType() ==
62 RightDeleterType->getUnqualifiedDesugaredType()) {
68 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
69 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
70 if (!LeftDeleter || !RightDeleter)
73 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
78 const auto *LeftAsTemplate =
79 dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
80 const auto *RightAsTemplate =
81 dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
82 if (LeftAsTemplate && RightAsTemplate &&
83 LeftAsTemplate->getSpecializedTemplate() ==
84 RightAsTemplate->getSpecializedTemplate()) {
96 void UniqueptrResetReleaseCheck::check(
const MatchFinder::MatchResult &Result) {
97 if (!areDeletersCompatible(Result))
100 const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>(
"reset_member");
101 const auto *ReleaseMember =
102 Result.Nodes.getNodeAs<MemberExpr>(
"release_member");
103 const auto *Right = Result.Nodes.getNodeAs<Expr>(
"right");
104 const auto *Left = Result.Nodes.getNodeAs<Expr>(
"left");
105 const auto *ResetCall =
106 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"reset_call");
108 std::string LeftText = clang::Lexer::getSourceText(
109 CharSourceRange::getTokenRange(Left->getSourceRange()),
110 *Result.SourceManager, Result.Context->getLangOpts());
111 std::string RightText = clang::Lexer::getSourceText(
112 CharSourceRange::getTokenRange(Right->getSourceRange()),
113 *Result.SourceManager, Result.Context->getLangOpts());
115 if (ResetMember->isArrow())
116 LeftText =
"*" + LeftText;
117 if (ReleaseMember->isArrow())
118 RightText =
"*" + RightText;
119 std::string DiagText;
121 if (!Right->isRValue() || ReleaseMember->isArrow()) {
122 RightText =
"std::move(" + RightText +
")";
123 DiagText =
"prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
126 "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
128 std::string NewText = LeftText +
" = " + RightText;
130 diag(ResetMember->getExprLoc(), DiagText)
131 << FixItHint::CreateReplacement(
132 CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
std::unique_ptr< ast_matchers::MatchFinder > Finder