Skip to content

Commit 9f8ccf5

Browse files
authored
[clang-tidy] fix misc-const-correctnes false-positive for fold expressions (#78320)
The check no longer emits a diagnostic for non-parameter-pack variables in C++17 fold expressions. The operator used is type-dependent because of the parameter pack and can therefore not be guaranteed to not mutate the variable. Fixes: #70323
1 parent 364a5b5 commit 9f8ccf5

File tree

4 files changed

+65
-1
lines changed

4 files changed

+65
-1
lines changed

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ Changes in existing checks
404404
using pointer to member function. Additionally, the check no longer emits
405405
a diagnostic when a variable that is not type-dependent is an operand of a
406406
type-dependent binary operator. Improved performance of the check through
407-
optimizations.
407+
optimizations. The check no longer emits a diagnostic for non-parameter-pack
408+
variables in C++17 fold expressions.
408409

409410
- Improved :doc:`misc-include-cleaner
410411
<clang-tidy/checks/misc/include-cleaner>` check by adding option

clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,31 @@ namespace gh57297{
3030
struct Stream { };
3131
template <typename T> void f() { T t; Stream x; x << t; }
3232
} // namespace gh57297
33+
34+
namespace gh70323{
35+
// A fold expression may contain the checked variable as it's initializer.
36+
// We don't know if the operator modifies that variable because the
37+
// operator is type dependent due to the parameter pack.
38+
39+
struct Stream {};
40+
template <typename... Args>
41+
void concatenate1(Args... args)
42+
{
43+
Stream stream;
44+
(stream << ... << args);
45+
}
46+
47+
template <typename... Args>
48+
void concatenate2(Args... args)
49+
{
50+
Stream stream;
51+
(args << ... << stream);
52+
}
53+
54+
template <typename... Args>
55+
void concatenate3(Args... args)
56+
{
57+
Stream stream;
58+
(..., (stream << args));
59+
}
60+
} // namespace gh70323

clang/lib/Analysis/ExprMutationAnalyzer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
343343
// in different instantiations of the template.
344344
binaryOperator(isTypeDependent(),
345345
hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))),
346+
// A fold expression may contain `Exp` as it's initializer.
347+
// We don't know if the operator modifies `Exp` because the
348+
// operator is type dependent due to the parameter pack.
349+
cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))),
346350
// Within class templates and member functions the member expression might
347351
// not be resolved. In that case, the `callExpr` is considered to be a
348352
// modification.

clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,37 @@ TEST(ExprMutationAnalyzerTest, DependentOperatorWithNonDependentOperand) {
359359
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << t"));
360360
}
361361

362+
TEST(ExprMutationAnalyzerTest, FoldExpression) {
363+
// gh70323
364+
// A fold expression may contain `Exp` as it's initializer.
365+
// We don't know if the operator modifies `Exp` because the
366+
// operator is type dependent due to the parameter pack.
367+
auto AST = buildASTFromCodeWithArgs(
368+
"struct Stream {};"
369+
"template <typename... Args> void concatenate(Args... args) "
370+
"{ Stream x; (x << ... << args); }",
371+
{"-fno-delayed-template-parsing"});
372+
auto Results =
373+
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
374+
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x << ... << args)"));
375+
376+
AST = buildASTFromCodeWithArgs(
377+
"struct Stream {};"
378+
"template <typename... Args> void concatenate(Args... args) "
379+
"{ Stream x; (args << ... << x); }",
380+
{"-fno-delayed-template-parsing"});
381+
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
382+
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(args << ... << x)"));
383+
384+
AST = buildASTFromCodeWithArgs(
385+
"struct Stream {};"
386+
"template <typename... Args> void concatenate(Args... args) "
387+
"{ Stream x; (..., (x << args)); }",
388+
{"-fno-delayed-template-parsing"});
389+
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
390+
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << args"));
391+
}
392+
362393
// Section: expression as call argument
363394

364395
TEST(ExprMutationAnalyzerTest, ByValueArgument) {

0 commit comments

Comments
 (0)