[clang-tidy] Fix readability-redundant-string-init for c++17/c++2a

Summary:
`readability-redundant-string-init` was one of several clang-tidy checks documented as failing for C++17. (The failure mode in C++17 is that it changes `std::string Name = ""`; to `std::string Name = Name;`, which actually compiles but crashes at run-time.)

Analyzing the AST with `clang -Xclang -ast-dump` showed that the outer `CXXConstructExprs` that previously held the correct SourceRange were being elided in C++17/2a, but the containing `VarDecl` expressions still had all the relevant information. So this patch changes the fix to get its source ranges from `VarDecl`.

It adds one test `std::string g = "u", h = "", i = "uuu", j = "", k;` to confirm proper warnings and fixit replacements in a single `DeclStmt` where some strings require replacement and others don't. The readability-redundant-string-init.cpp and readability-redundant-string-init-msvc.cpp tests now pass for C++11/14/17/2a.

Reviewers: gribozavr, etienneb, alexfh, hokein, aaron.ballman, gribozavr2

Patch by: poelmanc

Subscribers: NoQ, MyDeveloperDay, Eugene.Zelenko, dylanmckay, cfe-commits

Tags: #clang, #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D69238
This commit is contained in:
Mitchell Balan 2019-11-19 07:45:53 -05:00
parent f8901aff4a
commit 1315f4e009
3 changed files with 18 additions and 9 deletions

View File

@ -73,7 +73,7 @@ void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
namedDecl(
varDecl(
hasType(hasUnqualifiedDesugaredType(recordType(
hasDeclaration(cxxRecordDecl(hasName("basic_string")))))),
hasDeclaration(cxxRecordDecl(hasStringTypeName))))),
hasInitializer(expr(ignoringImplicit(anyOf(
EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries)))))
.bind("vardecl"),
@ -82,11 +82,12 @@ void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
}
void RedundantStringInitCheck::check(const MatchFinder::MatchResult &Result) {
const auto *CtorExpr = Result.Nodes.getNodeAs<Expr>("expr");
const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl");
diag(CtorExpr->getExprLoc(), "redundant string initialization")
<< FixItHint::CreateReplacement(CtorExpr->getSourceRange(),
Decl->getName());
const auto *VDecl = Result.Nodes.getNodeAs<VarDecl>("vardecl");
// VarDecl's getSourceRange() spans 'string foo = ""' or 'string bar("")'.
// So start at getLocation() to span just 'foo = ""' or 'bar("")'.
SourceRange ReplaceRange(VDecl->getLocation(), VDecl->getEndLoc());
diag(VDecl->getLocation(), "redundant string initialization")
<< FixItHint::CreateReplacement(ReplaceRange, VDecl->getName());
}
} // namespace readability

View File

@ -1,5 +1,4 @@
// RUN: %check_clang_tidy -std=c++11,c++14 %s readability-redundant-string-init %t
// FIXME: Fix the checker to work in C++17 mode.
// RUN: %check_clang_tidy %s readability-redundant-string-init %t
namespace std {
template <typename T>

View File

@ -1,4 +1,8 @@
// RUN: %check_clang_tidy -std=c++11,c++14 %s readability-redundant-string-init %t
// RUN: %check_clang_tidy -std=c++11,c++14 %s readability-redundant-string-init %t \
// RUN: -config="{CheckOptions: \
// RUN: [{key: readability-redundant-string-init.StringNames, \
// RUN: value: '::std::basic_string;our::TestString'}] \
// RUN: }"
// FIXME: Fix the checker to work in C++17 mode.
namespace std {
@ -131,6 +135,11 @@ void k() {
// CHECK-FIXES: std::string a, b, c;
std::string d = "u", e = "u", f = "u";
std::string g = "u", h = "", i = "uuu", j = "", k;
// CHECK-MESSAGES: [[@LINE-1]]:24: warning: redundant string initialization
// CHECK-MESSAGES: [[@LINE-2]]:43: warning: redundant string initialization
// CHECK-FIXES: std::string g = "u", h, i = "uuu", j, k;
}
// These cases should not generate warnings.