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