123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- //===--- MoveConstArgCheck.cpp - clang-tidy -----------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "MoveConstArgCheck.h"
- #include "clang/Lex/Lexer.h"
- using namespace clang::ast_matchers;
- namespace clang::tidy::performance {
- static void replaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,
- const SourceManager &SM,
- const LangOptions &LangOpts) {
- const Expr *Arg = Call->getArg(0);
- CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
- CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()),
- SM, LangOpts);
- CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
- CharSourceRange::getCharRange(Call->getEndLoc(),
- Call->getEndLoc().getLocWithOffset(1)),
- SM, LangOpts);
- if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
- Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
- << FixItHint::CreateRemoval(AfterArgumentsRange);
- }
- }
- void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
- Options.store(Opts, "CheckMoveToConstRef", CheckMoveToConstRef);
- }
- void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
- auto MoveCallMatcher =
- callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
- unless(isInTemplateInstantiation()))
- .bind("call-move");
- Finder->addMatcher(MoveCallMatcher, this);
- auto ConstTypeParmMatcher =
- qualType(references(isConstQualified())).bind("invocation-parm-type");
- auto RValueTypeParmMatcher =
- qualType(rValueReferenceType()).bind("invocation-parm-type");
- // Matches respective ParmVarDecl for a CallExpr or CXXConstructExpr.
- auto ArgumentWithParamMatcher = forEachArgumentWithParam(
- MoveCallMatcher, parmVarDecl(anyOf(hasType(ConstTypeParmMatcher),
- hasType(RValueTypeParmMatcher)))
- .bind("invocation-parm"));
- // Matches respective types of arguments for a CallExpr or CXXConstructExpr
- // and it works on calls through function pointers as well.
- auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(
- MoveCallMatcher, anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
- Finder->addMatcher(
- invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))
- .bind("receiving-expr"),
- this);
- }
- bool IsRValueReferenceParam(const Expr *Invocation,
- const QualType *InvocationParmType,
- const Expr *Arg) {
- if (Invocation && (*InvocationParmType)->isRValueReferenceType() &&
- Arg->isLValue()) {
- if (!Invocation->getType()->isRecordType())
- return true;
- else {
- if (const auto *ConstructCallExpr =
- dyn_cast<CXXConstructExpr>(Invocation)) {
- if (const auto *ConstructorDecl = ConstructCallExpr->getConstructor()) {
- if (!ConstructorDecl->isCopyOrMoveConstructor() &&
- !ConstructorDecl->isDefaultConstructor())
- return true;
- }
- }
- }
- }
- return false;
- }
- void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
- const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
- const auto *InvocationParm =
- Result.Nodes.getNodeAs<ParmVarDecl>("invocation-parm");
- const auto *InvocationParmType =
- Result.Nodes.getNodeAs<QualType>("invocation-parm-type");
- // Skipping matchers which have been matched.
- if (!ReceivingExpr && AlreadyCheckedMoves.contains(CallMove))
- return;
- if (ReceivingExpr)
- AlreadyCheckedMoves.insert(CallMove);
- const Expr *Arg = CallMove->getArg(0);
- SourceManager &SM = Result.Context->getSourceManager();
- CharSourceRange MoveRange =
- CharSourceRange::getCharRange(CallMove->getSourceRange());
- CharSourceRange FileMoveRange =
- Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
- if (!FileMoveRange.isValid())
- return;
- bool IsConstArg = Arg->getType().isConstQualified();
- bool IsTriviallyCopyable =
- Arg->getType().isTriviallyCopyableType(*Result.Context);
- if (IsConstArg || IsTriviallyCopyable) {
- if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
- // According to [expr.prim.lambda]p3, "whether the closure type is
- // trivially copyable" property can be changed by the implementation of
- // the language, so we shouldn't rely on it when issuing diagnostics.
- if (R->isLambda())
- return;
- // Don't warn when the type is not copyable.
- for (const auto *Ctor : R->ctors()) {
- if (Ctor->isCopyConstructor() && Ctor->isDeleted())
- return;
- }
- }
- if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
- return;
- bool IsVariable = isa<DeclRefExpr>(Arg);
- // std::move shouldn't be removed when an lvalue wrapped by std::move is
- // passed to the function with an rvalue reference parameter.
- bool IsRVRefParam =
- IsRValueReferenceParam(ReceivingExpr, InvocationParmType, Arg);
- const auto *Var =
- IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
- {
- auto Diag = diag(FileMoveRange.getBegin(),
- "std::move of the %select{|const }0"
- "%select{expression|variable %5}1 "
- "%select{|of the trivially-copyable type %6 }2"
- "has no effect%select{; remove std::move()|}3"
- "%select{| or make the variable non-const}4")
- << IsConstArg << IsVariable << IsTriviallyCopyable
- << IsRVRefParam
- << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
- << Arg->getType();
- if (!IsRVRefParam)
- replaceCallWithArg(CallMove, Diag, SM, getLangOpts());
- }
- if (IsRVRefParam) {
- // Generate notes for an invocation with an rvalue reference parameter.
- const auto *ReceivingCallExpr = dyn_cast<CallExpr>(ReceivingExpr);
- const auto *ReceivingConstructExpr =
- dyn_cast<CXXConstructExpr>(ReceivingExpr);
- // Skipping the invocation which is a template instantiation.
- if ((!ReceivingCallExpr || !ReceivingCallExpr->getDirectCallee() ||
- ReceivingCallExpr->getDirectCallee()->isTemplateInstantiation()) &&
- (!ReceivingConstructExpr ||
- !ReceivingConstructExpr->getConstructor() ||
- ReceivingConstructExpr->getConstructor()->isTemplateInstantiation()))
- return;
- const NamedDecl *FunctionName = nullptr;
- FunctionName =
- ReceivingCallExpr
- ? ReceivingCallExpr->getDirectCallee()->getUnderlyingDecl()
- : ReceivingConstructExpr->getConstructor()->getUnderlyingDecl();
- QualType NoRefType = (*InvocationParmType)->getPointeeType();
- PrintingPolicy PolicyWithSuppressedTag(getLangOpts());
- PolicyWithSuppressedTag.SuppressTagKeyword = true;
- PolicyWithSuppressedTag.SuppressUnwrittenScope = true;
- std::string ExpectParmTypeName =
- NoRefType.getAsString(PolicyWithSuppressedTag);
- if (!NoRefType->isPointerType()) {
- NoRefType.addConst();
- ExpectParmTypeName =
- NoRefType.getAsString(PolicyWithSuppressedTag) + " &";
- }
- diag(InvocationParm->getLocation(),
- "consider changing the %ordinal0 parameter of %1 from %2 to '%3'",
- DiagnosticIDs::Note)
- << (InvocationParm->getFunctionScopeIndex() + 1) << FunctionName
- << *InvocationParmType << ExpectParmTypeName;
- }
- } else if (ReceivingExpr && CheckMoveToConstRef) {
- if ((*InvocationParmType)->isRValueReferenceType())
- return;
- auto Diag = diag(FileMoveRange.getBegin(),
- "passing result of std::move() as a const reference "
- "argument; no move will actually happen");
- replaceCallWithArg(CallMove, Diag, SM, getLangOpts());
- }
- }
- } // namespace clang::tidy::performance
|