blob: e42e3a6f201635eb0b6b7a0852c28b7c8794b139 [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.
23 if (getLangOpts().CPlusPlus11) {
24 Finder->addMatcher(
25 memberCallExpr(
26 on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
27 callee(methodDecl(hasName("reset"),
28 ofClass(recordDecl(hasName("::std::unique_ptr"),
29 decl().bind("left_class"))))),
30 has(memberCallExpr(
31 on(expr().bind("right")),
32 callee(memberExpr().bind("release_member")),
33 callee(methodDecl(
34 hasName("release"),
35 ofClass(recordDecl(hasName("::std::unique_ptr"),
36 decl().bind("right_class"))))))))
37 .bind("reset_call"),
38 this);
39 }
Alexander Kornienkobc0c4232014-12-05 11:59:05 +000040}
41
Samuel Benzaquen91d85dc2015-04-09 17:51:01 +000042namespace {
43const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
44 StringRef ID) {
45 const auto *Class =
46 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
47 if (!Class)
48 return nullptr;
49 auto DeleterArgument = Class->getTemplateArgs()[1];
50 if (DeleterArgument.getKind() != TemplateArgument::Type)
51 return nullptr;
52 return DeleterArgument.getAsType().getTypePtr();
53}
54
55bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
56 const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
57 const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
58
59 if (LeftDeleterType->getUnqualifiedDesugaredType() ==
60 RightDeleterType->getUnqualifiedDesugaredType()) {
61 // Same type. We assume they are compatible.
62 // This check handles the case where the deleters are function pointers.
63 return true;
64 }
65
66 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
67 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
68 if (!LeftDeleter || !RightDeleter)
69 return false;
70
71 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
72 // Same class. We assume they are compatible.
73 return true;
74 }
75
76 const auto *LeftAsTemplate =
77 dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
78 const auto *RightAsTemplate =
79 dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
80 if (LeftAsTemplate && RightAsTemplate &&
81 LeftAsTemplate->getSpecializedTemplate() ==
82 RightAsTemplate->getSpecializedTemplate()) {
83 // They are different instantiations of the same template. We assume they
84 // are compatible.
85 // This handles things like std::default_delete<Base> vs.
86 // std::default_delete<Derived>.
87 return true;
88 }
89 return false;
90}
91
92} // namespace
93
Alexander Kornienko5b982e52015-03-09 11:48:54 +000094void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
Samuel Benzaquen91d85dc2015-04-09 17:51:01 +000095 if (!areDeletersCompatible(Result))
96 return;
97
Alexander Kornienkobc0c4232014-12-05 11:59:05 +000098 const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
99 const auto *ReleaseMember =
100 Result.Nodes.getNodeAs<MemberExpr>("release_member");
101 const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
102 const auto *Left = Result.Nodes.getNodeAs<Expr>("left");
103 const auto *ResetCall =
104 Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
105
106 std::string LeftText = clang::Lexer::getSourceText(
107 CharSourceRange::getTokenRange(Left->getSourceRange()),
108 *Result.SourceManager, Result.Context->getLangOpts());
109 std::string RightText = clang::Lexer::getSourceText(
110 CharSourceRange::getTokenRange(Right->getSourceRange()),
111 *Result.SourceManager, Result.Context->getLangOpts());
112
113 if (ResetMember->isArrow())
114 LeftText = "*" + LeftText;
115 if (ReleaseMember->isArrow())
116 RightText = "*" + RightText;
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000117 std::string DiagText;
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000118 // Even if x was rvalue, *x is not rvalue anymore.
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000119 if (!Right->isRValue() || ReleaseMember->isArrow()) {
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000120 RightText = "std::move(" + RightText + ")";
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000121 DiagText = "prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
122 } else {
123 DiagText =
124 "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
125 }
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000126 std::string NewText = LeftText + " = " + RightText;
127
Alexander Kornienkoed07a252015-03-05 13:53:21 +0000128 diag(ResetMember->getExprLoc(), DiagText)
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000129 << FixItHint::CreateReplacement(
130 CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
131}
132
Alexander Kornienko2b3124202015-03-02 12:25:03 +0000133} // namespace misc
Alexander Kornienkobc0c4232014-12-05 11:59:05 +0000134} // namespace tidy
135} // namespace clang