//===--- MoveConstArgCheck.cpp - clang-tidy -----------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "MoveConstArgCheck.h" #include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace 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->getLocStart(), Arg->getLocStart()), SM, LangOpts); CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange( CharSourceRange::getCharRange(Call->getLocEnd(), Call->getLocEnd().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); } void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; auto MoveCallMatcher = callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1), unless(isInTemplateInstantiation())) .bind("call-move"); Finder->addMatcher(MoveCallMatcher, this); auto ConstParamMatcher = forEachArgumentWithParam( MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified())))); Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this); Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"), this); } void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs("call-move"); const auto *ReceivingExpr = Result.Nodes.getNodeAs("receiving-expr"); 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(Arg); const auto *Var = IsVariable ? dyn_cast(Arg)->getDecl() : nullptr; auto Diag = diag(FileMoveRange.getBegin(), "std::move of the %select{|const }0" "%select{expression|variable %4}1 " "%select{|of the trivially-copyable type %5 }2" "has no effect; remove std::move()" "%select{| or make the variable non-const}3") << IsConstArg << IsVariable << IsTriviallyCopyable << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var << Arg->getType(); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } else if (ReceivingExpr) { 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 performance } // namespace tidy } // namespace clang