//===--- NoAutomaticMoveCheck.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 "NoAutomaticMoveCheck.h" #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; namespace clang::tidy::performance { namespace { AST_MATCHER(VarDecl, isNRVOVariable) { return Node.isNRVOVariable(); } } // namespace NoAutomaticMoveCheck::NoAutomaticMoveCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), AllowedTypes( utils::options::parseStringList(Options.get("AllowedTypes", ""))) {} void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) { const auto NonNrvoConstLocalVariable = varDecl(hasLocalStorage(), unless(hasType(lValueReferenceType())), unless(isNRVOVariable()), hasType(qualType( isConstQualified(), hasCanonicalType(matchers::isExpensiveToCopy()), unless(hasDeclaration(namedDecl( matchers::matchesAnyListedName(AllowedTypes))))))) .bind("vardecl"); // A matcher for a `DstT::DstT(const Src&)` where DstT also has a // `DstT::DstT(Src&&)`. const auto LValueRefCtor = cxxConstructorDecl( hasParameter(0, hasType(lValueReferenceType(pointee(type().bind("SrcT"))))), ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl( hasParameter(0, hasType(rValueReferenceType( pointee(type(equalsBoundNode("SrcT"))))))))))); // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an // instantiation of `template DstT::DstT(U&&)`. const auto ConstRefRefCtor = cxxConstructorDecl( parameterCountIs(1), hasParameter(0, hasType(rValueReferenceType(pointee(isConstQualified()))))); Finder->addMatcher( traverse( TK_AsIs, returnStmt(hasReturnValue( ignoringElidableConstructorCall(ignoringParenImpCasts( cxxConstructExpr( hasDeclaration(anyOf(LValueRefCtor, ConstRefRefCtor)), hasArgument(0, ignoringParenImpCasts(declRefExpr( to(NonNrvoConstLocalVariable))))) .bind("ctor_call")))))), this); } void NoAutomaticMoveCheck::check(const MatchFinder::MatchResult &Result) { const auto *Var = Result.Nodes.getNodeAs("vardecl"); const auto *CtorCall = Result.Nodes.getNodeAs("ctor_call"); diag(CtorCall->getExprLoc(), "constness of '%0' prevents automatic move") << Var->getName(); } void NoAutomaticMoveCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "AllowedTypes", utils::options::serializeStringList(AllowedTypes)); } } // namespace clang::tidy::performance