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 | |
20 | void UniqueptrDeleteReleaseCheck::storeOptions( |
21 | ClangTidyOptions::OptionMap &Opts) { |
22 | Options.store(Options&: Opts, LocalName: "PreferResetCall" , Value: PreferResetCall); |
23 | } |
24 | |
25 | UniqueptrDeleteReleaseCheck::UniqueptrDeleteReleaseCheck( |
26 | StringRef Name, ClangTidyContext *Context) |
27 | : ClangTidyCheck(Name, Context), |
28 | PreferResetCall(Options.get(LocalName: "PreferResetCall" , Default: false)) {} |
29 | |
30 | void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) { |
31 | |
32 | auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl( |
33 | hasName(Name: "::std::unique_ptr" ), |
34 | hasTemplateArgument(N: 1, InnerMatcher: refersToType(InnerMatcher: hasDeclaration(InnerMatcher: cxxRecordDecl( |
35 | hasName(Name: "::std::default_delete" )))))); |
36 | |
37 | Finder->addMatcher( |
38 | NodeMatch: cxxDeleteExpr( |
39 | unless(isInTemplateInstantiation()), |
40 | has(cxxMemberCallExpr( |
41 | callee(InnerMatcher: memberExpr(hasObjectExpression(InnerMatcher: anyOf( |
42 | hasType(InnerMatcher: UniquePtrWithDefaultDelete), |
43 | hasType(InnerMatcher: pointsTo( |
44 | InnerMatcher: UniquePtrWithDefaultDelete)))), |
45 | member(InnerMatcher: cxxMethodDecl(hasName(Name: "release" )))) |
46 | .bind(ID: "release_expr" ))) |
47 | .bind(ID: "release_call" ))) |
48 | .bind(ID: "delete" ), |
49 | Action: this); |
50 | } |
51 | |
52 | void UniqueptrDeleteReleaseCheck::check( |
53 | const MatchFinder::MatchResult &Result) { |
54 | const auto *DeleteExpr = Result.Nodes.getNodeAs<CXXDeleteExpr>(ID: "delete" ); |
55 | const auto *ReleaseExpr = Result.Nodes.getNodeAs<MemberExpr>(ID: "release_expr" ); |
56 | const auto *ReleaseCallExpr = |
57 | Result.Nodes.getNodeAs<CXXMemberCallExpr>(ID: "release_call" ); |
58 | |
59 | if (ReleaseExpr->getBeginLoc().isMacroID()) |
60 | return; |
61 | |
62 | auto D = |
63 | diag(Loc: DeleteExpr->getBeginLoc(), Description: "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(RemoveRange: ReleaseExpr->getMemberLoc(), Code: "reset" ); |
71 | } else { |
72 | if (ReleaseExpr->isArrow()) |
73 | D << FixItHint::CreateInsertion(InsertionLoc: ReleaseExpr->getBase()->getBeginLoc(), |
74 | Code: "*" ); |
75 | D << FixItHint::CreateReplacement( |
76 | CharSourceRange::getTokenRange(ReleaseExpr->getOperatorLoc(), |
77 | ReleaseCallExpr->getEndLoc()), |
78 | " = nullptr" ); |
79 | } |
80 | } |
81 | |
82 | } // namespace clang::tidy::readability |
83 | |