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