Skip to content

Commit 0e62d5c

Browse files
authored
[clang-tidy] Fix assert in modernize-use-std-format/print (#94104)
Ensure that FormatStringConverter's constructor fails with a sensible error message rather than asserting if the format string is not a narrow string literal. Also, ensure that we don't even get that far in modernize-use-std-print and modernize-use-std-format by checking that the format string parameter is a char pointer. Fixes #92896
1 parent 7bc7672 commit 0e62d5c

File tree

7 files changed

+70
-13
lines changed

7 files changed

+70
-13
lines changed

clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
4747
}
4848

4949
void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
50+
auto CharPointerType =
51+
hasType(pointerType(pointee(matchers::isSimpleChar())));
5052
Finder->addMatcher(
51-
callExpr(argumentCountAtLeast(1),
52-
hasArgument(0, stringLiteral(isOrdinary())),
53-
callee(functionDecl(unless(cxxMethodDecl()),
54-
matchers::matchesAnyListedName(
55-
StrFormatLikeFunctions))
56-
.bind("func_decl")))
53+
callExpr(
54+
argumentCountAtLeast(1), hasArgument(0, stringLiteral(isOrdinary())),
55+
callee(functionDecl(
56+
unless(cxxMethodDecl()), hasParameter(0, CharPointerType),
57+
matchers::matchesAnyListedName(StrFormatLikeFunctions))
58+
.bind("func_decl")))
5759
.bind("strformat"),
5860
this);
5961
}

clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,15 @@ unusedReturnValue(clang::ast_matchers::StatementMatcher MatchedCallExpr) {
9595
}
9696

9797
void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
98+
auto CharPointerType =
99+
hasType(pointerType(pointee(matchers::isSimpleChar())));
98100
if (!PrintfLikeFunctions.empty())
99101
Finder->addMatcher(
100102
unusedReturnValue(
101103
callExpr(argumentCountAtLeast(1),
102104
hasArgument(0, stringLiteral(isOrdinary())),
103105
callee(functionDecl(unless(cxxMethodDecl()),
106+
hasParameter(0, CharPointerType),
104107
matchers::matchesAnyListedName(
105108
PrintfLikeFunctions))
106109
.bind("func_decl")))
@@ -113,6 +116,7 @@ void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
113116
callExpr(argumentCountAtLeast(2),
114117
hasArgument(1, stringLiteral(isOrdinary())),
115118
callee(functionDecl(unless(cxxMethodDecl()),
119+
hasParameter(1, CharPointerType),
116120
matchers::matchesAnyListedName(
117121
FprintfLikeFunctions))
118122
.bind("func_decl")))

clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,11 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
208208
assert(ArgsOffset <= NumArgs);
209209
FormatExpr = llvm::dyn_cast<StringLiteral>(
210210
Args[FormatArgOffset]->IgnoreImplicitAsWritten());
211-
assert(FormatExpr);
212-
if (!FormatExpr->isOrdinary())
213-
return; // No wide string support yet
211+
if (!FormatExpr || !FormatExpr->isOrdinary()) {
212+
// Function must have a narrow string literal as its first argument.
213+
conversionNotPossible("first argument is not a narrow string literal");
214+
return;
215+
}
214216
PrintfFormatString = FormatExpr->getString();
215217

216218
// Assume that the output will be approximately the same size as the input,

clang-tools-extra/clang-tidy/utils/Matchers.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isPointerToConst) {
4949
return pointerType(pointee(qualType(isConstQualified())));
5050
}
5151

52+
// Returns QualType matcher for target char type only.
53+
AST_MATCHER(QualType, isSimpleChar) {
54+
const auto ActualType = Node.getTypePtr();
55+
return ActualType &&
56+
(ActualType->isSpecificBuiltinType(BuiltinType::Char_S) ||
57+
ActualType->isSpecificBuiltinType(BuiltinType::Char_U));
58+
}
59+
5260
AST_MATCHER(Expr, hasUnevaluatedContext) {
5361
if (isa<CXXNoexceptExpr>(Node) || isa<RequiresExpr>(Node))
5462
return true;

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,11 @@ Changes in existing checks
351351
<clang-tidy/checks/modernize/use-starts-ends-with>` check to also handle
352352
calls to ``compare`` method.
353353

354+
- Improved :doc:`modernize-use-std-print
355+
<clang-tidy/checks/modernize/use-std-print>` check to not crash if the
356+
format string parameter of the function to be replaced is not of the
357+
expected type.
358+
354359
- Improved :doc:`modernize-use-using <clang-tidy/checks/modernize/use-using>`
355360
check by adding support for detection of typedefs declared on function level.
356361

clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
33
// RUN: -config="{CheckOptions: { \
44
// RUN: modernize-use-std-format.StrictMode: true, \
5-
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
5+
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2; bad_format_type_strprintf', \
66
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
77
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
88
// RUN: }}" \
99
// RUN: -- -isystem %clang_tidy_headers
1010
// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \
1111
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
1212
// RUN: -config="{CheckOptions: { \
13-
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
13+
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2; bad_format_type_strprintf', \
1414
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
1515
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
1616
// RUN: }}" \
@@ -50,3 +50,17 @@ std::string A(const std::string &in)
5050
{
5151
return "_" + in;
5252
}
53+
54+
// Issue #92896: Ensure that the check doesn't assert if the argument is
55+
// promoted to something that isn't a string.
56+
struct S {
57+
S(...);
58+
};
59+
std::string bad_format_type_strprintf(const S &, ...);
60+
61+
std::string unsupported_format_parameter_type()
62+
{
63+
// No fixes here because the format parameter of the function called is not a
64+
// string.
65+
return bad_format_type_strprintf("");
66+
}

clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print-custom.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// RUN: %check_clang_tidy -std=c++23 %s modernize-use-std-print %t -- \
22
// RUN: -config="{CheckOptions: \
33
// RUN: { \
4-
// RUN: modernize-use-std-print.PrintfLikeFunctions: 'unqualified_printf;::myprintf; mynamespace::myprintf2', \
5-
// RUN: modernize-use-std-print.FprintfLikeFunctions: '::myfprintf; mynamespace::myfprintf2' \
4+
// RUN: modernize-use-std-print.PrintfLikeFunctions: 'unqualified_printf;::myprintf; mynamespace::myprintf2; bad_format_type_printf', \
5+
// RUN: modernize-use-std-print.FprintfLikeFunctions: '::myfprintf; mynamespace::myfprintf2; bad_format_type_fprintf' \
66
// RUN: } \
77
// RUN: }" \
88
// RUN: -- -isystem %clang_tidy_headers
@@ -86,3 +86,25 @@ void no_name(const std::string &in)
8686
{
8787
"A" + in;
8888
}
89+
90+
int myprintf(const wchar_t *, ...);
91+
92+
void wide_string_not_supported() {
93+
myprintf(L"wide string %s", L"string");
94+
}
95+
96+
// Issue #92896: Ensure that the check doesn't assert if the argument is
97+
// promoted to something that isn't a string.
98+
struct S {
99+
S(...) {}
100+
};
101+
int bad_format_type_printf(const S &, ...);
102+
int bad_format_type_fprintf(FILE *, const S &, ...);
103+
104+
void unsupported_format_parameter_type()
105+
{
106+
// No fixes here because the format parameter of the function called is not a
107+
// string.
108+
bad_format_type_printf("Hello %s", "world");
109+
bad_format_type_fprintf(stderr, "Hello %s", "world");
110+
}

0 commit comments

Comments
 (0)