Index: clang-tidy/boost/BoostTidyModule.cpp =================================================================== --- clang-tidy/boost/BoostTidyModule.cpp +++ clang-tidy/boost/BoostTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "UseToStringCheck.h" using namespace clang::ast_matchers; namespace clang { @@ -19,6 +20,7 @@ class BoostModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck("boost-use-to-string"); } ClangTidyOptions getModuleOptions() override { @@ -29,7 +31,7 @@ // Register the BoostModule using this statically initialized variable. static ClangTidyModuleRegistry::Add X("boost-module", - "Add boost checks."); + "Add boost checks."); } // namespace boost Index: clang-tidy/boost/CMakeLists.txt =================================================================== --- clang-tidy/boost/CMakeLists.txt +++ clang-tidy/boost/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_library(clangTidyBoostModule BoostTidyModule.cpp + UseToStringCheck.cpp LINK_LIBS clangAST Index: clang-tidy/boost/UseToStringCheck.h =================================================================== --- /dev/null +++ clang-tidy/boost/UseToStringCheck.h @@ -0,0 +1,39 @@ +//===--- UseToStringCheck.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_BOOST_USE_TO_STRING_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace boost { + +/// Matches calls to boost::lexical_cast and +/// boost::lexical_cast and replaces it with to_string and +/// to_wstring calls. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/boost-use-to-string.html +class UseToStringCheck : public ClangTidyCheck { +public: + UseToStringCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void apply(const CallExpr *MatchedExpr, const std::string &message, + const std::string &replacement); +}; + +} // namespace boost +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H Index: clang-tidy/boost/UseToStringCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/boost/UseToStringCheck.cpp @@ -0,0 +1,78 @@ +//===--- UseToStringCheck.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 "UseToStringCheck.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace boost { + +AST_MATCHER(Type, isStrictlyInteger) { + return Node.isIntegerType() && !Node.isAnyCharacterType() && + !Node.isBooleanType(); +} + +void UseToStringCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + callExpr( + hasDeclaration(functionDecl( + returns(hasDeclaration(classTemplateSpecializationDecl( + hasName("std::basic_string"), + hasTemplateArgument(0, + templateArgument().bind("char_type"))))), + hasName("boost::lexical_cast"), + hasParameter(0, hasType(qualType(has( + substTemplateTypeParmType(isStrictlyInteger()) + .bind("t"))))))), + argumentCountIs(1)) //, unless(isInTemplateInstantiation())) + .bind("to_string"), + this); +} + +void UseToStringCheck::apply(const CallExpr *MatchedExpr, + const std::string &message, + const std::string &replacement) { + auto *Argument = MatchedExpr->getArg(0); + + diag(MatchedExpr->getLocStart(), message) << FixItHint::CreateReplacement( + CharSourceRange::getCharRange(MatchedExpr->getLocStart(), + Argument->getExprLoc()), + replacement); +} + +void UseToStringCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedToString = Result.Nodes.getNodeAs("to_string"); + auto Q = Result.Nodes.getNodeAs("char_type")->getAsType(); + + if (Q.isNull()) + return; + + if (Q->isSpecificBuiltinType(BuiltinType::Char_S) || + Q->isSpecificBuiltinType(BuiltinType::Char_U)) { + apply(MatchedToString, + "use std::to_string instead of boost::lexical_cast", + "std::to_string("); + } + // Is Q 'wchar_t' + else if (Q->isSpecificBuiltinType(BuiltinType::WChar_S) || + Q->isSpecificBuiltinType(BuiltinType::WChar_U)) { + apply(MatchedToString, + "use std::to_wstring instead of boost::lexical_cast", + "std::to_wstring("); + } +} + +} // namespace boost +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/boost-use-to-string.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/boost-use-to-string.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - boost-use-to-string + +boost-use-to-string +=================== + +This check finds conversion from integer type like int to std::string or +std::wstring using boost::lexical_cast, and replace it to calls of +std::to_string and std::to_wstring. + +It doesn't replace conversion from floating points despite the to_string +overloads, because it would change the behaviour. + + + .. code-block:: c++ + - auto str = boost::lexical_cast(42); + + auto str = std::to_string(42); + + - auto wstr = boost::lexical_cast(2137LL); + + auto wstr = std::to_wstring(2137LL); + Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -4,6 +4,7 @@ ========================= .. toctree:: + boost-use-to-string cert-dcl03-c (redirects to misc-static-assert) cert-dcl50-cpp cert-dcl54-cpp (redirects to misc-new-delete-overloads) Index: test/clang-tidy/boost-use-to-string.cpp =================================================================== --- /dev/null +++ test/clang-tidy/boost-use-to-string.cpp @@ -0,0 +1,138 @@ +// RUN: %check_clang_tidy %s boost-use-to-string %t + + +namespace std { + +template class basic_string {}; + +using string = basic_string; +using wstring = basic_string; +} + +namespace boost { +template +T lexical_cast(const V&) { + return T(); +}; +} + +struct my_weird_type {}; + +std::string fun(const std::string &) {} + +void test_to_string1() { + + auto xa = boost::lexical_cast(5); +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string instead of boost::lexical_cast [boost-use-to-string] +// CHECK-FIXES: auto xa = std::to_string(5); + + auto z = boost::lexical_cast(42LL); +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use std::to_string {{..}} +// CHECK-FIXES: auto z = std::to_string(42LL); + + // this should not trigger + fun(boost::lexical_cast(42.0)); + auto non = boost::lexical_cast(42); + boost::lexical_cast("12"); +} + +void test_to_string2() { + int a; + long b; + long long c; + unsigned d; + unsigned long e; + unsigned long long f; + float g; + double h; + long double i; + bool j; + + fun(boost::lexical_cast(a)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string {{..}} +// CHECK-FIXES: fun(std::to_string(a)); + fun(boost::lexical_cast(b)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string {{..}} +// CHECK-FIXES: fun(std::to_string(b)); + fun(boost::lexical_cast(c)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string {{..}} +// CHECK-FIXES: fun(std::to_string(c)); + fun(boost::lexical_cast(d)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string {{..}} +// CHECK-FIXES: fun(std::to_string(d)); + fun(boost::lexical_cast(e)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string {{..}} +// CHECK-FIXES: fun(std::to_string(e)); + fun(boost::lexical_cast(f)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string {{..}} +// CHECK-FIXES: fun(std::to_string(f)); + + // No change for floating numbers. + fun(boost::lexical_cast(g)); + fun(boost::lexical_cast(h)); + fun(boost::lexical_cast(i)); + // And bool. + fun(boost::lexical_cast(j)); +} + +std::string fun(const std::wstring &) {} + +void test_to_wstring() { + int a; + long b; + long long c; + unsigned d; + unsigned long e; + unsigned long long f; + float g; + double h; + long double i; + bool j; + + fun(boost::lexical_cast(a)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring instead of boost::lexical_cast [boost-use-to-string] +// CHECK-FIXES: fun(std::to_wstring(a)); + fun(boost::lexical_cast(b)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring {{..}} +// CHECK-FIXES: fun(std::to_wstring(b)); + fun(boost::lexical_cast(c)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring {{..}} +// CHECK-FIXES: fun(std::to_wstring(c)); + fun(boost::lexical_cast(d)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring {{..}} +// CHECK-FIXES: fun(std::to_wstring(d)); + fun(boost::lexical_cast(e)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring {{..}} +// CHECK-FIXES: fun(std::to_wstring(e)); + fun(boost::lexical_cast(f)); +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring {{..}} +// CHECK-FIXES: fun(std::to_wstring(f)); + + // No change for floating numbers + fun(boost::lexical_cast(g)); + fun(boost::lexical_cast(h)); + fun(boost::lexical_cast(i)); + // and bool. + fun(boost::lexical_cast(j)); +} + +template +void string_as_T() { + boost::lexical_cast(42); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use std::to_string{{..}} +// CHECK-FIXES: std::to_string(42); + // FIXME it should not replace this call + //boost::lexical_cast(42); +} + + +void no_warnings() { + fun(boost::lexical_cast("abc")); + fun(boost::lexical_cast("abc")); + fun(boost::lexical_cast(my_weird_type{})); + string_as_T(); + string_as_T(); +} + + +