clang-tools 22.0.0git
AmbiguousSmartptrResetCallCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Lex/Lexer.h"
15
16using namespace clang::ast_matchers;
17
19
20namespace {
21
22AST_MATCHER(CXXMethodDecl, hasOnlyDefaultParameters) {
23 return llvm::all_of(Node.parameters(), [](const ParmVarDecl *Param) {
24 return Param->hasDefaultArg();
25 });
26}
27
28const auto DefaultSmartPointers = "::std::shared_ptr;::std::unique_ptr;"
29 "::boost::shared_ptr";
30} // namespace
31
33 StringRef Name, ClangTidyContext *Context)
34 : ClangTidyCheck(Name, Context),
35 SmartPointers(utils::options::parseStringList(
36 Options.get("SmartPointers", DefaultSmartPointers))) {}
37
40 Options.store(Opts, "SmartPointers",
42}
43
45 const auto IsSmartptr = hasAnyName(SmartPointers);
46
47 const auto ResetMethod =
48 cxxMethodDecl(hasName("reset"), hasOnlyDefaultParameters());
49
50 const auto TypeWithReset =
51 anyOf(cxxRecordDecl(
52 anyOf(hasMethod(ResetMethod),
53 isDerivedFrom(cxxRecordDecl(hasMethod(ResetMethod))))),
54 classTemplateSpecializationDecl(
55 hasSpecializedTemplate(classTemplateDecl(has(ResetMethod)))));
56
57 const auto SmartptrWithReset = expr(hasType(hasUnqualifiedDesugaredType(
58 recordType(hasDeclaration(classTemplateSpecializationDecl(
59 IsSmartptr,
60 hasTemplateArgument(
61 0, templateArgument(refersToType(hasUnqualifiedDesugaredType(
62 recordType(hasDeclaration(TypeWithReset))))))))))));
63
64 Finder->addMatcher(
65 cxxMemberCallExpr(
66 callee(ResetMethod),
67 unless(hasAnyArgument(expr(unless(cxxDefaultArgExpr())))),
68 anyOf(on(cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
69 hasArgument(0, SmartptrWithReset))
70 .bind("ArrowOp")),
71 on(SmartptrWithReset)))
72 .bind("MemberCall"),
73 this);
74}
75
77 const MatchFinder::MatchResult &Result) {
78 const auto *MemberCall =
79 Result.Nodes.getNodeAs<CXXMemberCallExpr>("MemberCall");
80 assert(MemberCall);
81
82 if (const auto *Arrow =
83 Result.Nodes.getNodeAs<CXXOperatorCallExpr>("ArrowOp")) {
84 const CharSourceRange SmartptrSourceRange =
85 Lexer::getAsCharRange(Arrow->getArg(0)->getSourceRange(),
86 *Result.SourceManager, getLangOpts());
87
88 diag(MemberCall->getBeginLoc(),
89 "ambiguous call to 'reset()' on a pointee of a smart pointer, prefer "
90 "more explicit approach");
91
92 diag(MemberCall->getBeginLoc(),
93 "consider dereferencing smart pointer to call 'reset' method "
94 "of the pointee here",
95 DiagnosticIDs::Note)
96 << FixItHint::CreateInsertion(SmartptrSourceRange.getBegin(), "(*")
97 << FixItHint::CreateInsertion(SmartptrSourceRange.getEnd(), ")")
98 << FixItHint::CreateReplacement(
99 CharSourceRange::getCharRange(
100 Arrow->getOperatorLoc(),
101 Arrow->getOperatorLoc().getLocWithOffset(2)),
102 ".");
103 } else {
104 const auto *Member = cast<MemberExpr>(MemberCall->getCallee());
105 assert(Member);
106
107 diag(MemberCall->getBeginLoc(),
108 "ambiguous call to 'reset()' on a smart pointer with pointee that "
109 "also has a 'reset()' method, prefer more explicit approach");
110
111 diag(MemberCall->getBeginLoc(),
112 "consider assigning the pointer to 'nullptr' here",
113 DiagnosticIDs::Note)
114 << FixItHint::CreateReplacement(
115 SourceRange(Member->getOperatorLoc(), Member->getOperatorLoc()),
116 " =")
117 << FixItHint::CreateReplacement(
118 SourceRange(Member->getMemberLoc(), MemberCall->getEndLoc()),
119 " nullptr");
120 }
121}
122
123} // namespace clang::tidy::readability
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap