clang-tools  9.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(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
85  hasEitherOperand(ignoringImpCasts(
86  anyOf(cxxNullPtrLiteralExpr(), gnuNullExpr(),
87  integerLiteral(equals(0))))),
88  hasEitherOperand(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  // Only register the matchers for C++; the functionality currently does not
103  // provide any benefit to other languages, despite being benign.
104  if (!getLangOpts().CPlusPlus)
105  return;
106 
107  registerMatchersForGetArrowStart(Finder, this);
108  registerMatchersForGetEquals(Finder, this);
109 }
110 
111 namespace {
112 bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
113  if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
114  return true;
115  // Verify that the types match.
116  // We can't do this on the matcher because the type nodes can be different,
117  // even though they represent the same type. This difference comes from how
118  // the type is referenced (eg. through a typedef, a type trait, etc).
119  const Type *OpArrowType =
120  Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
121  const Type *OpStarType =
122  Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
123  const Type *GetType =
124  Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
125  return OpArrowType == OpStarType && OpArrowType == GetType;
126 }
127 } // namespace
128 
129 void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {
130  if (!allReturnTypesMatch(Result))
131  return;
132 
133  bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
134  bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
135  const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
136  if (GetCall->getBeginLoc().isMacroID() && IgnoreMacros)
137  return;
138 
139  const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
140 
141  if (IsPtrToPtr && IsMemberExpr) {
142  // Ignore this case (eg. Foo->get()->DoSomething());
143  return;
144  }
145 
146  StringRef SmartptrText = Lexer::getSourceText(
147  CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
148  *Result.SourceManager, getLangOpts());
149  // Replace foo->get() with *foo, and foo.get() with foo.
150  std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
151  diag(GetCall->getBeginLoc(), "redundant get() call on smart pointer")
152  << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
153 }
154 
155 } // namespace readability
156 } // namespace tidy
157 } // namespace clang
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:203
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
NodeType Type