1 //===--- ContainerDataPointerCheck.cpp - clang-tidy -----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ContainerDataPointerCheck.h"
11 #include "../utils/Matchers.h"
12 #include "../utils/OptionsUtils.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/StringRef.h"
16 using namespace clang::ast_matchers
;
18 namespace clang::tidy::readability
{
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";
26 void ContainerDataPointerCheck::storeOptions(
27 ClangTidyOptions::OptionMap
&Opts
) {
28 Options
.store(Opts
, "IgnoredContainers",
29 utils::options::serializeStringList(IgnoredContainers
));
32 ContainerDataPointerCheck::ContainerDataPointerCheck(StringRef Name
,
33 ClangTidyContext
*Context
)
34 : ClangTidyCheck(Name
, Context
),
35 IgnoredContainers(utils::options::parseStringList(
36 Options
.get("IgnoredContainers", ""))) {}
38 void ContainerDataPointerCheck::registerMatchers(MatchFinder
*Finder
) {
41 unless(matchers::matchesAnyListedName(IgnoredContainers
)),
44 has(cxxMethodDecl(isPublic(), hasName("data")).bind("data")))
48 const auto NonTemplateContainerType
=
49 qualType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(Record
))));
50 const auto TemplateContainerType
=
51 qualType(hasUnqualifiedDesugaredType(templateSpecializationType(
52 hasDeclaration(classTemplateDecl(has(Record
))))));
54 const auto Container
=
55 qualType(anyOf(NonTemplateContainerType
, TemplateContainerType
));
57 const auto ContainerExpr
= anyOf(
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
));
72 const auto Zero
= integerLiteral(equals(0));
74 const auto SubscriptOperator
= callee(cxxMethodDecl(hasName("operator[]")));
78 unless(isExpansionInSystemHeader()), hasOperatorName("&"),
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
))))))
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
);
99 if (DCE
&& !CE
->getType()->isPointerType())
104 SourceRange SrcRange
= CE
->getSourceRange();
106 std::string ReplacementText
{
107 Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange
),
108 *Result
.SourceManager
, getLangOpts())};
110 if (!isa
<DeclRefExpr
, ArraySubscriptExpr
, CXXOperatorCallExpr
, CallExpr
,
112 ReplacementText
= "(" + ReplacementText
+ ")";
114 if (CE
->getType()->isPointerType())
115 ReplacementText
+= "->data()";
117 ReplacementText
+= ".data()";
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")
126 } // namespace clang::tidy::readability