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