195fe5493SClement Courbet //===--- NoAutomaticMoveCheck.cpp - clang-tidy ----------------------------===//
295fe5493SClement Courbet //
395fe5493SClement Courbet // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
495fe5493SClement Courbet // See https://llvm.org/LICENSE.txt for license information.
595fe5493SClement Courbet // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
695fe5493SClement Courbet //
795fe5493SClement Courbet //===----------------------------------------------------------------------===//
895fe5493SClement Courbet
995fe5493SClement Courbet #include "NoAutomaticMoveCheck.h"
1095fe5493SClement Courbet #include "../utils/Matchers.h"
1195fe5493SClement Courbet #include "../utils/OptionsUtils.h"
1295fe5493SClement Courbet #include "clang/AST/ASTContext.h"
1395fe5493SClement Courbet #include "clang/ASTMatchers/ASTMatchFinder.h"
1495fe5493SClement Courbet
1595fe5493SClement Courbet using namespace clang::ast_matchers;
1695fe5493SClement Courbet
177d2ea6c4SCarlos Galvez namespace clang::tidy::performance {
1895fe5493SClement Courbet
19e51f4670SLogan Gnanapragasam namespace {
20e51f4670SLogan Gnanapragasam
AST_MATCHER(VarDecl,isNRVOVariable)21e51f4670SLogan Gnanapragasam AST_MATCHER(VarDecl, isNRVOVariable) { return Node.isNRVOVariable(); }
22e51f4670SLogan Gnanapragasam
23e51f4670SLogan Gnanapragasam } // namespace
24e51f4670SLogan Gnanapragasam
NoAutomaticMoveCheck(StringRef Name,ClangTidyContext * Context)2595fe5493SClement Courbet NoAutomaticMoveCheck::NoAutomaticMoveCheck(StringRef Name,
2695fe5493SClement Courbet ClangTidyContext *Context)
2795fe5493SClement Courbet : ClangTidyCheck(Name, Context),
2895fe5493SClement Courbet AllowedTypes(
2995fe5493SClement Courbet utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
3095fe5493SClement Courbet
registerMatchers(MatchFinder * Finder)3195fe5493SClement Courbet void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) {
32e51f4670SLogan Gnanapragasam const auto NonNrvoConstLocalVariable =
3395fe5493SClement Courbet varDecl(hasLocalStorage(), unless(hasType(lValueReferenceType())),
34e51f4670SLogan Gnanapragasam unless(isNRVOVariable()),
3595fe5493SClement Courbet hasType(qualType(
3695fe5493SClement Courbet isConstQualified(),
3795fe5493SClement Courbet hasCanonicalType(matchers::isExpensiveToCopy()),
3895fe5493SClement Courbet unless(hasDeclaration(namedDecl(
3995fe5493SClement Courbet matchers::matchesAnyListedName(AllowedTypes)))))))
4095fe5493SClement Courbet .bind("vardecl");
4195fe5493SClement Courbet
4295fe5493SClement Courbet // A matcher for a `DstT::DstT(const Src&)` where DstT also has a
4395fe5493SClement Courbet // `DstT::DstT(Src&&)`.
4495fe5493SClement Courbet const auto LValueRefCtor = cxxConstructorDecl(
4595fe5493SClement Courbet hasParameter(0,
4695fe5493SClement Courbet hasType(lValueReferenceType(pointee(type().bind("SrcT"))))),
4795fe5493SClement Courbet ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl(
4895fe5493SClement Courbet hasParameter(0, hasType(rValueReferenceType(
4995fe5493SClement Courbet pointee(type(equalsBoundNode("SrcT")))))))))));
5095fe5493SClement Courbet
51*9182c679SClement Courbet // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
52*9182c679SClement Courbet // instantiation of `template <typename U> DstT::DstT(U&&)`.
53*9182c679SClement Courbet const auto ConstRefRefCtor = cxxConstructorDecl(
54*9182c679SClement Courbet parameterCountIs(1),
55*9182c679SClement Courbet hasParameter(0,
56*9182c679SClement Courbet hasType(rValueReferenceType(pointee(isConstQualified())))));
57*9182c679SClement Courbet
5895fe5493SClement Courbet Finder->addMatcher(
59*9182c679SClement Courbet traverse(
60*9182c679SClement Courbet TK_AsIs,
61a72307c3SStephen Kelly returnStmt(hasReturnValue(
62a72307c3SStephen Kelly ignoringElidableConstructorCall(ignoringParenImpCasts(
63a72307c3SStephen Kelly cxxConstructExpr(
64*9182c679SClement Courbet hasDeclaration(anyOf(LValueRefCtor, ConstRefRefCtor)),
6595fe5493SClement Courbet hasArgument(0, ignoringParenImpCasts(declRefExpr(
66e51f4670SLogan Gnanapragasam to(NonNrvoConstLocalVariable)))))
67a72307c3SStephen Kelly .bind("ctor_call")))))),
6895fe5493SClement Courbet this);
6995fe5493SClement Courbet }
7095fe5493SClement Courbet
check(const MatchFinder::MatchResult & Result)7195fe5493SClement Courbet void NoAutomaticMoveCheck::check(const MatchFinder::MatchResult &Result) {
7295fe5493SClement Courbet const auto *Var = Result.Nodes.getNodeAs<VarDecl>("vardecl");
7395fe5493SClement Courbet const auto *CtorCall = Result.Nodes.getNodeAs<Expr>("ctor_call");
7495fe5493SClement Courbet diag(CtorCall->getExprLoc(), "constness of '%0' prevents automatic move")
7595fe5493SClement Courbet << Var->getName();
7695fe5493SClement Courbet }
7795fe5493SClement Courbet
storeOptions(ClangTidyOptions::OptionMap & Opts)7895fe5493SClement Courbet void NoAutomaticMoveCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
7995fe5493SClement Courbet Options.store(Opts, "AllowedTypes",
8095fe5493SClement Courbet utils::options::serializeStringList(AllowedTypes));
8195fe5493SClement Courbet }
8295fe5493SClement Courbet
837d2ea6c4SCarlos Galvez } // namespace clang::tidy::performance
84