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