diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 1015b6bd188d8..27f098a54327d 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -404,7 +404,8 @@ Changes in existing checks using pointer to member function. Additionally, the check no longer emits a diagnostic when a variable that is not type-dependent is an operand of a type-dependent binary operator. Improved performance of the check through - optimizations. + optimizations. The check no longer emits a diagnostic for non-parameter-pack + variables in C++17 fold expressions. - Improved :doc:`misc-include-cleaner ` check by adding option diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp index 794578ceeeba8..9da468128743e 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp @@ -30,3 +30,31 @@ namespace gh57297{ struct Stream { }; template void f() { T t; Stream x; x << t; } } // namespace gh57297 + +namespace gh70323{ +// A fold expression may contain the checked variable as it's initializer. +// We don't know if the operator modifies that variable because the +// operator is type dependent due to the parameter pack. + +struct Stream {}; +template +void concatenate1(Args... args) +{ + Stream stream; + (stream << ... << args); +} + +template +void concatenate2(Args... args) +{ + Stream stream; + (args << ... << stream); +} + +template +void concatenate3(Args... args) +{ + Stream stream; + (..., (stream << args)); +} +} // namespace gh70323 diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp index 9af818be0415f..bb042760d297a 100644 --- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -343,6 +343,10 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { // in different instantiations of the template. binaryOperator(isTypeDependent(), hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))), + // A fold expression may contain `Exp` as it's initializer. + // We don't know if the operator modifies `Exp` because the + // operator is type dependent due to the parameter pack. + cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))), // Within class templates and member functions the member expression might // not be resolved. In that case, the `callExpr` is considered to be a // modification. diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp index a94f857720b03..f58ce4aebcbfc 100644 --- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp +++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp @@ -359,6 +359,37 @@ TEST(ExprMutationAnalyzerTest, DependentOperatorWithNonDependentOperand) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << t")); } +TEST(ExprMutationAnalyzerTest, FoldExpression) { + // gh70323 + // A fold expression may contain `Exp` as it's initializer. + // We don't know if the operator modifies `Exp` because the + // operator is type dependent due to the parameter pack. + auto AST = buildASTFromCodeWithArgs( + "struct Stream {};" + "template void concatenate(Args... args) " + "{ Stream x; (x << ... << args); }", + {"-fno-delayed-template-parsing"}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x << ... << args)")); + + AST = buildASTFromCodeWithArgs( + "struct Stream {};" + "template void concatenate(Args... args) " + "{ Stream x; (args << ... << x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(args << ... << x)")); + + AST = buildASTFromCodeWithArgs( + "struct Stream {};" + "template void concatenate(Args... args) " + "{ Stream x; (..., (x << args)); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << args")); +} + // Section: expression as call argument TEST(ExprMutationAnalyzerTest, ByValueArgument) {