Index: clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp +++ clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp @@ -60,6 +60,18 @@ handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context); } +// Returns true iff the specified variable is initialized via copy construction +// and the argument to the copy constructor is an lvalue. +// +// Note: We don't want to suggest reference binding + lifetime extending a +// loop index because it's less clear than copying the temporary. +static bool isCopyConstructedFromLValueArg(const VarDecl &LoopVar, ASTContext& Ctx) { + const auto *Init = dyn_cast_or_null(LoopVar.getInit()); + if (!Init || !Init->getConstructor()->isCopyConstructor() || Init->getNumArgs() < 1) + return false; + return Init->getArg(0)->isLValue(); +} + bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar, ASTContext &Context) { if (WarnOnAllAutoCopies) { @@ -73,6 +85,8 @@ utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context); if (!Expensive || !*Expensive) return false; + if (!isCopyConstructedFromLValueArg(LoopVar, Context)) + return false; auto Diagnostic = diag(LoopVar.getLocation(), "the loop variable's type is not a reference type; this creates a " @@ -93,6 +107,8 @@ utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context); if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive) return false; + if (!isCopyConstructedFromLValueArg(LoopVar, Context)) + return false; // We omit the case where the loop variable is not used in the loop body. E.g. // // for (auto _ : benchmark_state) { Index: clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp +++ clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp @@ -1,4 +1,5 @@ // RUN: %check_clang_tidy %s performance-for-range-copy %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy %s -std=c++17 performance-for-range-copy %t -- -- -fno-delayed-template-parsing namespace std { @@ -46,6 +47,7 @@ S &operator=(const S &); }; + struct Convertible { operator S() const { return S(); @@ -61,12 +63,16 @@ Convertible C[0]; for (const S &S1 : C) { } + for (const S S2 : C) { + } } void negativeImplicitConstructorConversion() { ConstructorConvertible C[0]; for (const S &S1 : C) { } + for (const S S2 : C) { + } } template