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