Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -10,6 +10,7 @@ MacroParenthesesCheck.cpp MacroRepeatedSideEffectsCheck.cpp MiscTidyModule.cpp + MoveConstantArgumentCheck.cpp NoexceptMoveConstructorCheck.cpp StaticAssertCheck.cpp SwappedArgumentsCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -18,6 +18,7 @@ #include "InefficientAlgorithmCheck.h" #include "MacroParenthesesCheck.h" #include "MacroRepeatedSideEffectsCheck.h" +#include "MoveConstantArgumentCheck.h" #include "NoexceptMoveConstructorCheck.h" #include "StaticAssertCheck.h" #include "SwappedArgumentsCheck.h" @@ -66,6 +67,7 @@ "misc-unused-parameters"); CheckFactories.registerCheck("misc-unused-raii"); CheckFactories.registerCheck("misc-use-override"); + CheckFactories.registerCheck("move-const-arg"); } }; Index: clang-tidy/misc/MoveConstantArgumentCheck.h =================================================================== --- clang-tidy/misc/MoveConstantArgumentCheck.h +++ clang-tidy/misc/MoveConstantArgumentCheck.h @@ -0,0 +1,17 @@ +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +class MoveConstantArgumentCheck : public ClangTidyCheck { +public: + MoveConstantArgumentCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MoveConstantArgumentCheck.cpp =================================================================== --- clang-tidy/misc/MoveConstantArgumentCheck.cpp +++ clang-tidy/misc/MoveConstantArgumentCheck.cpp @@ -0,0 +1,38 @@ +#include "MoveConstantArgumentCheck.h" + +#include +using namespace std; + +namespace clang { +namespace tidy { +namespace misc { + +using namespace ast_matchers; + +void MoveConstantArgumentCheck::registerMatchers(MatchFinder* Finder) { + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("::std::move")))).bind("call-move"), + this); +} + +void MoveConstantArgumentCheck::check(const MatchFinder::MatchResult& result) { + const auto* CallMove = result.Nodes.getNodeAs("call-move"); + if (CallMove->getNumArgs() != 1) return; + const Expr* Arg = CallMove->getArg(0); + + if (Arg->getType().isConstQualified()) { + SourceManager* sm = result.SourceManager; + SourceRange MoveRange(CallMove->getLocStart(), CallMove->getRParenLoc()); + clang::SourceLocation ArgBegin(Arg->getLocStart()), + ArgEnd(Arg->getLocEnd()); + int length = + sm->getCharacterData(ArgEnd) - sm->getCharacterData(ArgBegin) + 1; + std::string ArgString(sm->getCharacterData(ArgBegin), length); + diag(CallMove->getLocStart(), "move of const variable") + << FixItHint::CreateReplacement(MoveRange, ArgString); + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: test/clang-tidy/move-const-arg.cpp =================================================================== --- test/clang-tidy/move-const-arg.cpp +++ test/clang-tidy/move-const-arg.cpp @@ -0,0 +1,95 @@ +// RUN: $(dirname %s)/check_clang_tidy.sh %s move-const-arg %t +// REQUIRES: shell + +namespace std { +// Directly copied from the stl. +template +struct remove_reference; + +template + struct remove_reference + { typedef _Tp type; }; + +template + struct remove_reference<_Tp&> + { typedef _Tp type; }; + +template + struct remove_reference<_Tp&&> + { typedef _Tp type; }; + +template + constexpr typename std::remove_reference<_Tp>::type&& + move(_Tp&& __t); + +} // namespace std + +int f1() +{ + return std::move(42); +} + +int f2(int x) +{ + return std::move(x); +} + +int f3(const int x) +{ + return std::move(x); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: move of const variable [move-const-arg] + // CHECK-FIXES: return x; +} + +int f4(int& x) +{ + return std::move(x); +} + +int f5(const int& x) +{ + return std::move(x); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: move of const variable [move-const-arg] + // CHECK-FIXES: return x; +} + +int f6(int* x) +{ + return std::move(*x); +} + +int f7(const int* x) +{ + return std::move(*x); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: move of const variable [move-const-arg] + // CHECK-FIXES: return *x; +} + +int f8(int&& x) +{ + return std::move(x); +} + +int f9(const int&& x) +{ + return std::move(x); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: move of const variable [move-const-arg] + // CHECK-FIXES: return x; +} + +int f10(const int* x) +{ + return std::move(*const_cast(x)); +} + +const int& f11() +{ + return std::move(f1()); +} + +int f12() +{ + return std::move(f11()); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: move of const variable [move-const-arg] + // CHECK-FIXES: return f11(); +}