blob: d48d74c1125e34bf076ed416fa91b659977e15a2 [file] [log] [blame]
Samuel Benzaquen3a571012014-03-27 17:42:26 +00001//===--- RedundantSmartptrGet.cpp - clang-tidy ----------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "RedundantSmartptrGet.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
18
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000019namespace {
20internal::Matcher<Expr> callToGet(internal::Matcher<Decl> OnClass) {
21 return memberCallExpr(
22 on(expr(anyOf(hasType(OnClass),
23 hasType(qualType(pointsTo(decl(OnClass).bind(
24 "ptr_to_ptr")))))).bind("smart_pointer")),
Samuel Benzaquen20e93f32014-04-29 13:41:23 +000025 unless(callee(memberExpr(hasObjectExpression(thisExpr())))),
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000026 callee(methodDecl(hasName("get")))).bind("redundant_get");
27}
28
29void registerMatchersForGetArrowStart(MatchFinder *Finder,
30 MatchFinder::MatchCallback *Callback) {
Samuel Benzaquen3a571012014-03-27 17:42:26 +000031 const auto QuacksLikeASmartptr = recordDecl(
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000032 recordDecl().bind("duck_typing"),
Samuel Benzaquen3a571012014-03-27 17:42:26 +000033 has(methodDecl(hasName("operator->"),
34 returns(qualType(pointsTo(type().bind("op->Type")))))),
35 has(methodDecl(hasName("operator*"),
36 returns(qualType(references(type().bind("op*Type")))))),
37 has(methodDecl(hasName("get"),
38 returns(qualType(pointsTo(type().bind("getType")))))));
39
Samuel Benzaquen3a571012014-03-27 17:42:26 +000040 // Catch 'ptr.get()->Foo()'
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000041 Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(),
42 hasObjectExpression(ignoringImpCasts(
43 callToGet(QuacksLikeASmartptr)))),
44 Callback);
Samuel Benzaquen3a571012014-03-27 17:42:26 +000045
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000046 // Catch '*ptr.get()' or '*ptr->get()'
Samuel Benzaquen3a571012014-03-27 17:42:26 +000047 Finder->addMatcher(
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000048 unaryOperator(hasOperatorName("*"),
49 hasUnaryOperand(callToGet(QuacksLikeASmartptr))),
50 Callback);
51}
Samuel Benzaquen3a571012014-03-27 17:42:26 +000052
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000053void registerMatchersForGetEquals(MatchFinder *Finder,
54 MatchFinder::MatchCallback *Callback) {
55 // This one is harder to do with duck typing.
56 // The operator==/!= that we are looking for might be member or non-member,
57 // might be on global namespace or found by ADL, might be a template, etc.
58 // For now, lets keep a list of known standard types.
59
60 const auto IsAKnownSmartptr = recordDecl(
61 anyOf(hasName("::std::unique_ptr"), hasName("::std::shared_ptr")));
62
63 // Matches against nullptr.
Samuel Benzaquen3a571012014-03-27 17:42:26 +000064 Finder->addMatcher(
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000065 binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
66 hasEitherOperand(ignoringImpCasts(nullPtrLiteralExpr())),
67 hasEitherOperand(callToGet(IsAKnownSmartptr))),
68 Callback);
69 // TODO: Catch ptr.get() == other_ptr.get()
70}
71
72
73} // namespace
74
75void RedundantSmartptrGet::registerMatchers(MatchFinder *Finder) {
76 registerMatchersForGetArrowStart(Finder, this);
77 registerMatchersForGetEquals(Finder, this);
Samuel Benzaquen3a571012014-03-27 17:42:26 +000078}
79
80namespace {
81bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +000082 if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
83 return true;
Samuel Benzaquen3a571012014-03-27 17:42:26 +000084 // Verify that the types match.
85 // We can't do this on the matcher because the type nodes can be different,
86 // even though they represent the same type. This difference comes from how
87 // the type is referenced (eg. through a typedef, a type trait, etc).
88 const Type *OpArrowType =
89 Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
90 const Type *OpStarType =
91 Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
92 const Type *GetType =
93 Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
94 return OpArrowType == OpStarType && OpArrowType == GetType;
95}
96} // namespace
97
98void RedundantSmartptrGet::check(const MatchFinder::MatchResult &Result) {
99 if (!allReturnTypesMatch(Result)) return;
100
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +0000101 bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
102 bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
Samuel Benzaquen3a571012014-03-27 17:42:26 +0000103 const Expr *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
104 const Expr *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
105
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +0000106 if (IsPtrToPtr && IsMemberExpr) {
107 // Ignore this case (eg. Foo->get()->DoSomething());
108 return;
109 }
110
Samuel Benzaquen3a571012014-03-27 17:42:26 +0000111 StringRef SmartptrText = Lexer::getSourceText(
112 CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
113 *Result.SourceManager, LangOptions());
Samuel Benzaquen110f3cc2014-04-09 14:17:23 +0000114 // Replace foo->get() with *foo, and foo.get() with foo.
Samuel Benzaquen3a571012014-03-27 17:42:26 +0000115 std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
116 diag(GetCall->getLocStart(), "Redundant get() call on smart pointer.")
117 << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
118}
119
120} // namespace tidy
121} // namespace clang