[clang-tidy] Fix bugprone-string-constructor FN with allocators. (#180337)

Fixes https://github.com/llvm/llvm-project/issues/180324.
This commit is contained in:
Baranov Victor 2026-02-08 12:18:42 +03:00 committed by GitHub
parent 706cc8b3fe
commit aff5afc48d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 6 deletions

View File

@ -81,7 +81,8 @@ void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxConstructExpr(
hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
argumentCountIs(2), hasArgument(0, hasType(qualType(isInteger()))),
anyOf(argumentCountIs(2), argumentCountIs(3)),
hasArgument(0, hasType(qualType(isInteger()))),
hasArgument(1, hasType(qualType(isInteger()))),
anyOf(
// Detect the expression: string('x', 40);
@ -101,7 +102,10 @@ void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(ofClass(
cxxRecordDecl(hasAnyName(removeNamespaces(StringNames)))))),
argumentCountIs(2), hasArgument(0, hasType(CharPtrType)),
anyOf(argumentCountIs(2),
allOf(argumentCountIs(3),
hasArgument(2, unless(hasType(qualType(isInteger())))))),
hasArgument(0, hasType(CharPtrType)),
hasArgument(1, hasType(isInteger())),
anyOf(
// Detect the expression: string("...", 0);
@ -123,7 +127,8 @@ void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(ofClass(
cxxRecordDecl(hasAnyName(removeNamespaces(StringNames)))))),
argumentCountIs(3), hasArgument(0, hasType(CharPtrType)),
anyOf(argumentCountIs(3), argumentCountIs(4)),
hasArgument(0, hasType(CharPtrType)),
hasArgument(1, hasType(qualType(isInteger()))),
hasArgument(2, hasType(qualType(isInteger()))),
anyOf(

View File

@ -148,6 +148,11 @@ Changes in existing checks
<clang-tidy/checks/bugprone/macro-parentheses>` check by printing the macro
definition in the warning message if the macro is defined on command line.
- Improved :doc:`bugprone-string-constructor
<clang-tidy/checks/bugprone/string-constructor>` check to detect suspicious
string constructor calls when the string class constructor has a default
allocator argument.
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check by adding the function
``std::get_temporary_buffer`` to the default list of unsafe functions. (This

View File

@ -8,10 +8,10 @@ class char_traits {};
template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C> >
struct basic_string {
basic_string();
basic_string(const C*, unsigned int size);
basic_string(const C*, unsigned int size, const A &a = A());
basic_string(const C *, const A &allocator = A());
basic_string(unsigned int size, C c);
basic_string(const C*, unsigned int pos, unsigned int size);
basic_string(unsigned int size, C c, const A &a = A());
basic_string(const C*, unsigned int pos, unsigned int size, const A &a = A());
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
@ -119,6 +119,44 @@ std::string_view StringViewFromZero() {
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: constructing string from nullptr is undefined behaviour
}
void TestExplicitAllocator() {
std::allocator<char> a;
std::string s1('x', 5, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: string constructor parameters are probably swapped; expecting string(count, character) [bugprone-string-constructor]
// CHECK-FIXES: std::string s1(5, 'x', a);
std::string s2(0, 'x', a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
std::string s3(-4, 'x', a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
std::string s4(0x1000000, 'x', a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
std::string q0("test", 0, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
std::string q1(kText, -4, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
std::string q2("test", 200, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than string literal size
std::string q3(kText3, 0x1000000, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
std::string r1("test", 1, 0, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
std::string r2("test", 0, -4, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
std::string r3("test", -4, 1, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as position of the first character parameter
std::string r4("test", 0, 0x1000000, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
std::string r5("test", 0, 5, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than string literal size
std::string r6("test", 3, 2, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than remaining string literal size
std::string r7("test", 4, 1, a);
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: position of the first character parameter is bigger than string literal character range
}
void Valid() {
std::string empty();
std::string str(4, 'x');
@ -132,6 +170,13 @@ void Valid() {
std::string s8("test", 3, 1);
std::string s9("te" "st", 1, 2);
std::allocator<char> a;
std::string sa1(4, 'x', a);
std::string sa2("test", 4, a);
std::string sa3("test", 3, a);
std::string sa4("test", 0, 4, a);
std::string sa5("test", 3, 1, a);
std::string_view emptyv();
std::string_view sv1("test", 4);
std::string_view sv2("test", 3);