xref: /llvm-project/clang-tools-extra/clang-tidy/readability/ContainerDataPointerCheck.cpp (revision d0d9e6f0849b2e76e980e2edf365302f47f4e35f)
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 ContainerDataPointerCheck::ContainerDataPointerCheck(StringRef Name,
20                                                      ClangTidyContext *Context)
21     : ClangTidyCheck(Name, Context) {}
22 
23 void ContainerDataPointerCheck::registerMatchers(MatchFinder *Finder) {
24   const auto Record =
25       cxxRecordDecl(
26           isSameOrDerivedFrom(
27               namedDecl(
28                   has(cxxMethodDecl(isPublic(), hasName("data")).bind("data")))
29                   .bind("container")))
30           .bind("record");
31 
32   const auto NonTemplateContainerType =
33       qualType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(Record))));
34   const auto TemplateContainerType =
35       qualType(hasUnqualifiedDesugaredType(templateSpecializationType(
36           hasDeclaration(classTemplateDecl(has(Record))))));
37 
38   const auto Container =
39       qualType(anyOf(NonTemplateContainerType, TemplateContainerType));
40 
41   Finder->addMatcher(
42       unaryOperator(
43           unless(isExpansionInSystemHeader()), hasOperatorName("&"),
44           hasUnaryOperand(anyOf(
45               ignoringParenImpCasts(
46                   cxxOperatorCallExpr(
47                       callee(cxxMethodDecl(hasName("operator[]"))
48                                  .bind("operator[]")),
49                       argumentCountIs(2),
50                       hasArgument(
51                           0,
52                           anyOf(ignoringParenImpCasts(
53                                     declRefExpr(
54                                         to(varDecl(anyOf(
55                                             hasType(Container),
56                                             hasType(references(Container))))))
57                                         .bind("var")),
58                                 ignoringParenImpCasts(hasDescendant(
59                                     declRefExpr(
60                                         to(varDecl(anyOf(
61                                             hasType(Container),
62                                             hasType(pointsTo(Container)),
63                                             hasType(references(Container))))))
64                                         .bind("var"))))),
65                       hasArgument(1,
66                                   ignoringParenImpCasts(
67                                       integerLiteral(equals(0)).bind("zero"))))
68                       .bind("operator-call")),
69               ignoringParenImpCasts(
70                   cxxMemberCallExpr(
71                       hasDescendant(
72                           declRefExpr(to(varDecl(anyOf(
73                                           hasType(Container),
74                                           hasType(references(Container))))))
75                               .bind("var")),
76                       argumentCountIs(1),
77                       hasArgument(0,
78                                   ignoringParenImpCasts(
79                                       integerLiteral(equals(0)).bind("zero"))))
80                       .bind("member-call")),
81               ignoringParenImpCasts(
82                   arraySubscriptExpr(
83                       hasLHS(ignoringParenImpCasts(
84                           declRefExpr(to(varDecl(anyOf(
85                                           hasType(Container),
86                                           hasType(references(Container))))))
87                               .bind("var"))),
88                       hasRHS(ignoringParenImpCasts(
89                           integerLiteral(equals(0)).bind("zero"))))
90                       .bind("array-subscript")))))
91           .bind("address-of"),
92       this);
93 }
94 
95 void ContainerDataPointerCheck::check(const MatchFinder::MatchResult &Result) {
96   const auto *UO = Result.Nodes.getNodeAs<UnaryOperator>("address-of");
97   const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("var");
98 
99   std::string ReplacementText;
100   ReplacementText = std::string(Lexer::getSourceText(
101       CharSourceRange::getTokenRange(DRE->getSourceRange()),
102       *Result.SourceManager, getLangOpts()));
103   if (DRE->getType()->isPointerType())
104     ReplacementText += "->data()";
105   else
106     ReplacementText += ".data()";
107 
108   FixItHint Hint =
109       FixItHint::CreateReplacement(UO->getSourceRange(), ReplacementText);
110   diag(UO->getBeginLoc(),
111        "'data' should be used for accessing the data pointer instead of taking "
112        "the address of the 0-th element")
113       << Hint;
114 }
115 } // namespace readability
116 } // namespace tidy
117 } // namespace clang
118