Skip to content

Commit 12d7500

Browse files
author
Mitchell Balan
committed
[clang-tidy] Give readability-redundant-string-init a customizable list of string types to fix
Summary: This patch adds a feature requested in https://reviews.llvm.org/D69238 to enable `readability-redundant-string-init` to take a list of strings to apply the fix to rather than hard-coding `basic_string`. It adds a `StringNames` option of semicolon-delimited names of string classes to which to apply this fix. Tests ensure this works with test class out::TestString as well as std::string and std::wstring as before. It should be applicable to llvm::StringRef, QString, etc. Note: This commit was previously reverted due to a failing unit test. That test has been fixed in this version. Reviewers: MyDeveloperDay, aaron.ballman, hokein, alexfh, JonasToth, gribozavr2 Patch by: poelmanc Subscribers: gribozavr2, xazax.hun, Eugene.Zelenko, cfe-commits Tags: #clang-tools-extra, #clang Differential Revision: https://reviews.llvm.org/D69548
1 parent 06f3dab commit 12d7500

File tree

5 files changed

+144
-13
lines changed

5 files changed

+144
-13
lines changed

clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.cpp

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "RedundantStringInitCheck.h"
1010
#include "../utils/Matchers.h"
11+
#include "../utils/OptionsUtils.h"
1112
#include "clang/ASTMatchers/ASTMatchers.h"
1213

