clang-tools  14.0.0git
RedundantSmartptrGetCheck.cpp
Go to the documentation of this file.
1 //===--- RedundantSmartptrGetCheck.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 
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/Lex/Lexer.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace readability {
18 
19 namespace {
20 internal::Matcher<Expr> callToGet(const internal::Matcher<Decl> &OnClass) {
21  return expr(
22  anyOf(cxxMemberCallExpr(
23  on(expr(anyOf(hasType(OnClass),
24  hasType(qualType(pointsTo(
25  decl(OnClass).bind("ptr_to_ptr"))))))
26  .bind("smart_pointer")),
27  unless(callee(
28  memberExpr(hasObjectExpression(cxxThisExpr())))),
29  callee(cxxMethodDecl(hasName("get"),
30  returns(qualType(pointsTo(
31  type().bind("getType"))))))),
32  cxxDependentScopeMemberExpr(
33  hasMemberName("get"),
34  hasObjectExpression(
35  expr(hasType(qualType(hasCanonicalType(
36  templateSpecializationType(hasDeclaration(
37  classTemplateDecl(has(cxxRecordDecl(
38  OnClass,
39  hasMethod(cxxMethodDecl(
40  hasName("get"),
41  returns(qualType(
42  pointsTo(type().bind(
43  "getType")))))))))))))))
44  .bind("smart_pointer")))))
45  .bind("redundant_get");
46 }
47 
48 internal::Matcher<Decl> knownSmartptr() {
49  return recordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));
50 }
51 
52 void registerMatchersForGetArrowStart(MatchFinder *Finder,
53  MatchFinder::MatchCallback *Callback) {
54  const auto QuacksLikeASmartptr = recordDecl(
55  recordDecl().bind("duck_typing"),
56  has(cxxMethodDecl(hasName("operator->"),
57  returns(qualType(pointsTo(type().bind("op->Type")))))),
58  has(cxxMethodDecl(hasName("operator*"), returns(qualType(references(
59  type().bind("op*Type")))))));
60 
61  // Make sure we are not missing the known standard types.
62  const auto Smartptr = anyOf(knownSmartptr(), QuacksLikeASmartptr);
63 
64  // Catch 'ptr.get()->Foo()'
65  Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(),
66  hasObjectExpression(callToGet(Smartptr))),
67  Callback);
68 
69  // Catch '*ptr.get()' or '*ptr->get()'
70  Finder->addMatcher(
71  unaryOperator(hasOperatorName("*"), hasUnaryOperand(callToGet(Smartptr))),
72  Callback);
73 
74  // Catch '!ptr.get()'
75  const auto CallToGetAsBool = callToGet(
76  recordDecl(Smartptr, has(cxxConversionDecl(returns(booleanType())))));
77  Finder->addMatcher(
78  unaryOperator(hasOperatorName("!"), hasUnaryOperand(CallToGetAsBool)),
79  Callback);
80 
81  // Catch 'if(ptr.get())'
82  Finder->addMatcher(ifStmt(hasCondition(CallToGetAsBool)), Callback);
83 
84  // Catch 'ptr.get() ? X : Y'
85  Finder->addMatcher(conditionalOperator(hasCondition(CallToGetAsBool)),
86  Callback);
87 
88  Finder->addMatcher(cxxDependentScopeMemberExpr(hasObjectExpression(
89  callExpr(has(callToGet(Smartptr))).bind("obj"))),
90  Callback);
91 }
92 
93 void registerMatchersForGetEquals(MatchFinder *Finder,
94  MatchFinder::MatchCallback *Callback) {
95  // This one is harder to do with duck typing.
96  // The operator==/!= that we are looking for might be member or non-member,
97  // might be on global namespace or found by ADL, might be a template, etc.
98  // For now, lets keep it to the known standard types.
99 
100  // Matches against nullptr.
101  Finder->addMatcher(
102  binaryOperator(hasAnyOperatorName("==", "!="),
103  hasOperands(anyOf(cxxNullPtrLiteralExpr(), gnuNullExpr(),
104  integerLiteral(equals(0))),
105  callToGet(knownSmartptr()))),
106  Callback);
107 
108  // FIXME: Match and fix if (l.get() == r.get()).
109 }
110 
111 } // namespace
112 
113 void RedundantSmartptrGetCheck::storeOptions(
115  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
116 }
117 
118 void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {
119  registerMatchersForGetArrowStart(Finder, this);
120  registerMatchersForGetEquals(Finder, this);
121 }
122 
123 namespace {
124 bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
125  if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
126  return true;
127  // Verify that the types match.
128  // We can't do this on the matcher because the type nodes can be different,
129  // even though they represent the same type. This difference comes from how
130  // the type is referenced (eg. through a typedef, a type trait, etc).
131  const Type *OpArrowType =
132  Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
133  const Type *OpStarType =
134  Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
135  const Type *GetType =
136  Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
137  return OpArrowType == OpStarType && OpArrowType == GetType;
138 }
139 } // namespace
140 
141 void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {
142  if (!allReturnTypesMatch(Result))
143  return;
144 
145  bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
146  bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
147  const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
148  if (GetCall->getBeginLoc().isMacroID() && IgnoreMacros)
149  return;
150 
151  const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
152 
153  if (IsPtrToPtr && IsMemberExpr) {
154  // Ignore this case (eg. Foo->get()->DoSomething());
155  return;
156  }
157 
158  auto SR = GetCall->getSourceRange();
159  // CXXDependentScopeMemberExpr source range does not include parens
160  // Extend the source range of the get call to account for them.
161  if (isa<CXXDependentScopeMemberExpr>(GetCall))
162  SR.setEnd(Lexer::getLocForEndOfToken(SR.getEnd(), 0, *Result.SourceManager,
163  getLangOpts())
164  .getLocWithOffset(1));
165 
166  StringRef SmartptrText = Lexer::getSourceText(
167  CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
168  *Result.SourceManager, getLangOpts());
169  // Replace foo->get() with *foo, and foo.get() with foo.
170  std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
171  diag(GetCall->getBeginLoc(), "redundant get() call on smart pointer")
172  << FixItHint::CreateReplacement(SR, Replacement);
173 }
174 
175 } // namespace readability
176 } // namespace tidy
177 } // namespace clang
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:112
clang::ast_matchers
Definition: AbseilMatcher.h:14
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
RedundantSmartptrGetCheck.h
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:258
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Callback
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28