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