brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.9 KiB · e308aef Raw
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