[clang-tidy] Add support for different char-types for the readability-redundant-string-cstr checker.

Summary:
The current checker is able to recognize std::string but does not recognize other string variants.
This patch is adding the support for any string defined with basic_string without considering the
the underlying char type.

The most common variant is: 'std::wstring' based on 'wchar_t'.

There are also other string variants added to the standard: u16string, u32string, etc...

Reviewers: alexfh

Subscribers: mamai, dblaikie, cfe-commits

Differential Revision: http://reviews.llvm.org/D18412

git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@264325 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Etienne Bergeron 2016-03-24 19:42:36 +00:00
Родитель df5cd1802d
Коммит 18c46d063a
2 изменённых файлов: 64 добавлений и 14 удалений

Просмотреть файл

@ -65,14 +65,6 @@ formatDereference(const ast_matchers::MatchFinder::MatchResult &Result,
return (llvm::Twine("*") + Text).str(); return (llvm::Twine("*") + Text).str();
} }
const char StringConstructor[] =
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
"::basic_string";
const char StringCStrMethod[] =
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
"::c_str";
} // end namespace } // end namespace
namespace tidy { namespace tidy {
@ -85,23 +77,31 @@ void RedundantStringCStrCheck::registerMatchers(
if (!getLangOpts().CPlusPlus) if (!getLangOpts().CPlusPlus)
return; return;
// Match expressions of type 'string' or 'string*'.
const auto StringDecl =
cxxRecordDecl(hasName("::std::basic_string"));
const auto StringExpr =
expr(anyOf(hasType(StringDecl),
hasType(qualType(pointsTo(StringDecl)))));
// Match string constructor. // Match string constructor.
const auto StringConstructorExpr = expr(anyOf( const auto StringConstructorExpr = expr(anyOf(
cxxConstructExpr( cxxConstructExpr(
argumentCountIs(1), argumentCountIs(1),
hasDeclaration(cxxMethodDecl(hasName(StringConstructor)))), hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
cxxConstructExpr( cxxConstructExpr(
argumentCountIs(2), argumentCountIs(2),
hasDeclaration(cxxMethodDecl(hasName(StringConstructor))), hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
// If present, the second argument is the alloc object which must not // If present, the second argument is the alloc object which must not
// be present explicitly. // be present explicitly.
hasArgument(1, cxxDefaultArgExpr())))); hasArgument(1, cxxDefaultArgExpr()))));
// Match a call to the string 'c_str()' method. // Match a call to the string 'c_str()' method.
const auto StringCStrCallExpr = cxxMemberCallExpr( const auto StringCStrCallExpr =
callee(memberExpr().bind("member")), cxxMemberCallExpr(on(StringExpr.bind("arg")),
callee(cxxMethodDecl(hasName(StringCStrMethod))), callee(memberExpr().bind("member")),
on(expr().bind("arg"))).bind("call"); callee(cxxMethodDecl(hasName("c_str"))))
.bind("call");
Finder->addMatcher( Finder->addMatcher(
cxxConstructExpr(StringConstructorExpr, cxxConstructExpr(StringConstructorExpr,

Просмотреть файл

@ -12,7 +12,11 @@ struct basic_string {
const C *c_str() const; const C *c_str() const;
}; };
typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string; typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
typedef basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t>> wstring;
typedef basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t>> u16string;
typedef basic_string<char32_t, std::char_traits<char32_t>, std::allocator<char32_t>> u32string;
} }
namespace llvm { namespace llvm {
struct StringRef { struct StringRef {
StringRef(const char *p); StringRef(const char *p);
@ -20,6 +24,8 @@ struct StringRef {
}; };
} }
// Tests for std::string.
void f1(const std::string &s) { void f1(const std::string &s) {
f1(s.c_str()); f1(s.c_str());
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-redundant-string-cstr] // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-redundant-string-cstr]
@ -39,3 +45,47 @@ void f3(const llvm::StringRef &r) {
// CHECK-FIXES: {{^ }}std::string s;{{$}} // CHECK-FIXES: {{^ }}std::string s;{{$}}
// CHECK-FIXES-NEXT: {{^ }}f3(s);{{$}} // CHECK-FIXES-NEXT: {{^ }}f3(s);{{$}}
} }
void f4(const std::string &s) {
const std::string* ptr = &s;
f1(ptr->c_str());
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-redundant-string-cstr]
// CHECK-FIXES: {{^ }}f1(*ptr);{{$}}
}
// Tests for std::wstring.
void g1(const std::wstring &s) {
g1(s.c_str());
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-redundant-string-cstr]
// CHECK-FIXES: {{^ }}g1(s);{{$}}
}
// Tests for std::u16string.
void h1(const std::u16string &s) {
h1(s.c_str());
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-redundant-string-cstr]
// CHECK-FIXES: {{^ }}h1(s);{{$}}
}
// Tests for std::u32string.
void k1(const std::u32string &s) {
k1(s.c_str());
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-redundant-string-cstr]
// CHECK-FIXES: {{^ }}k1(s);{{$}}
}
// Tests on similar classes that aren't good candidates for this checker.
struct NotAString {
NotAString();
NotAString(const NotAString&);
const char *c_str() const;
};
void dummy(const char*) {}
void invalid(const NotAString &s) {
dummy(s.c_str());
}