brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.3 KiB · 28bfe57 Raw
138 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 "RvalueReferenceParamNotMovedCheck.h"10#include "../utils/Matchers.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::cppcoreguidelines {17 18using matchers::hasUnevaluatedContext;19 20namespace {21AST_MATCHER_P(LambdaExpr, valueCapturesVar, DeclarationMatcher, VarMatcher) {22  return std::find_if(Node.capture_begin(), Node.capture_end(),23                      [&](const LambdaCapture &Capture) {24                        return Capture.capturesVariable() &&25                               VarMatcher.matches(*Capture.getCapturedVar(),26                                                  Finder, Builder) &&27                               Capture.getCaptureKind() == LCK_ByCopy;28                      }) != Node.capture_end();29}30AST_MATCHER_P2(Stmt, argumentOf, bool, AllowPartialMove, StatementMatcher,31               Ref) {32  if (AllowPartialMove) {33    return stmt(anyOf(Ref, hasDescendant(Ref))).matches(Node, Finder, Builder);34  }35  return Ref.matches(Node, Finder, Builder);36}37} // namespace38 39void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {40  auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));41 42  const StatementMatcher MoveCallMatcher =43      callExpr(44          argumentCountIs(1),45          anyOf(callee(functionDecl(hasName(MoveFunction))),46                callee(unresolvedLookupExpr(hasAnyDeclaration(47                    namedDecl(hasUnderlyingDecl(hasName(MoveFunction))))))),48          hasArgument(49              0, argumentOf(50                     AllowPartialMove,51                     declRefExpr(to(equalsBoundNode("param"))).bind("ref"))),52          unless(hasAncestor(53              lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))),54          unless(anyOf(hasAncestor(typeLoc()),55                       hasAncestor(expr(hasUnevaluatedContext())))))56          .bind("move-call");57 58  Finder->addMatcher(59      parmVarDecl(60          hasType(type(rValueReferenceType())), parmVarDecl().bind("param"),61          unless(hasType(references(qualType(62              anyOf(isConstQualified(), substTemplateTypeParmType()))))),63          optionally(hasType(qualType(references(templateTypeParmType(64              hasDeclaration(templateTypeParmDecl().bind("template-type"))))))),65          hasDeclContext(66              functionDecl(67                  isDefinition(), unless(isDeleted()), unless(isDefaulted()),68                  unless(cxxConstructorDecl(isMoveConstructor())),69                  unless(cxxMethodDecl(isMoveAssignmentOperator())), ToParam,70                  anyOf(cxxConstructorDecl(71                            optionally(hasDescendant(MoveCallMatcher))),72                        functionDecl(unless(cxxConstructorDecl()),73                                     optionally(hasBody(74                                         hasDescendant(MoveCallMatcher))))))75                  .bind("func"))),76      this);77}78 79void RvalueReferenceParamNotMovedCheck::check(80    const MatchFinder::MatchResult &Result) {81  const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");82  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("func");83  const auto *TemplateType =84      Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type");85 86  if (!Param || !Function)87    return;88 89  if (IgnoreUnnamedParams && Param->getName().empty())90    return;91 92  if (!Param->isUsed() && Param->hasAttr<UnusedAttr>())93    return;94 95  if (IgnoreNonDeducedTemplateTypes && TemplateType)96    return;97 98  if (TemplateType) {99    if (const FunctionTemplateDecl *FuncTemplate =100            Function->getDescribedFunctionTemplate()) {101      const TemplateParameterList *Params =102          FuncTemplate->getTemplateParameters();103      if (llvm::is_contained(*Params, TemplateType)) {104        // Ignore forwarding reference105        return;106      }107    }108  }109 110  const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call");111  if (!MoveCall) {112    diag(Param->getLocation(),113         "rvalue reference parameter %0 is never moved from "114         "inside the function body")115        << Param;116  }117}118 119RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck(120    StringRef Name, ClangTidyContext *Context)121    : ClangTidyCheck(Name, Context),122      AllowPartialMove(Options.get("AllowPartialMove", false)),123      IgnoreUnnamedParams(Options.get("IgnoreUnnamedParams", false)),124      IgnoreNonDeducedTemplateTypes(125          Options.get("IgnoreNonDeducedTemplateTypes", false)),126      MoveFunction(Options.get("MoveFunction", "::std::move")) {}127 128void RvalueReferenceParamNotMovedCheck::storeOptions(129    ClangTidyOptions::OptionMap &Opts) {130  Options.store(Opts, "AllowPartialMove", AllowPartialMove);131  Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams);132  Options.store(Opts, "IgnoreNonDeducedTemplateTypes",133                IgnoreNonDeducedTemplateTypes);134  Options.store(Opts, "MoveFunction", MoveFunction);135}136 137} // namespace clang::tidy::cppcoreguidelines138