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

source code of clang-tools-extra/clang-tidy/readability/ContainerDataPointerCheck.cpp