staronj created this revision.
staronj added reviewers: Prazek, alex.
staronj added a subscriber: cfe-commits.

Adds performance-returning-type check.

This check is trying to find places, where one is returning different type than 
function returning type and when returned object is being copied instead of 
moved. Check wraps it with std::move when it could make it being moved.


I ran this check on LLVM codebase and it produced following changes: 
http://reviews.llvm.org/D21302

http://reviews.llvm.org/D21303

Files:
  clang-tidy/performance/CMakeLists.txt
  clang-tidy/performance/PerformanceTidyModule.cpp
  clang-tidy/performance/ReturningTypeCheck.cpp
  clang-tidy/performance/ReturningTypeCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/performance-returning-type.rst
  test/clang-tidy/performance-returning-type.cpp

Index: test/clang-tidy/performance-returning-type.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/performance-returning-type.cpp
@@ -0,0 +1,294 @@
+// RUN: %check_clang_tidy %s performance-returning-type %t
+// CHECK-FIXES: {{^}}#include <utility>{{$}}
+
+// we need std::move mock
+namespace std {
+template <typename _Tp>
+struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp>
+struct remove_reference<_Tp &> { typedef _Tp type; };
+
+template <typename _Tp>
+struct remove_reference<_Tp &&> { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&
+move(_Tp &&__t) noexcept { return static_cast<typename std::remove_reference<_Tp>::type &&>(__t); }
+}
+
+class SimpleClass {
+public:
+  SimpleClass() = default;
+  SimpleClass(const SimpleClass &) = default;
+  SimpleClass(SimpleClass &&) = default;
+
+  // We don't want to add std::move here because it will be added by compiler
+  SimpleClass foo(SimpleClass a, const SimpleClass b, SimpleClass &c, const SimpleClass &d, SimpleClass &&e, const SimpleClass &&f, char k) {
+    switch (k) {
+    case 'a':
+      return a;
+    case 'b':
+      return b;
+    case 'c':
+      return c;
+    case 'd':
+      return d;
+    case 'e':
+      return e;
+    case 'f':
+      return f;
+    default:
+      return SimpleClass();
+    }
+  }
+};
+
+SimpleClass simpleClassFoo() {
+  return SimpleClass();
+}
+
+class FromValueClass {
+public:
+  FromValueClass(SimpleClass a) {}
+
+  FromValueClass foo(SimpleClass a, const SimpleClass b, SimpleClass &c, const SimpleClass &d, SimpleClass &&e, const SimpleClass &&f, char k) {
+    switch (k) {
+    case 'a':
+      // Because SimpleClass is move constructible
+      return a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: expression could be wrapped with std::move [performance-returning-type]
+    // CHECK-FIXES: {{^ *}}return FromValueClass(std::move(a));{{$}}
+    case 'b':
+      return b;
+    case 'c':
+      return c;
+    case 'd':
+      return d;
+    case 'e':
+      return e;
+    case 'f':
+      return f;
+    case 'g':
+      return simpleClassFoo();
+    default:
+      return SimpleClass();
+    }
+  }
+};
+
+class FromRRefClass {
+public:
+  FromRRefClass() = default;
+  FromRRefClass(const SimpleClass &a) {}
+  FromRRefClass(SimpleClass &&a) {}
+
+  FromRRefClass foo1(SimpleClass a, const SimpleClass b, SimpleClass &c, const SimpleClass &d, SimpleClass &&e, const SimpleClass &&f, char k) {
+    switch (k) {
+    case 'a':
+      return a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: expression could be wrapped with std::move [performance-returning-type]
+    // CHECK-FIXES: {{^ *}}return FromRRefClass(std::move(a));{{$}}
+
+    // We don't want to add std::move in cases 'b-f because
+    case 'b':
+      return b;
+    case 'c':
+      return c;
+    case 'd':
+      return d;
+    case 'e':
+      return e;
+    case 'f':
+      return f;
+    // We don't want to add std::move below because it is not necessary (TODO is that true?)
+    case 'g':
+      return simpleClassFoo();
+    default:
+      return SimpleClass();
+    }
+  }
+
+  FromRRefClass foo2(char k) {
+    SimpleClass a;
+    const SimpleClass &b = a;
+    SimpleClass &c = a;
+    SimpleClass *d = &a;
+    const SimpleClass e;
+
+    switch (k) {
+    case 'a':
+      return a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: expression could be wrapped with std::move [performance-returning-type]
+    // CHECK-FIXES: {{^ *}}return FromRRefClass(std::move(a));{{$}}
+    case 'b':
+      return b;
+    case 'c':
+      return c;
+    case 'd':
+      return *d;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: expression could be wrapped with std::move [performance-returning-type]
+    // CHECK-FIXES: {{^ *}}return FromRRefClass(std::move(*d));{{$}}
+    case 'e':
+      return e;
+    case 'f':
+      return simpleClassFoo();
+    case 'x':
+      return SimpleClass();
+    case 'y':
+      return FromRRefClass(SimpleClass());
+    }
+  }
+
+  FromRRefClass foo3(char k) {
+    SimpleClass a;
+    SimpleClass b;
+    FromRRefClass c;
+    switch (k) {
+    case 'a':
+      return std::move(a);
+    case 'b':
+      return FromRRefClass(std::move(a));
+    case 'c':
+      return c;
+    default:
+      return FromRRefClass();
+    }
+  }
+};
+
+template <typename T>
+FromRRefClass justTemplateFunction(T &&t) {
+  return t;
+}
+
+void call_justTemplateFunction() {
+  justTemplateFunction(SimpleClass{});
+  SimpleClass a;
+  justTemplateFunction(a);
+  justTemplateFunction(FromRRefClass{});
+  FromRRefClass b;
+  justTemplateFunction(b);
+}
+
+class FromValueWithDeleted {
+public:
+  FromValueWithDeleted() = default;
+  FromValueWithDeleted(SimpleClass a) {}
+  FromValueWithDeleted(SimpleClass &&a) = delete;
+};
+
+FromValueWithDeleted foo9() {
+  SimpleClass a;
+  return a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{..}}
+  // CHECK-FIXES: {{^ *}}return FromValueWithDeleted(std::move(a));{{$}}
+}
+
+template <typename T>
+struct old_optional {
+  old_optional(const T &t) {}
+};
+
+old_optional<SimpleClass> test_old_optional() {
+  SimpleClass a;
+  return a;
+}
+
+template <typename T>
+struct optional {
+  optional(const T &) {}
+  optional(T &&) {}
+};
+
+optional<SimpleClass> test_optional() {
+  SimpleClass a;
+  return a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{..}}
+  // CHECK-FIXES: {{^ *}}return optional<SimpleClass>(std::move(a));{{$}}
+}
+
+using Opt = optional<SimpleClass>;
+Opt test_Opt() {
+  SimpleClass a;
+  return a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{..}}
+  // CHECK-FIXES: {{^ *}}return Opt(std::move(a));{{$}}
+}
+
+struct any {
+  any() = default;
+
+  template <typename T>
+  any(T &&) {}
+
+  any(any &&) = default;
+  any(const any &) = default;
+};
+
+any test_any() {
+  SimpleClass a;
+  return a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{..}}
+  // CHECK-FIXES: {{^ *}}return any(std::move(a));{{$}}
+}
+
+any test_any2() {
+  SimpleClass a;
+  return std::move(a);
+}
+any test_any3() {
+  SimpleClass a;
+  return any(std::move(a));
+}
+
+any test_any4() {
+  any a;
+  return a;
+}
+
+class FromRefWithDeleted {
+public:
+  FromRefWithDeleted(SimpleClass &&) = delete;
+  FromRefWithDeleted(const SimpleClass &a) {}
+};
+
+FromRefWithDeleted foo11(SimpleClass a) {
+  return a;
+}
+
+class ClassWithUsings {
+public:
+  using value = SimpleClass;
+  using const_value = const SimpleClass;
+  using lreference = SimpleClass &;
+  using const_lreference = const SimpleClass &;
+  using rreference = SimpleClass &&;
+  using const_rreference = const SimpleClass &&;
+
+  ClassWithUsings(rreference) {}
+  ClassWithUsings(const_lreference) {}
+
+  ClassWithUsings foo(value a, const_value b, lreference c, const_lreference d, rreference e, const_rreference f, char k) {
+    switch (k) {
+    case 'a':
+      return a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{..}}
+    // CHECK-FIXES: {{^ *}}return ClassWithUsings(std::move(a));{{$}}
+    case 'b':
+      return b;
+    case 'c':
+      return c;
+    case 'd':
+      return d;
+    case 'e':
+      return e;
+    case 'f':
+      return f;
+    case 'g':
+      return simpleClassFoo();
+    default:
+      return SimpleClass();
+    }
+  }
+};
Index: docs/clang-tidy/checks/performance-returning-type.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/performance-returning-type.rst
@@ -0,0 +1,35 @@
+.. title:: clang-tidy - performance-returning-type
+
+performance-returning-type
+==========================
+
+This check finds places where we are returning object of a different type than
+the function return type. In such places, we should use std::move, otherwise
+the object will not be moved automatically.
+Check adds std::move if it could be beneficial.
+
+For example:
+
+.. code-block:: c++
+
+  boost::optional<std::string> get() {
+    string s;
+    ;;;
+    return s;
+  }
+
+  // becomes
+
+  boost::optional<std::string> get() {
+    string s;
+    ;;;
+    return std::move(s);
+  }
+
+Of course if we return an rvalue (e.g., temporary) we don’t have to move it:
+
+.. code-block:: c++
+
+  boost::optional<std::string> get() {
+    return "str";
+  }
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -109,6 +109,7 @@
    performance-faster-string-find
    performance-for-range-copy
    performance-implicit-cast-in-loop
