Index: clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.h =================================================================== --- clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.h +++ clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.h @@ -11,6 +11,9 @@ #include "../ClangTidyCheck.h" +#include +#include + namespace clang { namespace tidy { namespace readability { @@ -18,10 +21,13 @@ /// Finds unnecessary string initializations. class RedundantStringInitCheck : public ClangTidyCheck { public: - RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + std::vector StringNames; }; } // namespace readability Index: clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.cpp +++ clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.cpp @@ -8,6 +8,7 @@ #include "RedundantStringInitCheck.h" #include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" #include "clang/ASTMatchers/ASTMatchers.h" using namespace clang::ast_matchers; @@ -17,19 +18,32 @@ namespace tidy { namespace readability { +const char DefaultStringNames[] = "basic_string"; + +RedundantStringInitCheck::RedundantStringInitCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + StringNames(utils::options::parseStringList( + Options.get("StringNames", DefaultStringNames))) {} + +void RedundantStringInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "StringNames", DefaultStringNames); +} + void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; - // Match string constructor. - const auto StringConstructorExpr = expr(anyOf( - cxxConstructExpr(argumentCountIs(1), - hasDeclaration(cxxMethodDecl(hasName("basic_string")))), - // If present, the second argument is the alloc object which must not - // be present explicitly. - cxxConstructExpr(argumentCountIs(2), - hasDeclaration(cxxMethodDecl(hasName("basic_string"))), - hasArgument(1, cxxDefaultArgExpr())))); + const auto StringConstructorExpr = + expr(anyOf(cxxConstructExpr( + argumentCountIs(1), + hasDeclaration(cxxMethodDecl(hasListedName(StringNames)))), + // If present, the second argument is the alloc object which + // must not be present explicitly. + cxxConstructExpr( + argumentCountIs(2), + hasDeclaration(cxxMethodDecl(hasListedName(StringNames))), + hasArgument(1, cxxDefaultArgExpr())))); // Match a string constructor expression with an empty string literal. const auto EmptyStringCtorExpr = cxxConstructExpr( @@ -46,8 +60,8 @@ // string bar(""); Finder->addMatcher( namedDecl( - varDecl(hasType(hasUnqualifiedDesugaredType(recordType( - hasDeclaration(cxxRecordDecl(hasName("basic_string")))))), + varDecl(hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( + cxxRecordDecl(hasListedName(StringNames)))))), hasInitializer(expr(ignoringImplicit(anyOf( EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries))) Index: clang-tools-extra/docs/clang-tidy/checks/readability-redundant-string-init.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/readability-redundant-string-init.rst +++ clang-tools-extra/docs/clang-tidy/checks/readability-redundant-string-init.rst @@ -5,7 +5,8 @@ Finds unnecessary string initializations. -Examples: +Examples +-------- .. code-block:: c++ @@ -17,3 +18,16 @@ std::string a; std::string b; + +Options +------- + +.. option:: StringNames + + Default is ``basic_string``. + + Semicolon-delimited list of base class names to apply this check to. + By default ``basic_string`` applies to std::string and std::wstring. + Set to e.g. ``basic_string;StringRef;QString`` to perform this check on + custom classes as well. + Index: clang-tools-extra/test/clang-tidy/checkers/readability-redundant-string-init.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/readability-redundant-string-init.cpp +++ clang-tools-extra/test/clang-tidy/checkers/readability-redundant-string-init.cpp @@ -1,4 +1,8 @@ // RUN: %check_clang_tidy %s readability-redundant-string-init %t +// RUN: -config="{CheckOptions: \ +// RUN: [{key: readability-redundant-string-init.StringNames, \ +// RUN: value: "basic_string;OurTestString"}] \ +// RUN: }" namespace std { template @@ -139,3 +143,31 @@ void Param3(std::string param = "") {} void Param4(STRING param = "") {} +struct OurTestString { + OurTestString(); + OurTestString(const OurTestString &); + // MSVC headers define two constructors instead of using optional arguments. + OurTestString(const char *); + ~OurTestString(); +}; + +void OurTestStringTests() { + OurTestString a = ""; + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: redundant string initialization + // CHECK-FIXES: OurTestString a; + OurTestString b(""); + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: redundant string initialization + // CHECK-FIXES: OurTestString b; + OurTestString c = R"()"; + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: redundant string initialization + // CHECK-FIXES: OurTestString c; + OurTestString d(R"()"); + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: redundant string initialization + // CHECK-FIXES: OurTestString d; + + OurTestString u = "u"; + OurTestString w("w"); + OurTestString x = R"(x)"; + OurTestString y(R"(y)"); + OurTestString z; +} Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2551,6 +2551,19 @@ internal::hasAnyNameFunc> hasAnyName; +/// Matches NamedDecl nodes that have any of the specified names. +/// +/// \code +/// hasListedName({a, b, c,...}) +/// \endcode +/// is equivalent to +/// \code +/// anyOf(hasName(a), hasName(b), hasName(c),...) +/// \endcode +inline internal::Matcher hasListedName(const std::vector &Names) { + return internal::Matcher(new internal::HasNameMatcher(Names)); +} + /// Matches NamedDecl nodes whose fully qualified names contain /// a substring matched by the given RegExp. ///