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