1 //===--- ContainerDataPointerCheck.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 
9 #include "ContainerDataPointerCheck.h"
10 
11 #include "clang/Lex/Lexer.h"
12 #include "llvm/ADT/StringRef.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace readability {
19 
20 constexpr llvm::StringLiteral ContainerExprName = "container-expr";
21 constexpr llvm::StringLiteral DerefContainerExprName = "deref-container-expr";
22 constexpr llvm::StringLiteral AddrOfContainerExprName =
23     "addr-of-container-expr";
24 constexpr llvm::StringLiteral AddressOfName = "address-of";
25 
ContainerDataPointerCheck(StringRef Name,ClangTidyContext * Context)26 ContainerDataPointerCheck::ContainerDataPointerCheck(StringRef Name,
27                                                      ClangTidyContext *Context)
28     : ClangTidyCheck(Name, Context) {}
29 
registerMatchers(MatchFinder * Finder)30 void ContainerDataPointerCheck::registerMatchers(MatchFinder *Finder) {
31   const auto Record =
32       cxxRecordDecl(
33           isSameOrDerivedFrom(
34               namedDecl(
35                   has(cxxMethodDecl(isPublic(), hasName("data")).bind("data")))
36                   .bind("container")))
37           .bind("record");
38 
39   const auto NonTemplateContainerType =
40       qualType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(Record))));
41   const auto TemplateContainerType =
42       qualType(hasUnqualifiedDesugaredType(templateSpecializationType(
43           hasDeclaration(classTemplateDecl(has(Record))))));
44 
45   const auto Container =
46       qualType(anyOf(NonTemplateContainerType, TemplateContainerType));
47 
48   const auto ContainerExpr = anyOf(
49       unaryOperator(
50           hasOperatorName("*"),
51           hasUnaryOperand(
52               expr(hasType(pointsTo(Container))).bind(DerefContainerExprName)))
53           .bind(ContainerExprName),
54       unaryOperator(hasOperatorName("&"),
55                     hasUnaryOperand(expr(anyOf(hasType(Container),
56                                                hasType(references(Container))))
57                                         .bind(AddrOfContainerExprName)))
58           .bind(ContainerExprName),
59       expr(anyOf(hasType(Container), hasType(pointsTo(Container)),
60                  hasType(references(Container))))
61           .bind(ContainerExprName));
62 
63   const auto Zero = integerLiteral(equals(0));
64 
65   const auto SubscriptOperator = callee(cxxMethodDecl(hasName("operator[]")));
66 
67   Finder->addMatcher(
68       unaryOperator(
69           unless(isExpansionInSystemHeader()), hasOperatorName("&"),
70           hasUnaryOperand(expr(
71               anyOf(cxxOperatorCallExpr(SubscriptOperator, argumentCountIs(2),
72                                         hasArgument(0, ContainerExpr),
73                                         hasArgument(1, Zero)),
74                     cxxMemberCallExpr(SubscriptOperator, on(ContainerExpr),
75                                       argumentCountIs(1), hasArgument(0, Zero)),
76                     arraySubscriptExpr(hasLHS(ContainerExpr), hasRHS(Zero))))))
77           .bind(AddressOfName),
78       this);
79 }
80 
check(const MatchFinder::MatchResult & Result)81 void ContainerDataPointerCheck::check(const MatchFinder::MatchResult &Result) {
82   const auto *UO = Result.Nodes.getNodeAs<UnaryOperator>(AddressOfName);
83   const auto *CE = Result.Nodes.getNodeAs<Expr>(ContainerExprName);
84   const auto *DCE = Result.Nodes.getNodeAs<Expr>(DerefContainerExprName);
85   const auto *ACE = Result.Nodes.getNodeAs<Expr>(AddrOfContainerExprName);
86 
87   if (!UO || !CE)
88     return;
89 
90   if (DCE && !CE->getType()->isPointerType())
91     CE = DCE;
92   else if (ACE)
93     CE = ACE;
94 
95   SourceRange SrcRange = CE->getSourceRange();
96 
97   std::string ReplacementText{
98       Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange),
99                            *Result.SourceManager, getLangOpts())};
100 
101   if (!isa<DeclRefExpr, ArraySubscriptExpr, CXXOperatorCallExpr, CallExpr,
102            MemberExpr>(CE))
103     ReplacementText = "(" + ReplacementText + ")";
104 
105   if (CE->getType()->isPointerType())
106     ReplacementText += "->data()";
107   else
108     ReplacementText += ".data()";
109 
110   FixItHint Hint =
111       FixItHint::CreateReplacement(UO->getSourceRange(), ReplacementText);
112   diag(UO->getBeginLoc(),
113        "'data' should be used for accessing the data pointer instead of taking "
114        "the address of the 0-th element")
115       << Hint;
116 }
117 } // namespace readability
118 } // namespace tidy
119 } // namespace clang
120