brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · 19b4fc1 Raw
161 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 "OptionalValueConversionCheck.h"10#include "../utils/LexerUtils.h"11#include "../utils/Matchers.h"12#include "../utils/OptionsUtils.h"13#include "clang/AST/ASTContext.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include "clang/ASTMatchers/ASTMatchers.h"16#include <array>17 18using namespace clang::ast_matchers;19using clang::ast_matchers::internal::Matcher;20 21namespace clang::tidy::bugprone {22 23namespace {24 25AST_MATCHER_P(QualType, hasCleanType, Matcher<QualType>, InnerMatcher) {26  return InnerMatcher.matches(27      Node.getNonReferenceType().getUnqualifiedType().getCanonicalType(),28      Finder, Builder);29}30 31constexpr std::array<StringRef, 2> MakeSmartPtrList{32    "::std::make_unique",33    "::std::make_shared",34};35constexpr StringRef MakeOptional = "::std::make_optional";36 37} // namespace38 39OptionalValueConversionCheck::OptionalValueConversionCheck(40    StringRef Name, ClangTidyContext *Context)41    : ClangTidyCheck(Name, Context),42      OptionalTypes(utils::options::parseStringList(43          Options.get("OptionalTypes",44                      "::std::optional;::absl::optional;::boost::optional"))),45      ValueMethods(utils::options::parseStringList(46          Options.get("ValueMethods", "::value$;::get$"))) {}47 48std::optional<TraversalKind>49OptionalValueConversionCheck::getCheckTraversalKind() const {50  return TK_AsIs;51}52 53void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {54  auto BindOptionalType = qualType(55      hasCleanType(qualType(hasDeclaration(namedDecl(56                                matchers::matchesAnyListedName(OptionalTypes))))57                       .bind("optional-type")));58 59  auto EqualsBoundOptionalType =60      qualType(hasCleanType(equalsBoundNode("optional-type")));61 62  auto OptionalDerefMatcherImpl = callExpr(63      anyOf(64          cxxOperatorCallExpr(hasOverloadedOperatorName("*"),65                              hasUnaryOperand(hasType(EqualsBoundOptionalType)))66              .bind("op-call"),67          cxxMemberCallExpr(thisPointerType(EqualsBoundOptionalType),68                            callee(cxxMethodDecl(anyOf(69                                hasOverloadedOperatorName("*"),70                                matchers::matchesAnyListedName(ValueMethods)))))71              .bind("member-call")),72      hasType(qualType().bind("value-type")));73 74  auto StdMoveCallMatcher =75      callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))),76               hasArgument(0, ignoringImpCasts(OptionalDerefMatcherImpl)));77  auto OptionalDerefMatcher =78      ignoringImpCasts(anyOf(OptionalDerefMatcherImpl, StdMoveCallMatcher));79 80  Finder->addMatcher(81      expr(anyOf(82               // construct optional83               cxxConstructExpr(argumentCountIs(1), hasType(BindOptionalType),84                                hasArgument(0, OptionalDerefMatcher)),85               // known template methods in std86               callExpr(87                   argumentCountIs(1),88                   anyOf(89                       // match std::make_unique std::make_shared90                       callee(functionDecl(91                           matchers::matchesAnyListedName(MakeSmartPtrList),92                           hasTemplateArgument(93                               0, refersToType(BindOptionalType)))),94                       // match first std::make_optional by limit argument count95                       // (1) and template count (1).96                       // 1. template< class T > constexpr97                       //    std::optional<decay_t<T>> make_optional(T&& value);98                       // 2. template< class T, class... Args > constexpr99                       //    std::optional<T> make_optional(Args&&... args);100                       callee(functionDecl(templateArgumentCountIs(1),101                                           hasName(MakeOptional),102                                           returns(BindOptionalType)))),103                   hasArgument(0, OptionalDerefMatcher)),104               callExpr(argumentCountIs(1),105 106                        hasArgument(0, OptionalDerefMatcher))),107           unless(anyOf(hasAncestor(typeLoc()),108                        hasAncestor(expr(matchers::hasUnevaluatedContext())))))109          .bind("expr"),110      this);111}112 113void OptionalValueConversionCheck::storeOptions(114    ClangTidyOptions::OptionMap &Opts) {115  Options.store(Opts, "OptionalTypes",116                utils::options::serializeStringList(OptionalTypes));117  Options.store(Opts, "ValueMethods",118                utils::options::serializeStringList(ValueMethods));119}120 121void OptionalValueConversionCheck::check(122    const MatchFinder::MatchResult &Result) {123  const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>("expr");124  const auto *OptionalType = Result.Nodes.getNodeAs<QualType>("optional-type");125  const auto *ValueType = Result.Nodes.getNodeAs<QualType>("value-type");126 127  diag(MatchedExpr->getExprLoc(),128       "conversion from %0 into %1 and back into %0, remove potentially "129       "error-prone optional dereference")130      << *OptionalType << ValueType->getUnqualifiedType();131 132  if (const auto *OperatorExpr =133          Result.Nodes.getNodeAs<CXXOperatorCallExpr>("op-call")) {134    diag(OperatorExpr->getExprLoc(), "remove '*' to silence this warning",135         DiagnosticIDs::Note)136        << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(137               OperatorExpr->getBeginLoc(), OperatorExpr->getExprLoc()));138    return;139  }140  if (const auto *CallExpr =141          Result.Nodes.getNodeAs<CXXMemberCallExpr>("member-call")) {142    const SourceLocation Begin =143        utils::lexer::getPreviousToken(CallExpr->getExprLoc(),144                                       *Result.SourceManager, getLangOpts())145            .getLocation();146    auto Diag =147        diag(CallExpr->getExprLoc(),148             "remove call to %0 to silence this warning", DiagnosticIDs::Note);149    Diag << CallExpr->getMethodDecl()150         << FixItHint::CreateRemoval(151                CharSourceRange::getTokenRange(Begin, CallExpr->getEndLoc()));152    if (const auto *Member =153            llvm::dyn_cast<MemberExpr>(CallExpr->getCallee()->IgnoreImplicit());154        Member && Member->isArrow())155      Diag << FixItHint::CreateInsertion(CallExpr->getBeginLoc(), "*");156    return;157  }158}159 160} // namespace clang::tidy::bugprone161