blob: 99758d33475b1cfe68026f3e3b773b3b0440581f [file] [log] [blame]
Alexander Kornienko5b982e52015-03-09 11:48:54 +00001//===--- UniqueptrResetReleaseCheck.cpp - clang-tidy ----------------------===//
Alexander Kornienkobc0c4232014-12-05 11:59:05 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Alexander Kornienko5b982e52015-03-09 11:48:54 +000010#include "UniqueptrResetReleaseCheck.h"
Alexander Kornienkobc0c4232014-12-05 11:59:05 +000011#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
Alexander Kornienko2b3124202015-03-02 12:25:03 +000018namespace misc {
Alexander Kornienkobc0c4232014-12-05 11:59:05 +000019
Alexander Kornienko5b982e52015-03-09 11:48:54 +000020void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman327e97b2015-08-28 19:27:19 +000021 // Only register the matchers for C++11; the functionality currently does not
22 // provide any benefit to other languages, despite being benign.
Aaron Ballmanbf891092015-08-31 15:28:57 +000023 if (!getLangOpts().CPlusPlus11)
24 return;
25
26 Finder->addMatcher(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000027 cxxMemberCallExpr(
Aaron Ballmanbf891092015-08-31 15:28:57 +000028 on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000029 callee(
30 cxxMethodDecl(hasName("reset"),
31 ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
32 decl().bind("left_class"))))),
Piotr Padlewskie93a73f2016-05-31 15:26:56 +000033 has(ignoringParenImpCasts(cxxMemberCallExpr(
Aaron Ballmanbf891092015-08-31 15:28:57 +000034 on(expr().bind("right")),
35 callee(memberExpr().bind("release_member")),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000036 callee(cxxMethodDecl(
Aaron Ballmanbf891092015-08-31 15:28:57 +000037 hasName("release"),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000038 ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
Piotr Padlewskie93a73f2016-05-31 15:26:56 +000039 decl().bind("right_class")))))))))
Aaron Ballmanbf891092015-08-31 15:28:57 +000040 .bind("reset_call"),
41 this);
Alexander Kornienkobc0c4232014-12-05 11:59:05 +000042}
43
Samuel Benzaquen91d85dc2015-04-09 17:51:01 +000044namespace {
45const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
46 StringRef ID) {
47 const auto *Class =
48 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
49 if (!Class)
50 return nullptr;
51 auto DeleterArgument = Class->getTemplateArgs()[1];
52 if (DeleterArgument.getKind() != TemplateArgument::Type)
53 return nullptr;
54 return DeleterArgument.getAsType().getTypePtr();
55}
56
57bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
58 const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
59 const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
60
61 if (LeftDeleterType->getUnqualifiedDesugaredType() ==
62 RightDeleterType->getUnqualifiedDesugaredType()) {
63 // Same type. We assume they are compatible.
64 // This check handles the case where the deleters are function pointers.
65 return true;
66 }
67
68 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
69 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
70 if (!LeftDeleter || !RightDeleter)
71 return false;
72
73 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
74 // Same class. We assume they are compatible.
75 return true;
76 }
77
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()) {
85 // They are different instantiations of the same template. We assume they
86 // are compatible.
87 // This handles things like std::default_delete<Base> vs.
88 // std::default_delete<Derived>.
89 return true;
90 }
91 return false;
92}
93
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000094} // namespace
Samuel Benzaquen91d85dc2015-04-09 17:51:01 +000095
Alexander Kornienko5b982e52015-03-09 11:48:54 +000096void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
Samuel Benzaquen91d85dc2015-04-09 17:51:01 +000097 if (!areDeletersCompatible(Result))
98 return;
99
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000100 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");
107
108 std::string LeftText = clang::Lexer::getSourceText(
109 CharSourceRange::getTokenRange(Left->getSourceRange()),
Gabor Horvathafad84c2016-09-24 02:13:45 +0000110 *Result.SourceManager, getLangOpts());
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000111 std::string RightText = clang::Lexer::getSourceText(
112 CharSourceRange::getTokenRange(Right->getSourceRange()),
Gabor Horvathafad84c2016-09-24 02:13:45 +0000113 *Result.SourceManager, getLangOpts());
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000114
115 if (ResetMember->isArrow())
116 LeftText = "*" + LeftText;
117 if (ReleaseMember->isArrow())
118 RightText = "*" + RightText;
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000119 std::string DiagText;
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000120 // Even if x was rvalue, *x is not rvalue anymore.
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000121 if (!Right->isRValue() || ReleaseMember->isArrow()) {
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000122 RightText = "std::move(" + RightText + ")";
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000123 DiagText = "prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
124 } else {
125 DiagText =
126 "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
127 }
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000128 std::string NewText = LeftText + " = " + RightText;
129
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000130 diag(ResetMember->getExprLoc(), DiagText) << FixItHint::CreateReplacement(
131 CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000132}
133
Alexander Kornienko2b3124202015-03-02 12:25:03 +0000134} // namespace misc
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000135} // namespace tidy
136} // namespace clang