1314
using namespace clang::ast_matchers;
@@ -17,19 +18,43 @@ namespace clang {
1718
namespace tidy {
1819
namespace readability {
1920

21+
const char DefaultStringNames[] = "::std::basic_string";
22+
23+
RedundantStringInitCheck::RedundantStringInitCheck(StringRef Name,
24+
ClangTidyContext *Context)
25+
: ClangTidyCheck(Name, Context),
26+
StringNames(utils::options::parseStringList(
27+
Options.get("StringNames", DefaultStringNames))) {}
28+
29+
void RedundantStringInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
30+
Options.store(Opts, "StringNames", DefaultStringNames);
31+
}
32+
2033
void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
2134
if (!getLangOpts().CPlusPlus)
2235
return;
36+
const auto hasStringTypeName = hasAnyName(
37+
SmallVector<StringRef, 3>(StringNames.begin(), StringNames.end()));
38+
39+
// Version of StringNames with namespaces removed
40+
std::vector<std::string> stringNamesNoNamespace;
41+
for (const std::string &name : StringNames) {
42+
std::string::size_type colonPos = name.rfind(':');
43+
stringNamesNoNamespace.push_back(
44+
name.substr(colonPos == std::string::npos ? 0 : colonPos + 1));
45+
}
46+
const auto hasStringCtorName = hasAnyName(SmallVector<StringRef, 3>(
47+
stringNamesNoNamespace.begin(), stringNamesNoNamespace.end()));
2348

2449
// Match string constructor.
25-
const auto StringConstructorExpr = expr(anyOf(
26-
cxxConstructExpr(argumentCountIs(1),
27-
hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
28-
// If present, the second argument is the alloc object which must not
29-
// be present explicitly.
30-
cxxConstructExpr(argumentCountIs(2),
31-
hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
32-
hasArgument(1, cxxDefaultArgExpr()))));
50+
const auto StringConstructorExpr = expr(
51+
anyOf(cxxConstructExpr(argumentCountIs(1),
52+
hasDeclaration(cxxMethodDecl(hasStringCtorName))),
53+
// If present, the second argument is the alloc object which must
54+
// not be present explicitly.
55+
cxxConstructExpr(argumentCountIs(2),
56+
hasDeclaration(cxxMethodDecl(hasStringCtorName)),
57+
hasArgument(1, cxxDefaultArgExpr()))));
3358

3459
// Match a string constructor expression with an empty string literal.
3560
const auto EmptyStringCtorExpr = cxxConstructExpr(
@@ -48,7 +73,7 @@ void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
4873
namedDecl(
4974
varDecl(
5075
hasType(hasUnqualifiedDesugaredType(recordType(
51-
hasDeclaration(cxxRecordDecl(hasName("basic_string")))))),
76+
hasDeclaration(cxxRecordDecl(hasStringTypeName))))),
5277
hasInitializer(expr(ignoringImplicit(anyOf(
5378
EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries)))))
5479
.bind("vardecl"),

clang-tools-extra/clang-tidy/readability/RedundantStringInitCheck.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
1111

1212
#include "../ClangTidyCheck.h"
13+
#include <string>
14+
#include <vector>
1315

1416
namespace clang {
1517
namespace tidy {
@@ -18,10 +20,13 @@ namespace readability {
1820
/// Finds unnecessary string initializations.
1921
class RedundantStringInitCheck : public ClangTidyCheck {
2022
public:
21-
RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context)
22-
: ClangTidyCheck(Name, Context) {}
23+
RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context);
24+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
2325
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2426
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
27+
28+
private:
29+
std::vector<std::string> StringNames;
2530
};
2631

2732
} // namespace readability

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ Improvements to clang-tidy
170170
The check now supports the ``AllowOverrideAndFinal`` option to eliminate
171171
conflicts with ``gcc -Wsuggest-override`` or ``gcc -Werror=suggest-override``.
172172

173+
- The :doc:`readability-redundant-string-init
174+
<clang-tidy/checks/readability-redundant-string-init>` check now supports a
175+
`StringNames` option enabling its application to custom string classes.
176+
173177
Improvements to include-fixer
174178
-----------------------------
175179

clang-tools-extra/docs/clang-tidy/checks/readability-redundant-string-init.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ readability-redundant-string-init
55

66
Finds unnecessary string initializations.
77

8-
Examples:
8+
Examples
9+
--------
910

1011
.. code-block:: c++
1112

@@ -17,3 +18,15 @@ Examples:
1718

1819
std::string a;
1920
std::string b;
21+
22+
Options
23+
-------
24+
25+
.. option:: StringNames
26+
27+
Default is `::std::basic_string`.
28+
29+
Semicolon-delimited list of class names to apply this check to.
30+
By default `::std::basic_string` applies to ``std::string`` and
31+
``std::wstring``. Set to e.g. `::std::basic_string;llvm::StringRef;QString`
32+
to perform this check on custom classes.

clang-tools-extra/test/clang-tidy/checkers/readability-redundant-string-init.cpp

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
// RUN: %check_clang_tidy %s readability-redundant-string-init %t
1+
// RUN: %check_clang_tidy -std=c++11,c++14 %s readability-redundant-string-init %t \
2+
// RUN: -config="{CheckOptions: \
3+
// RUN: [{key: readability-redundant-string-init.StringNames, \
4+
// RUN: value: '::std::basic_string;our::TestString'}] \
5+
// RUN: }"
6+
// FIXME: Fix the checker to work in C++17 mode.
27

38
namespace std {
49
template <typename T>
@@ -143,3 +148,82 @@ extern void Param2(const std::string& param = "");
143148
void Param3(std::string param = "") {}
144149
void Param4(STRING param = "") {}
145150

151+
namespace our {
152+
struct TestString {
153+
TestString();
154+
TestString(const TestString &);
155+
TestString(const char *);
156+
~TestString();
157+
};
158+
}
159+
160+
void ourTestStringTests() {
161+
our::TestString a = "";
162+
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: redundant string initialization
163+
// CHECK-FIXES: our::TestString a;
164+
our::TestString b("");
165+
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: redundant string initialization
166+
// CHECK-FIXES: our::TestString b;
167+
our::TestString c = R"()";
168+
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: redundant string initialization
169+
// CHECK-FIXES: our::TestString c;
170+
our::TestString d(R"()");
171+
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: redundant string initialization
172+
// CHECK-FIXES: our::TestString d;
173+
174+
our::TestString u = "u";
175+
our::TestString w("w");
176+
our::TestString x = R"(x)";
177+
our::TestString y(R"(y)");
178+
our::TestString z;
179+
}
180+
181+
namespace their {
182+
using TestString = our::TestString;
183+
}
184+
185+
// their::TestString is the same type so should warn / fix
186+
void theirTestStringTests() {
187+
their::TestString a = "";
188+
// CHECK-MESSAGES: [[@LINE-1]]:21: warning: redundant string initialization
189+
// CHECK-FIXES: their::TestString a;
190+
their::TestString b("");
191+
// CHECK-MESSAGES: [[@LINE-1]]:21: warning: redundant string initialization
192+
// CHECK-FIXES: their::TestString b;
193+
}
194+
195+
namespace other {
196+
// Identical declarations to above but different type
197+
struct TestString {
198+
TestString();
199+
TestString(const TestString &);
200+
TestString(const char *);
201+
~TestString();
202+
};
203+
204+
// Identical declarations to above but different type
205+
template <typename T>
206+
class allocator {};
207+
template <typename T>
208+
class char_traits {};
209+
template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
210+
struct basic_string {
211+
basic_string();
212+
basic_string(const basic_string &);
213+
basic_string(const C *, const A &a = A());
214+
~basic_string();
215+
};
216+
typedef basic_string<char> string;
217+
typedef basic_string<wchar_t> wstring;
218+
}
219+
220+
// other::TestString, other::string, other::wstring are unrelated to the types
221+
// being checked. No warnings / fixes should be produced for these types.
222+
void otherTestStringTests() {
223+
other::TestString a = "";
224+
other::TestString b("");
225+
other::string c = "";
226+
other::string d("");
227+
other::wstring e = L"";
228+
other::wstring f(L"");
229+
}

0 commit comments

Comments
 (0)