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