Febbe updated this revision to Diff 474260.
Febbe added a comment.
Fixed segfaults due to asserts which were wrongly assumed to be always true
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D137205/new/
https://reviews.llvm.org/D137205
Files:
clang-tools-extra/clang-tidy/performance/CMakeLists.txt
clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyOnLastUseCheck.cpp
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyOnLastUseCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-copy-on-last-use.rst
clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-on-last-use.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-on-last-use.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-on-last-use.cpp
@@ -0,0 +1,168 @@
+// RUN: %check_clang_tidy %s -std=c++17 performance-unnecessary-copy-on-last-use %t
+// RUN: %check_clang_tidy %s -std=c++11 performance-unnecessary-copy-on-last-use %t
+// CHECK-FIXES: #include <utility>
+
+struct Movable {
+ Movable() = default;
+ Movable(Movable const &) = default;
+ Movable(Movable &&) = default;
+ Movable &operator=(Movable const &) = default;
+ Movable &operator=(Movable &&) = default;
+ ~Movable();
+
+ void memberUse() {}
+};
+// static_assert(!std::is_trivially_copyable_v<Movable>, "Movable must not be trivially copyable");
+
+void valueReceiver(Movable Mov);
+void constRefReceiver(Movable const &Mov);
+
+void valueTester() {
+ Movable Mov{};
+ valueReceiver(Mov);
+ valueReceiver(Mov);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: valueReceiver(std::move(Mov));
+ Mov = Movable{};
+ valueReceiver(Mov);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: valueReceiver(std::move(Mov));
+}
+
+void testUsageInBranch(bool Splitter) {
+ Movable Mov{};
+
+ valueReceiver(Mov);
+ if(Splitter){
+ Mov.memberUse();
+ } else {
+ Mov = Movable{};
+ }
+ valueReceiver(Mov);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: valueReceiver(std::move(Mov));
+
+ if(Splitter){
+ Mov = Movable{};
+ } else {
+ Mov = Movable{};
+ }
+ valueReceiver(Mov);
+ Mov.memberUse();
+}
+
+void testExplicitCopy() {
+ Movable Mov{};
+ constRefReceiver(Movable{Mov});
+ // CHECK-MESSAGES: [[@LINE-1]]:28: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: constRefReceiver(Movable{std::move(Mov)});
+}
+
+Movable testReturn() {
+ Movable Mov{};
+ return Mov; // no warning, copy elision
+}
+
+Movable testReturn2(Movable && Mov, bool F) {
+ return F? Mov: Movable{};
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: return F? std::move(Mov): Movable{};
+}
+
+void rValReferenceTester(Movable&& Mov) {
+ valueReceiver(Mov);
+ valueReceiver(Mov);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: valueReceiver(std::move(Mov));
+ Mov = Movable{};
+ valueReceiver(Mov);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // CHECK-FIXES: valueReceiver(std::move(Mov));
+}
+
+void referenceTester(Movable& Mov) {
+ valueReceiver(Mov);
+ valueReceiver(Mov);
+ Mov = Movable{};
+ valueReceiver(Mov);
+}
+
+void pointerTester(Movable* Mov) {
+ valueReceiver(*Mov);
+ valueReceiver(*Mov);
+ *Mov = Movable{};
+ valueReceiver(*Mov);
+}
+
+template <class T>
+struct RemoveRef{
+ using type = T;
+};
+
+template <class T>
+struct RemoveRef<T&&>{
+ using type = T;
+};
+
+template <class T>
+struct RemoveRef<T&>{
+ using type = T;
+};
+
+template<class T>
+using RemoveRefT = typename RemoveRef<T>::type;
+
+template <class Movable>
+void initSomething(Movable&& Mov) {
+ valueReceiver(Mov);
+ valueReceiver(Mov); // no warning: Todo(bug): Is this a bug? Fix or explain.
+ Mov = RemoveRefT<Movable>{};
+ valueReceiver(Mov);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Mov' may be copied on last use, consider forwarding it instead. [performance-unnecessary-copy-on-last-use]
+}
+
+void initSomethingVal(Movable const& Mov) {
+ initSomething<Movable>(Movable{Mov});
+}
+
+void initSomethingRValRef(Movable const& Mov) {
+ initSomething<Movable&&>(Movable{Mov});
+}
+
+void initSomethingRef(Movable& Mov) {
+ initSomething<Movable&>(Mov);
+}
+
+/*
+Todo (improvement):
+Currently the check does not find last usages of fields of local objects.
+
+struct MoveOwner{
+ Movable Mov{};
+};
+
+void testFieldMove(){
+ MoveOwner Owner{};
+ valueReceiver(Owner.Mov);
+ Owner.Mov = Movable{};
+ valueReceiver(Owner.Mov);
+ // DISABLED-CHECK-MESSAGES: [[@LINE-1]]:17: warning: Parameter 'Owner.Mov' is copied on last use, consider moving it instead. [performance-unnecessary-copy-on-last-use]
+ // DISABLED-CHECK-FIXES: valueReceiver(std::move(Owner.Mov));
+}
+*/
+
+/*
+Todo (improvement):
+Currently a heuristic detection of guards is not implemented, so this test is disabled
+But before this is done, the check should not be used for automatic fixing
+
+using NoMove = std::shared_ptr<std::lock_guard<std::mutex>>;
+
+void shareMutex(NoMove Nmov);
+
+void testNoMove(std::mutex& M, int& I) {
+ NoMove Nmov = std::make_shared<std::lock_guard<std::mutex>>(M);
+ shareMutex(Nmov);
+ I = 42;
+}
+*/
Index: clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-copy-on-last-use.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-copy-on-last-use.rst
@@ -0,0 +1,72 @@
+.. title:: clang-tidy - performance-unnecessary-copy-on-last-use
+
+performance-unnecessary-copy-on-last-use
+========================================
+
+Finds variables of non-trivially-copyable types, that are used in a copy
+construction on their last usage and suggest to wrap the usage in a
+``std::move``. The usage just before an assignment is interpreted as last usage.
+
+Many programmers tend to forget ``std::move`` for variables that can be moved.
+Instead, the compiler creates implicit copies, which can significantly decrease
+performance.
+
+Example
+--------
+
+.. code-block:: c++
+
+ void acceptor(std::vector<int> v);
+ void Function() {
+ std::vector<int> v;
+ v.emplace_back(20);
+ // The warning will suggest passing this as a rvalue-reference
+ acceptor(v);
+ }
+
+Options
+-------
+
+.. option:: BlockedTypes
+
+ A semicolon-separated list of names of types allowed to be copied on last
+ usage. Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches
+ every type with suffix `Ref`, `ref`, `Reference` and `reference`.
+ The default is empty. If a name in the list contains the sequence `::` it
+ is matched against the qualified typename (i.e. `namespace::Type`,
+ otherwise it is matched against only the type name (i.e. `Type`).
+
+.. option:: BlockedFunctions
+ A semicolon-separated list of names of functions who's parameters do not
+ participate in the resolution.
+ Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches
+ every type with suffix `Ref`, `ref`, `Reference` and `reference`.
+ The default is empty. If a name in the list contains the sequence `::` it
+ is matched against the qualified typename (i.e. `namespace::Type`,
+ otherwise it is matched against only the type name (i.e. `Type`).
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
+
+Limitations
+-----------
+
+This check does not implement a heuristic, if a variable is used as guard until
+the end of it's scope. It also does not check, if a variable has any side
+effects in the destructor, which must be applied at the end of the scope.
+Therefore, it will suggest to use ``std::move`` in the
+following case, where `guard` protects `i`:
+
+.. code-block:: cpp
+
+ void acceptor(std::shared_ptr<std::unique_lock<std::mutex>>, int& i);
+
+ void f(std::shared_ptr<std::unique_lock<std::mutex>> guard, int& i) {
+ acceptor(guard, i);
+ i++;
+ }
+
+ This check is also unable to detect last usages for fields, if the parent
+ class is destructed after the last usage.
\ No newline at end of file
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -318,6 +318,7 @@
`performance-trivially-destructible <performance/trivially-destructible.html>`_, "Yes"
`performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn.html>`_, "Yes"
`performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization.html>`_, "Yes"
+ `performance-unnecessary-copy-on-last-use <performance/unnecessary-copy-on-last-use.html>`_, "Yes"
`performance-unnecessary-value-param <performance/unnecessary-value-param.html>`_, "Yes"
`portability-restrict-system-includes <portability/restrict-system-includes.html>`_, "Yes"
`portability-simd-intrinsics <portability/simd-intrinsics.html>`_,
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -115,6 +115,13 @@
Warns when using ``do-while`` loops.
+- New :doc:`performance-unnecessary-copy-on-last-use
+ <clang-tidy/checks/performance/unnecessary-copy-on-last-use>` check.
+
+ Finds variables of non-trivially-copyable types, that are used in a copy
+ construction on their last usage and suggest to wrap the usage in a
+ ``std::move``.
+
New check aliases
^^^^^^^^^^^^^^^^^
Index: clang-tools-extra/clang-tidy/performance/UnnecessaryCopyOnLastUseCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyOnLastUseCheck.h
@@ -0,0 +1,62 @@
+//===--- UnnecessaryCopyOnLastUseCheck.h - clang-tidy ------------*- C++-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_ON_LAST_USE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_ON_LAST_USE_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+
+class CFG;
+namespace tidy::performance {
+
+/// A check that flags value parameters on last usages that can safely be moved,
+/// because they are copied anyway.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance/unnecessary-copy-on-last-use.html
+
+class UnnecessaryCopyOnLastUseCheck : public ClangTidyCheck {
+public:
+ UnnecessaryCopyOnLastUseCheck(StringRef Name, ClangTidyContext *Context);
+ UnnecessaryCopyOnLastUseCheck(UnnecessaryCopyOnLastUseCheck &&) = delete;
+ UnnecessaryCopyOnLastUseCheck(const UnnecessaryCopyOnLastUseCheck &) = delete;
+ UnnecessaryCopyOnLastUseCheck &
+ operator=(UnnecessaryCopyOnLastUseCheck &&) = delete;
+ UnnecessaryCopyOnLastUseCheck &
+ operator=(const UnnecessaryCopyOnLastUseCheck &) = delete;
+ ~UnnecessaryCopyOnLastUseCheck() override;
+
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void onEndOfTranslationUnit() override;
+
+private:
+ CFG *getOrCreateCFG(FunctionDecl const *FD, ASTContext *C);
+
+ utils::IncludeInserter Inserter;
+ std::vector<StringRef> const BlockedTypes;
+ std::vector<StringRef> const BlockedFunctions;
+
+ // cfg cache
+ llvm::DenseMap<FunctionDecl const *, std::unique_ptr<CFG>> CFGs;
+};
+
+} // namespace tidy::performance
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_ON_LAST_USE_H
Index: clang-tools-extra/clang-tidy/performance/UnnecessaryCopyOnLastUseCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyOnLastUseCheck.cpp
@@ -0,0 +1,314 @@
+//===--- UnnecessaryCopyOnLastUseCheck.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 "UnnecessaryCopyOnLastUseCheck.h"
+
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "../utils/TypeTraits.h"
+#include "FasterStringFindCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/PriorityQueue.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <utility>
+
+using namespace clang;
+using namespace clang::tidy;
+using namespace clang::tidy::performance;
+using namespace clang::ast_matchers;
+
+static constexpr auto BlockedTypesOption = "BlockedTypes";
+static constexpr auto BlockedFunctionsOption = "BlockedFunctions";
+
+namespace {
+
+struct FindDeclRefBlockReturn {
+ CFGBlock const *DeclRefBlock = nullptr;
+ CFGBlock::const_iterator StartElement{};
+};
+
+enum class Usage {
+ Error = -1,
+ Usage = 0,
+ DefiniteLastUse,
+ LikelyDefiniteLastUse,
+};
+
+} // namespace
+
+static auto findDeclRefBlock(CFG const *TheCFG, DeclRefExpr const *DeclRef)
+ -> FindDeclRefBlockReturn {
+ for (CFGBlock *Block : *TheCFG) {
+ auto Iter =
+ llvm::find_if(Block->Elements, [&, DeclRef](CFGElement const &Element) {
+ if (Element.getKind() == CFGElement::Statement) {
+ return Element.template castAs<CFGStmt>().getStmt() == DeclRef;
+ }
+ return false;
+ });
+ if (Iter != Block->Elements.end()) {
+ return {Block, ++Iter};
+ }
+ }
+ return {nullptr, {}};
+}
+
+static auto
+nextUsageInCurrentBlock(FindDeclRefBlockReturn const &StartBlockElement,
+ DeclRefExpr const *DeclRef) {
+ // Search for uses in the current block
+ auto Begi = StartBlockElement.StartElement;
+ auto Endi = StartBlockElement.DeclRefBlock->Elements.end();
+ auto Iter = std::find_if(Begi, Endi, [&](CFGElement const &Element) {
+ if (Element.getKind() == CFGElement::Statement) {
+ if (auto *Stmt = Element.template castAs<CFGStmt>().getStmt()) {
+ if (auto *DRE = dyn_cast<DeclRefExpr>(Stmt)) {
+ if (DRE->getDecl() == DeclRef->getDecl()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ });
+ return Iter != Endi ? &*Iter : nullptr;
+}
+
+static bool isLHSOfAssignment(DeclRefExpr const *DeclRef, ASTContext &Context) {
+ auto Matches = match(
+ stmt(cxxOperatorCallExpr(
+ isAssignmentOperator(),
+ hasLHS(ignoringParenImpCasts(declRefExpr(equalsNode(DeclRef)))))),
+ Context);
+ return !Matches.empty();
+}
+
+static Usage definiteLastUse(ASTContext *Context, CFG *const TheCFG,
+ DeclRefExpr const *DeclRef) {
+ assert(TheCFG != nullptr);
+
+ // Find the CFGBlock containing the DeclRefExpr
+ auto StartBlockElement = findDeclRefBlock(TheCFG, DeclRef);
+ if (StartBlockElement.DeclRefBlock == nullptr) {
+ return Usage::Error; // Todo(unexpected): DeclRefExpr not found in CFG
+ }
+
+ // Find next uses of the DeclRefExpr
+
+ auto TraverseCFGForUsage = [&]() -> Usage {
+ llvm::SmallPtrSet<CFGBlock const *, 8> VisitedBlocks;
+ llvm::SmallVector<CFGBlock const *, 8> Worklist;
+
+ auto HandleInternal = [&](FindDeclRefBlockReturn const &BlockElement) {
+ CFGElement const *NextUsageE =
+ nextUsageInCurrentBlock(BlockElement, DeclRef);
+ if (NextUsageE) {
+ if (bool const IsLastUsage =
+ isLHSOfAssignment(llvm::cast<DeclRefExpr>(
+ NextUsageE->castAs<CFGStmt>().getStmt()),
+ *Context);
+ !IsLastUsage) {
+ return Usage::Usage;
+ }
+ return Usage::DefiniteLastUse;
+ }
+ assert(BlockElement.DeclRefBlock);
+ // No successing DeclRefExpr found, appending successors
+ for (CFGBlock const *Succ : BlockElement.DeclRefBlock->succs()) {
+ if (Succ) { // Succ can be nullptr, if a block is unreachable
+ Worklist.push_back(Succ);
+ }
+ }
+ return Usage::DefiniteLastUse; // No usage found, assume last use
+ };
+
+ if (auto FoundUsage = HandleInternal(StartBlockElement);
+ FoundUsage == Usage::Usage) { // Usage found
+ return FoundUsage;
+ }
+ while (!Worklist.empty()) {
+ CFGBlock const *Block = Worklist.pop_back_val();
+ if (!VisitedBlocks.insert(Block).second) {
+ continue;
+ }
+ if (auto FoundUsage = HandleInternal({Block, Block->Elements.begin()});
+ FoundUsage == Usage::Usage) {
+ return FoundUsage;
+ }
+ }
+ return Usage::DefiniteLastUse;
+ };
+
+ return TraverseCFGForUsage();
+}
+
+namespace clang::tidy::performance {
+
+UnnecessaryCopyOnLastUseCheck::~UnnecessaryCopyOnLastUseCheck() = default;
+
+UnnecessaryCopyOnLastUseCheck::UnnecessaryCopyOnLastUseCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ Inserter(Options.getLocalOrGlobal("IncludeStyle",
+ utils::IncludeSorter::IS_LLVM),
+ areDiagsSelfContained()),
+ BlockedTypes(
+ utils::options::parseStringList(Options.get(BlockedTypesOption, ""))),
+ BlockedFunctions(utils::options::parseStringList(
+ Options.get(BlockedFunctionsOption, ""))),
+ CFGs() {}
+
+void UnnecessaryCopyOnLastUseCheck::registerMatchers(MatchFinder *Finder) {
+ auto const ValueParameter =
+ declRefExpr(to(valueDecl(hasType(qualType(unless(anyOf(
+ isConstQualified(), lValueReferenceType(), pointerType(),
+ hasDeclaration(namedDecl(
+ matchers::matchesAnyListedName(BlockedTypes))) //
+ )))))))
+ .bind("param");
+
+ auto const IsMoveAssingable = cxxOperatorCallExpr(
+ hasDeclaration(cxxMethodDecl(
+ isCopyAssignmentOperator(),
+ ofClass(hasMethod(cxxMethodDecl(isMoveAssignmentOperator(),
+ unless(isDeleted())))))),
+ hasRHS(ValueParameter));
+
+ auto const IsMoveConstructible =
+ ignoringElidableConstructorCall(ignoringParenImpCasts(
+ cxxConstructExpr(
+ unless(hasParent(callExpr(hasDeclaration(namedDecl(
+ matchers::matchesAnyListedName(BlockedFunctions)))))),
+ hasDeclaration(cxxConstructorDecl(
+ isCopyConstructor(),
+ ofClass(hasMethod(cxxConstructorDecl(isMoveConstructor(),
+ unless(isDeleted())))))),
+ hasArgument(0, ValueParameter))
+ .bind("constructExpr")));
+
+ Finder->addMatcher(stmt(anyOf(IsMoveAssingable, expr(IsMoveConstructible))),
+ this);
+}
+
+void UnnecessaryCopyOnLastUseCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ TraversalKindScope const RAII(*Result.Context,
+ TK_IgnoreUnlessSpelledInSource);
+
+ const auto *Param = Result.Nodes.getNodeAs<DeclRefExpr>("param");
+ const ValueDecl *const DeclOfParam = Param->getDecl();
+ const DeclContext *const FunctionOfDeclContext =
+ DeclOfParam->getParentFunctionOrMethod();
+
+ if (Param->getType().isTriviallyCopyableType(*Result.Context)) {
+ return; // A move would decrease readability without any benefit
+ }
+
+ if (!FunctionOfDeclContext) {
+ // The parameter is not defined in a function, therefore it is not
+ // possible to check if it is the last use via CFG analysis
+ // Todo (improvement): Add a flag to show unanalyzable cases
+ return;
+ }
+
+ const auto *const FunctionOfDecl =
+ llvm::cast<FunctionDecl>(FunctionOfDeclContext);
+
+ const auto *const VarDeclVal = llvm::dyn_cast<VarDecl>(DeclOfParam);
+ if (!VarDeclVal) {
+ return;
+ }
+
+ Usage DefiniteLastUse = definiteLastUse(
+ Result.Context, getOrCreateCFG(FunctionOfDecl, Result.Context), Param);
+
+ if (DefiniteLastUse == Usage::Usage || DefiniteLastUse == Usage::Error) {
+ return;
+ }
+
+ // Template code cant be fixed currently
+ if (!FunctionOfDecl->isTemplateInstantiation()) {
+ clang::SourceManager &SM = *Result.SourceManager;
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Param->getLocation(), 0, SM, Result.Context->getLangOpts());
+ auto Diag =
+ diag(
+ Param->getExprLoc(),
+ "Parameter '%0' is copied on last use, consider moving it instead.")
+ << Param->getDecl()->getNameAsString()
+ << FixItHint::CreateInsertion(Param->getBeginLoc(), "std::move(")
+ << FixItHint::CreateInsertion(EndLoc, ")")
+ << Inserter.createIncludeInsertion(SM.getFileID(Param->getBeginLoc()),
+ "<utility>");
+ } else { // Template code can't be fixed currently, also a std::forward may be
+ // more appropriate
+ auto Diag =
+ diag(Param->getExprLoc(), "Parameter '%0' may be copied on last use, "
+ "consider forwarding it instead.")
+ << Param->getDecl()->getNameAsString();
+ }
+}
+
+void UnnecessaryCopyOnLastUseCheck::registerPPCallbacks(
+ const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+ Inserter.registerPreprocessor(PP);
+}
+
+void UnnecessaryCopyOnLastUseCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle", Inserter.getStyle());
+ Options.store(Opts, BlockedTypesOption,
+ utils::options::serializeStringList(BlockedTypes));
+ Options.store(Opts, BlockedFunctionsOption,
+ utils::options::serializeStringList(BlockedFunctions));
+}
+
+void UnnecessaryCopyOnLastUseCheck::onEndOfTranslationUnit() {}
+
+static auto createBuildOptions() {
+ CFG::BuildOptions Options;
+ Options.setAlwaysAdd(DeclRefExpr::DeclRefExprClass);
+ Options.AddImplicitDtors = true;
+ Options.AddTemporaryDtors = true;
+ return Options;
+}
+
+CFG *UnnecessaryCopyOnLastUseCheck::getOrCreateCFG(FunctionDecl const *FD,
+ ASTContext *C) {
+ static auto BO = createBuildOptions();
+ if (auto Iter = this->CFGs.find(FD); Iter != this->CFGs.end()) {
+ return Iter->second.get();
+ }
+
+ auto EmplaceResult =
+ this->CFGs.try_emplace(FD, CFG::buildCFG(nullptr, FD->getBody(), C, BO));
+ return EmplaceResult.first->second.get();
+}
+} // namespace clang::tidy::performance
Index: clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -23,6 +23,7 @@
#include "TriviallyDestructibleCheck.h"
#include "TypePromotionInMathFnCheck.h"
#include "UnnecessaryCopyInitialization.h"
+#include "UnnecessaryCopyOnLastUseCheck.h"
#include "UnnecessaryValueParamCheck.h"
namespace clang {
@@ -59,6 +60,8 @@
"performance-type-promotion-in-math-fn");
CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
"performance-unnecessary-copy-initialization");
+ CheckFactories.registerCheck<UnnecessaryCopyOnLastUseCheck>(
+ "performance-unnecessary-copy-on-last-use");
CheckFactories.registerCheck<UnnecessaryValueParamCheck>(
"performance-unnecessary-value-param");
}
Index: clang-tools-extra/clang-tidy/performance/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -18,6 +18,7 @@
PerformanceTidyModule.cpp
TriviallyDestructibleCheck.cpp
TypePromotionInMathFnCheck.cpp
+ UnnecessaryCopyOnLastUseCheck.cpp
UnnecessaryCopyInitialization.cpp
UnnecessaryValueParamCheck.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits