diff --git a/clang-tools-extra/clang-tidy/utils/Aliasing.cpp b/clang-tools-extra/clang-tidy/utils/Aliasing.cpp --- a/clang-tools-extra/clang-tidy/utils/Aliasing.cpp +++ b/clang-tools-extra/clang-tidy/utils/Aliasing.cpp @@ -50,6 +50,13 @@ } else if (const auto *LE = dyn_cast(S)) { // Treat lambda capture by reference as a form of taking a reference. return capturesByRef(LE->getLambdaClass(), Var); + } else if (const auto *ILE = dyn_cast(S)) { + return llvm::any_of(ILE->inits(), [Var](const Expr *ChildE) { + // If the child expression is a reference to Var, this means that it's + // used as an initializer of a reference-typed field. Otherwise + // it would have been surrounded with an implicit lvalue-to-rvalue cast. + return isAccessForVar(ChildE, Var); + }); } return false; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp @@ -566,3 +566,29 @@ } return 0; } + +struct AggregateWithReference { + int &y; +}; + +void test_structured_bindings_good() { + int x = 0; + AggregateWithReference ref { x }; + auto &[y] = ref; + for (; x < 10; ++y) { + // No warning. The loop is finite because 'y' is a reference to 'x'. + } +} + +struct AggregateWithValue { + int y; +}; + +void test_structured_bindings_bad() { + int x = 0; + AggregateWithValue val { x }; + auto &[y] = val; + for (; x < 10; ++y) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop] + } +}