xref: /llvm-project/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1 //===--- UniqueptrDeleteReleaseCheck.cpp - clang-tidy----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "UniqueptrDeleteReleaseCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Basic/Diagnostic.h"
13 #include "clang/Basic/SourceLocation.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang::tidy::readability {
19 
storeOptions(ClangTidyOptions::OptionMap & Opts)20 void UniqueptrDeleteReleaseCheck::storeOptions(
21     ClangTidyOptions::OptionMap &Opts) {
22   Options.store(Opts, "PreferResetCall", PreferResetCall);
23 }
24 
UniqueptrDeleteReleaseCheck(StringRef Name,ClangTidyContext * Context)25 UniqueptrDeleteReleaseCheck::UniqueptrDeleteReleaseCheck(
26     StringRef Name, ClangTidyContext *Context)
27     : ClangTidyCheck(Name, Context),
28       PreferResetCall(Options.get("PreferResetCall", false)) {}
29 
registerMatchers(MatchFinder * Finder)30 void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) {
31 
32   auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl(
33       hasName("::std::unique_ptr"),
34       hasTemplateArgument(1, refersToType(hasDeclaration(cxxRecordDecl(
35                                  hasName("::std::default_delete"))))));
36 
37   Finder->addMatcher(
38       cxxDeleteExpr(
39           unless(isInTemplateInstantiation()),
40           has(cxxMemberCallExpr(
41                   callee(memberExpr(hasObjectExpression(anyOf(
42                                         hasType(UniquePtrWithDefaultDelete),
43                                         hasType(pointsTo(
44                                             UniquePtrWithDefaultDelete)))),
45                                     member(cxxMethodDecl(hasName("release"))))
46                              .bind("release_expr")))
47                   .bind("release_call")))
48           .bind("delete"),
49       this);
50 }
51 
check(const MatchFinder::MatchResult & Result)52 void UniqueptrDeleteReleaseCheck::check(
53     const MatchFinder::MatchResult &Result) {
54   const auto *DeleteExpr = Result.Nodes.getNodeAs<CXXDeleteExpr>("delete");
55   const auto *ReleaseExpr = Result.Nodes.getNodeAs<MemberExpr>("release_expr");
56   const auto *ReleaseCallExpr =
57       Result.Nodes.getNodeAs<CXXMemberCallExpr>("release_call");
58 
59   if (ReleaseExpr->getBeginLoc().isMacroID())
60     return;
61 
62   auto D =
63       diag(DeleteExpr->getBeginLoc(), "prefer '%select{= nullptr|reset()}0' "
64                                       "to reset 'unique_ptr<>' objects");
65   D << PreferResetCall << DeleteExpr->getSourceRange()
66     << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
67            DeleteExpr->getBeginLoc(),
68            DeleteExpr->getArgument()->getBeginLoc()));
69   if (PreferResetCall) {
70     D << FixItHint::CreateReplacement(ReleaseExpr->getMemberLoc(), "reset");
71   } else {
72     if (ReleaseExpr->isArrow())
73       D << FixItHint::CreateInsertion(ReleaseExpr->getBase()->getBeginLoc(),
74                                       "*");
75     D << FixItHint::CreateReplacement(
76         CharSourceRange::getTokenRange(ReleaseExpr->getOperatorLoc(),
77                                        ReleaseCallExpr->getEndLoc()),
78         " = nullptr");
79   }
80 }
81 
82 } // namespace clang::tidy::readability
83