Two more fixes to loop convert.

Summary: Ensure that the alias has the same type than the loop variable. Now it works with lambda captures.

Reviewers: klimek

Subscribers: alexfh, cfe-commits

Differential Revision: http://reviews.llvm.org/D12597

llvm-svn: 246762
diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
index f2e0f55..a690ecd 100644
--- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
@@ -334,7 +334,8 @@
 ///     // use t, do not use i
 ///   }
 /// \endcode
-static bool isAliasDecl(const Decl *TheDecl, const VarDecl *IndexVar) {
+static bool isAliasDecl(ASTContext *Context, const Decl *TheDecl,
+                        const VarDecl *IndexVar) {
   const auto *VDecl = dyn_cast<VarDecl>(TheDecl);
   if (!VDecl)
     return false;
@@ -346,6 +347,15 @@
   if (!Init)
     return false;
 
+  // Check that the declared type is the same as (or a reference to) the
+  // container type.
+  QualType DeclarationType = VDecl->getType();
+  if (DeclarationType->isReferenceType())
+    DeclarationType = DeclarationType.getNonReferenceType();
+  QualType InitType = Init->getType();
+  if (!Context->hasSameUnqualifiedType(DeclarationType, InitType))
+    return false;
+
   switch (Init->getStmtClass()) {
   case Stmt::ArraySubscriptExprClass: {
     const auto *E = cast<ArraySubscriptExpr>(Init);
@@ -711,13 +721,49 @@
   return true;
 }
 
+/// \brief If the loop index is captured by a lambda, replace this capture
+/// by the range-for loop variable.
+///
+/// For example:
+/// \code
+///   for (int i = 0; i < N; ++i) {
+///     auto f = [v, i](int k) {
+///       printf("%d\n", v[i] + k);
+///     };
+///     f(v[i]);
+///   }
+/// \endcode
+///
+/// Will be replaced by:
+/// \code
+///   for (auto & elem : v) {
+///     auto f = [v, elem](int k) {
+///       printf("%d\n", elem + k);
+///     };
+///     f(elem);
+///   }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
+                                                   const LambdaCapture *C) {
+  if (C->capturesVariable()) {
+    const VarDecl* VDecl = C->getCapturedVar();
+    if (areSameVariable(IndexVar, cast<ValueDecl>(VDecl))) {
+      // FIXME: if the index is captured, it will count as an usage and the
+      // alias (if any) won't work, because it is only used in case of having
+      // exactly one usage.
+      Usages.push_back(Usage(nullptr, false, C->getLocation()));
+    }
+  }
+  return VisitorBase::TraverseLambdaCapture(LE, C);
+}
+
 /// \brief If we find that another variable is created just to refer to the loop
 /// element, note it for reuse as the loop variable.
 ///
 /// See the comments for isAliasDecl.
 bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) {
   if (!AliasDecl && S->isSingleDecl() &&
-      isAliasDecl(S->getSingleDecl(), IndexVar)) {
+      isAliasDecl(Context, S->getSingleDecl(), IndexVar)) {
     AliasDecl = S;
     if (CurrStmtParent) {
       if (isa<IfStmt>(CurrStmtParent) || isa<WhileStmt>(CurrStmtParent) ||
diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h
index ec29b67..8a789fa 100644
--- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h
+++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h
@@ -309,6 +309,7 @@
   bool TraverseArraySubscriptExpr(ArraySubscriptExpr *E);
   bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *MemberCall);
   bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *OpCall);
+  bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C);
   bool TraverseMemberExpr(MemberExpr *Member);
   bool TraverseUnaryDeref(UnaryOperator *Uop);
   bool VisitDeclRefExpr(DeclRefExpr *E);