+   performance-returning-type
    performance-unnecessary-copy-initialization
    performance-unnecessary-value-param
    readability-avoid-const-params-in-decls
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -232,6 +232,14 @@
   Warns about range-based loop with a loop variable of const ref type where the
   type of the variable does not match the one returned by the iterator.
 
+- New `performance-returning-type
+  <http://clang.llvm.org/extra/clang-tidy/checks/performance-returning-type.html>`_ check
+
+  This check finds places where we are returning object of a different type than
+  the function return type. In such places, we should use std::move, otherwise
+  the object will not be moved automatically.
+  Check adds std::move if it could be beneficial.
+
 - New `performance-unnecessary-value-param
   <http://clang.llvm.org/extra/clang-tidy/checks/performance-unnecessary-value-param.html>`_ check
 
Index: clang-tidy/performance/ReturningTypeCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/performance/ReturningTypeCheck.h
@@ -0,0 +1,44 @@
+//===--- ReturningTypeCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_RETURNING_TYPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_RETURNING_TYPE_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// This check finds places where we are returning object of a different type than
+/// the function return type. In such places, we should use std::move, otherwise
+/// the object will not be moved automatically.
+/// Check adds std::move if it could be beneficial.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-returning-type.html
+class ReturningTypeCheck : public ClangTidyCheck {
+public:
+  ReturningTypeCheck(StringRef Name, ClangTidyContext *Context);
+
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_RETURNING_TYPE_H
Index: clang-tidy/performance/ReturningTypeCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/performance/ReturningTypeCheck.cpp
@@ -0,0 +1,259 @@
+//===--- ReturningTypeCheck.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 "ReturningTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+/// \brief Matches if the construct expression's callee's declaration matches
+/// the given matcher.
+///
+/// Given
+/// \code
+///   // POD types are trivially move constructible.
+///   struct Foo {
+///     Foo() = default;
+///     Foo(int i) { };
+///   };
+///
+///   Foo a;
+///   Foo b(1);
+///
+/// \endcode
+/// ctorCallee(parameterCountIs(1))
+///   matches "b(1)".
+AST_MATCHER_P(CXXConstructExpr, ctorCallee,
+              ast_matchers::internal::Matcher<CXXConstructorDecl>,
+              InnerMatcher) {
+  const CXXConstructorDecl *CtorDecl = Node.getConstructor();
+  return (CtorDecl != nullptr &&
+          InnerMatcher.matches(*CtorDecl, Finder, Builder));
+}
+
+/// \brief Matches if the matched type after removing const and reference
+/// matches the given matcher.
+AST_MATCHER_P(QualType, ignoringRefsAndConsts,
+              ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+  if (Node.isNull())
+    return false;
+
+  QualType Unqualified = Node.getNonReferenceType();
+  Unqualified.removeLocalConst();
+  return InnerMatcher.matches(Unqualified, Finder, Builder);
+}
+
+/// \brief Matches ParmVarDecls which have default arguments.
+AST_MATCHER(ParmVarDecl, hasDefaultArgument) { return Node.hasDefaultArg(); }
+
+/// \brief Matches function declarations which have all arguments defaulted
+/// except first.
+AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<FunctionDecl>,
+                     haveOneActiveArgument) {
+  return anyOf(parameterCountIs(1),
+               allOf(unless(parameterCountIs(0)),
+                     hasParameter(2, hasDefaultArgument())));
+}
+
+/// \brief Matches declarations of template type which matches the given
+/// matcher.
+AST_MATCHER_P(TemplateTypeParmDecl, hasTemplateType,
+              ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+  const QualType TemplateType =
+      Node.getTypeForDecl()->getCanonicalTypeInternal();
+  return InnerMatcher.matches(TemplateType, Finder, Builder);
+}
+
+/// \brief Matches constructor declarations which are templates and can be used
+/// to move-construct from any type.
+AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<CXXConstructorDecl>,
+                     moveConstructorFromAnyType) {
+  // Potentially danger: this matcher binds a name, with probably
+  // mean that you cant use it twice in your check!
+  const char TemplateArgument[] = "templateArgument";
+  return cxxConstructorDecl(
+      hasParent(functionTemplateDecl(has(templateTypeParmDecl(
+          hasTemplateType(qualType().bind(TemplateArgument)))))),
+      haveOneActiveArgument(),
+      hasParameter(
+          0, hasType(hasCanonicalType(allOf(
+                 rValueReferenceType(),
+                 ignoringRefsAndConsts(equalsBoundNode(TemplateArgument)))))));
+}
+
+/// \brief Matches to qual types which are cxx records and has constructors that
+/// matches the given matcher.
+AST_MATCHER_FUNCTION_P(ast_matchers::internal::Matcher<QualType>,
+                       hasConstructor,
+                       ast_matchers::internal::Matcher<CXXConstructorDecl>,
+                       InnerMatcher) {
+  return hasDeclaration(
+      cxxRecordDecl(hasDescendant(cxxConstructorDecl(InnerMatcher))));
+}
+
+/// \brief Matches to qual types which has constructors from type that matches
+/// the given matcher.
+AST_MATCHER_FUNCTION_P(ast_matchers::internal::Matcher<QualType>,
+                       hasConstructorFromType,
+                       ast_matchers::internal::Matcher<QualType>,
+                       InnerMatcher) {
+  auto ConstructorMatcher = cxxConstructorDecl(
+      unless(isDeleted()), haveOneActiveArgument(),
+      hasParameter(0, hasType(hasCanonicalType(qualType(InnerMatcher)))));
+
+  return hasConstructor(ConstructorMatcher);
+}
+
+} // namespace
+
+void ReturningTypeCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // Matches to type with after ignoring refs and consts is the same as
+  // "constructedType"
+  auto HasTypeSameAsConstructed = hasType(hasCanonicalType(
+      ignoringRefsAndConsts(equalsBoundNode("constructedType"))));
+
+  auto HasRvalueReferenceType =
+      hasType(hasCanonicalType(qualType(rValueReferenceType())));
+
+  auto RefOrConstVarDecl = varDecl(hasType(
+      hasCanonicalType(qualType(anyOf(referenceType(), isConstQualified())))));
+
+  // Matches to expression expression that have declaration with is reference or
+  // const
+  auto IsDeclaredAsRefOrConstType =
+      anyOf(hasDescendant(declRefExpr(to(RefOrConstVarDecl))),
+            declRefExpr(to(RefOrConstVarDecl)));
+
+  // Constructor parameter must not already be rreference
+  auto ParameterMatcher =
+      hasType(hasCanonicalType(qualType(unless(rValueReferenceType()))));
+
+  // Constructor argument must
+  // * have different type than constructed type
+  // * not be r value reference type
+  // * not be declared as const or as reference to other variable
+  // * not be temporary object
+  // * not be cast from temporary object
+  auto ArgumentMatcher =
+      unless(anyOf(HasTypeSameAsConstructed, HasRvalueReferenceType,
+                   IsDeclaredAsRefOrConstType, cxxTemporaryObjectExpr(),
+                   hasDescendant(cxxTemporaryObjectExpr())));
+
+  // Matches to type constructible from argumentCanonicalType type
+  // * by r reference or
+  // * by value, and argumentCanonicalType is move constructible
+  auto IsMoveOrCopyConstructibleFromParam = hasConstructorFromType(anyOf(
+      allOf(rValueReferenceType(),
+            ignoringRefsAndConsts(equalsBoundNode("argumentCanonicalType"))),
+      allOf(equalsBoundNode("argumentCanonicalType"),
+            hasConstructor(isMoveConstructor()))));
+
+  // Matches to type that has template constructor which is
+  // move constructor from any type (like boost::any)
+  auto IsCopyConstructibleFromParamViaTemplate =
+      hasConstructor(moveConstructorFromAnyType());
+
+  // Matches construct expr that
+  // * has one argument
+  // * that argument satisfies ArgumentMatcher
+  // * argument is not the result of move constructor
+  // * parameter of constructor satisfies ParameterMatcher
+  // * constructed type is move constructible from argument
+  // ** or is value constructible from argument and argument is movable
+  //    constructible
+  // ** constructed type has template constructor that can take by rref
+  //    (like boost::any)
+  auto ConstructExprMatcher =
+      cxxConstructExpr(
+          hasType(qualType().bind("constructedType")), argumentCountIs(1),
+          unless(has(ignoringParenImpCasts(
+              cxxConstructExpr(ctorCallee(isMoveConstructor()))))),
+          hasArgument(0, hasType(hasCanonicalType(ignoringRefsAndConsts(
+                             qualType().bind("argumentCanonicalType"))))),
+          ctorCallee(hasParameter(0, ParameterMatcher)),
+          hasArgument(0, ArgumentMatcher),
+          hasArgument(0, expr().bind("argument")),
+          hasType(qualType(anyOf(IsMoveOrCopyConstructibleFromParam,
+                                 IsCopyConstructibleFromParamViaTemplate))))
+          .bind("construct");
+
+  auto IsCopyOrMoveConstructor =
+      anyOf(isCopyConstructor(), isMoveConstructor());
+
+  Finder->addMatcher(
+      returnStmt(has(ignoringParenImpCasts(cxxConstructExpr(
+                     ctorCallee(IsCopyOrMoveConstructor),
+                     has(ignoringParenImpCasts(ConstructExprMatcher))))))
+          .bind("return"),
+      this);
+}
+
+ReturningTypeCheck::ReturningTypeCheck(StringRef Name,
+                                       ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void ReturningTypeCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  // Only register the preprocessor callbacks for C++; the functionality
+  // currently does not provide any benefit to other languages, despite being
+  // benign.
+  if (getLangOpts().CPlusPlus11) {
+    Inserter.reset(new utils::IncludeInserter(
+        Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+    Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+  }
+}
+
+void ReturningTypeCheck::check(const MatchFinder::MatchResult &Result) {
+  const LangOptions &Opts = Result.Context->getLangOpts();
+
+  const auto *Argument = Result.Nodes.getNodeAs<Expr>("argument");
+  assert(Argument != nullptr);
+
+  const auto *Type = Result.Nodes.getNodeAs<QualType>("constructedType");
+  assert(Type != nullptr);
+  assert(!Type->isNull());
+
+  std::string ReplacementText = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(Argument->getSourceRange()),
+      *Result.SourceManager, Opts);
+
+  ReplacementText =
+      Type->getAsString(getLangOpts()) + "(std::move(" + ReplacementText + "))";
+
+  auto Diag = diag(Argument->getExprLoc(),
+                   "expression could be wrapped with std::move");
+  Diag << FixItHint::CreateReplacement(Argument->getSourceRange(),
+                                       ReplacementText);
+
+  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+          Result.SourceManager->getFileID(Argument->getLocStart()), "utility",
+          /*IsAngled=*/true)) {
+    Diag << *IncludeFixit;
+  }
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/performance/PerformanceTidyModule.cpp
===================================================================
--- clang-tidy/performance/PerformanceTidyModule.cpp
+++ clang-tidy/performance/PerformanceTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "ReturningTypeCheck.h"
 
 #include "FasterStringFindCheck.h"
 #include "ForRangeCopyCheck.h"
@@ -30,6 +31,8 @@
         "performance-for-range-copy");
     CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
         "performance-implicit-cast-in-loop");
+    CheckFactories.registerCheck<ReturningTypeCheck>(
+        "performance-returning-type");
     CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
         "performance-unnecessary-copy-initialization");
     CheckFactories.registerCheck<UnnecessaryValueParamCheck>(
Index: clang-tidy/performance/CMakeLists.txt
===================================================================
--- clang-tidy/performance/CMakeLists.txt
+++ clang-tidy/performance/CMakeLists.txt
@@ -5,6 +5,7 @@
   ForRangeCopyCheck.cpp
   ImplicitCastInLoopCheck.cpp
   PerformanceTidyModule.cpp
+  ReturningTypeCheck.cpp
   UnnecessaryCopyInitialization.cpp
   UnnecessaryValueParamCheck.cpp
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to