clang-tools  11.0.0
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 cxxMemberCallExpr(
22  on(expr(anyOf(hasType(OnClass),
23  hasType(qualType(
24  pointsTo(decl(OnClass).bind("ptr_to_ptr"))))))
25  .bind("smart_pointer")),
26  unless(callee(memberExpr(hasObjectExpression(cxxThisExpr())))),
27  callee(cxxMethodDecl(
28  hasName("get"),
29  returns(qualType(pointsTo(type().bind("getType")))))))
30  .bind("redundant_get");
31 }
32 
33 internal::Matcher<Decl> knownSmartptr() {
34  return recordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));
35 }
36 
37 void registerMatchersForGetArrowStart(MatchFinder *Finder,
38  MatchFinder::MatchCallback *Callback) {
39  const auto QuacksLikeASmartptr = recordDecl(
40  recordDecl().bind("duck_typing"),
41  has(cxxMethodDecl(hasName("operator->"),
42  returns(qualType(pointsTo(type().bind("op->Type")))))),
43  has(cxxMethodDecl(hasName("operator*"), returns(qualType(references(
44  type().bind("op*Type")))))));
45 
46  // Make sure we are not missing the known standard types.
47  const auto Smartptr = anyOf(knownSmartptr(), QuacksLikeASmartptr);
48 
49  // Catch 'ptr.get()->Foo()'
50  Finder->addMatcher(
51  memberExpr(expr().bind("memberExpr"), isArrow(),
52  hasObjectExpression(ignoringImpCasts(callToGet(Smartptr)))),
53  Callback);
54 
55  // Catch '*ptr.get()' or '*ptr->get()'
56  Finder->addMatcher(
57  unaryOperator(hasOperatorName("*"), hasUnaryOperand(callToGet(Smartptr))),
58  Callback);
59 
60  // Catch '!ptr.get()'
61  const auto CallToGetAsBool = ignoringParenImpCasts(callToGet(
62  recordDecl(Smartptr, has(cxxConversionDecl(returns(booleanType()))))));
63  Finder->addMatcher(
64  unaryOperator(hasOperatorName("!"), hasUnaryOperand(CallToGetAsBool)),
65  Callback);
66 
67  // Catch 'if(ptr.get())'
68  Finder->addMatcher(ifStmt(hasCondition(CallToGetAsBool)), Callback);
69 
70  // Catch 'ptr.get() ? X : Y'
71  Finder->addMatcher(conditionalOperator(hasCondition(CallToGetAsBool)),
72  Callback);
73 }
74 
75 void registerMatchersForGetEquals(MatchFinder *Finder,
76  MatchFinder::MatchCallback *Callback) {
77  // This one is harder to do with duck typing.
78  // The operator==/!= that we are looking for might be member or non-member,
79  // might be on global namespace or found by ADL, might be a template, etc.
80  // For now, lets keep it to the known standard types.
81 
82  // Matches against nullptr.
83  Finder->addMatcher(
84  binaryOperator(hasAnyOperatorName("==", "!="),
85  hasOperands(ignoringImpCasts(anyOf(
86  cxxNullPtrLiteralExpr(), gnuNullExpr(),
87  integerLiteral(equals(0)))),
88  callToGet(knownSmartptr()))),
89  Callback);
90 
91  // FIXME: Match and fix if (l.get() == r.get()).
92 }
93 
94 } // namespace
95 
96 void RedundantSmartptrGetCheck::storeOptions(
98  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
99 }
100 
101 void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {
102  registerMatchersForGetArrowStart(Finder, this);
103  registerMatchersForGetEquals(Finder, this);
104 }
105 
106 namespace {
107 bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
108  if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
109  return true;
110  // Verify that the types match.
111  // We can't do this on the matcher because the type nodes can be different,
112  // even though they represent the same type. This difference comes from how
113  // the type is referenced (eg. through a typedef, a type trait, etc).
114  const Type *OpArrowType =
115  Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
116  const Type *OpStarType =
117  Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
118  const Type *GetType =
119  Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
120  return OpArrowType == OpStarType && OpArrowType == GetType;
121 }
122 } // namespace
123 
124 void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {
125  if (!allReturnTypesMatch(Result))
126  return;
127 
128  bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
129  bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
130  const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
131  if (GetCall->getBeginLoc().isMacroID() && IgnoreMacros)
132  return;
133 
134  const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
135 
136  if (IsPtrToPtr && IsMemberExpr) {
137  // Ignore this case (eg. Foo->get()->DoSomething());
138  return;
139  }
140 
141  StringRef SmartptrText = Lexer::getSourceText(
142  CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
143  *Result.SourceManager, getLangOpts());
144  // Replace foo->get() with *foo, and foo.get() with foo.
145  std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
146  diag(GetCall->getBeginLoc(), "redundant get() call on smart pointer")
147  << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
148 }
149 
150 } // namespace readability
151 } // namespace tidy
152 } // namespace clang
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::ast_matchers
Definition: AbseilMatcher.h:14
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
RedundantSmartptrGetCheck.h
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
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111