Skip to content

Commit 385bafd

Browse files
committed
Deal better with a macro that surrounds the whole call
We don't really want to stop enclosing an entire call to a printf-like function to stop the check from replacing the call since that makes the check ineffective when using testing frameworks such as Catch2.
1 parent ff230d5 commit 385bafd

File tree

4 files changed

+85
-19
lines changed

4 files changed

+85
-19
lines changed

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

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ FormatStringConverter::FormatStringConverter(
216216
}
217217

218218
if (const std::optional<StringRef> MaybeMacroName =
219-
formatStringContainsUnreplaceableMacro(FormatExpr, SM, PP);
219+
formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
220220
MaybeMacroName) {
221221
conversionNotPossible(
222222
("format string contains unreplaceable macro '" + *MaybeMacroName + "'")
@@ -243,30 +243,43 @@ FormatStringConverter::FormatStringConverter(
243243

244244
std::optional<StringRef>
245245
FormatStringConverter::formatStringContainsUnreplaceableMacro(
246-
const StringLiteral *FormatExpr, SourceManager &SM, Preprocessor &PP) {
246+
const CallExpr *Call, const StringLiteral *FormatExpr, SourceManager &SM,
247+
Preprocessor &PP) {
248+
// If a macro invocation surrounds the entire call then we don't want that to
249+
// inhibit conversion. The whole format string will appear to come from that
250+
// macro, as will the function call.
251+
std::optional<StringRef> MaybeSurroundingMacroName;
252+
if (SourceLocation BeginCallLoc = Call->getBeginLoc();
253+
BeginCallLoc.isMacroID())
254+
MaybeSurroundingMacroName =
255+
Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());
256+
247257
for (auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
248258
I != E; ++I) {
249259
const SourceLocation &TokenLoc = *I;
250260
if (TokenLoc.isMacroID()) {
251261
const StringRef MacroName =
252262
Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());
253263

254-
// glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes
255-
// for types that change size so we must look for multiple prefixes.
256-
if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI"))
257-
return MacroName;
258-
259-
const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
260-
const OptionalFileEntryRef MaybeFileEntry =
261-
SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
262-
if (!MaybeFileEntry)
263-
return MacroName;
264-
265-
HeaderSearch &HS = PP.getHeaderSearchInfo();
266-
// Check if the file is a system header
267-
if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
268-
llvm::sys::path::filename(MaybeFileEntry->getName()) != "inttypes.h")
269-
return MacroName;
264+
if (MaybeSurroundingMacroName != MacroName) {
265+
// glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes
266+
// for types that change size so we must look for multiple prefixes.
267+
if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI"))
268+
return MacroName;
269+
270+
const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
271+
const OptionalFileEntryRef MaybeFileEntry =
272+
SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
273+
if (!MaybeFileEntry)
274+
return MacroName;
275+
276+
HeaderSearch &HS = PP.getHeaderSearchInfo();
277+
// Check if the file is a system header
278+
if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
279+
llvm::sys::path::filename(MaybeFileEntry->getName()) !=
280+
"inttypes.h")
281+
return MacroName;
282+
}
270283
}
271284
}
272285
return std::nullopt;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class FormatStringConverter
112112
void appendFormatText(StringRef Text);
113113
void finalizeFormatText();
114114
static std::optional<StringRef>
115-
formatStringContainsUnreplaceableMacro(const StringLiteral *FormatExpr,
115+
formatStringContainsUnreplaceableMacro(const CallExpr *CallExpr,
116+
const StringLiteral *FormatExpr,
116117
SourceManager &SM, Preprocessor &PP);
117118
bool conversionNotPossible(std::string Reason) {
118119
ConversionNotPossibleReason = std::move(Reason);

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,30 @@ std::string StrFormat_macros() {
152152
// Needs a __PRI prefix so that we get as far as looking for where the macro comes from
153153
auto s11 = absl::StrFormat(" macro from command line %" __PRI_CMDLINE_MACRO, s);
154154
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_CMDLINE_MACRO' [modernize-use-std-format]
155+
156+
// We ought to be able to fix this since the macro surrounds the whole call
157+
// and therefore can't change the format string independently. This is
158+
// required to be able to fix calls inside Catch2 macros for example.
159+
#define SURROUND_ALL(x) x
160+
auto s12 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation %" PRIu64, u64));
161+
// CHECK-MESSAGES: [[@LINE-1]]:27: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
162+
// CHECK-FIXES: auto s12 = SURROUND_ALL(std::format("Macro surrounding entire invocation {}", u64));
163+
164+
// But having that surrounding macro shouldn't stop us ignoring an
165+
// unreplaceable macro elsewhere.
166+
auto s13 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation with unreplaceable macro %" PRI_FMT_MACRO, s));
167+
// CHECK-MESSAGES: [[@LINE-1]]:27: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]
168+
169+
// At the moment at least the check will replace occurrences where the
170+
// function name is the result of expanding a macro.
171+
#define SURROUND_FUNCTION_NAME(x) absl:: x
172+
auto s14 = SURROUND_FUNCTION_NAME(StrFormat)("Hello %d", 4442);
173+
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
174+
// CHECK-FIXES: auto s14 = std::format("Hello {}", 4442);
175+
176+
// We can't safely fix occurrences where the macro may affect the format
177+
// string differently in different builds.
178+
#define SURROUND_FORMAT(x) "!" x
179+
auto s15 = absl::StrFormat(SURROUND_FORMAT("Hello %d"), 4443);
180+
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'SURROUND_FORMAT' [modernize-use-std-format]
155181
}

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,4 +1613,30 @@ void macro_expansion(const char *s)
16131613
// Needs a __PRI prefix so that we get as far as looking for where the macro comes from
16141614
printf(" macro from command line %" __PRI_CMDLINE_MACRO, s);
16151615
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro '__PRI_CMDLINE_MACRO' [modernize-use-std-print]
1616+
1617+
// We ought to be able to fix this since the macro surrounds the whole call
1618+
// and therefore can't change the format string independently. This is
1619+
// required to be able to fix calls inside Catch2 macros for example.
1620+
#define SURROUND_ALL(x) x
1621+
SURROUND_ALL(printf("Macro surrounding entire invocation %" PRIu64, u64));
1622+
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: use 'std::print' instead of 'printf' [modernize-use-std-print]
1623+
// CHECK-FIXES: SURROUND_ALL(std::print("Macro surrounding entire invocation {}", u64));
1624+
1625+
// But having that surrounding macro shouldn't stop us ignoring an
1626+
// unreplaceable macro elsewhere.
1627+
SURROUND_ALL(printf("Macro surrounding entire invocation with unreplaceable macro %" PRI_FMT_MACRO, s));
1628+
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-print]
1629+
1630+
// At the moment at least the check will replace occurrences where the
1631+
// function name is the result of expanding a macro.
1632+
#define SURROUND_FUNCTION_NAME(x) x
1633+
SURROUND_FUNCTION_NAME(printf)("Hello %d", 4442);
1634+
// CHECK-MESSAGES: [[@LINE-1]]:26: warning: use 'std::print' instead of 'printf' [modernize-use-std-print]
1635+
// CHECK-FIXES: std::print("Hello {}", 4442);
1636+
1637+
// We can't safely fix occurrences where the macro may affect the format
1638+
// string differently in different builds.
1639+
#define SURROUND_FORMAT(x) "!" x
1640+
printf(SURROUND_FORMAT("Hello %d"), 4443);
1641+
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'SURROUND_FORMAT' [modernize-use-std-print]
16161642
}

0 commit comments

Comments
 (0)