The motivation of this path is to catch code like this:

for (int i = 0; i < 10; ++i)
  for (int j = 0; j < 10; ++i)
    { }

The second for loop increments i instead of j causing an infinite loop.
 The warning also checks the body of the for loop so it will trigger on:

for (int i; i <10; ) { }

But not trigger on:

for (int i; i< 10; ) { ++i; }

I'm still fine-tuning the trigger conditions, but would like some feedback
on this patch.
Index: test/SemaCXX/warn-loop-analysis.cpp
===================================================================
--- test/SemaCXX/warn-loop-analysis.cpp	(revision 0)
+++ test/SemaCXX/warn-loop-analysis.cpp	(revision 0)
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fsyntax-only -Wloop-analysis -verify %s
+
+struct S {
+  bool stop() { return false; }
+  bool keep_running;
+};
+
+void by_ref(int &value) { }
+void by_value(int value) { }
+void by_pointer(int *value) {}
+
+void test1() {
+  S s;
+  for (; !s.stop();) {}
+  for (; s.keep_running;) {}
+  for (int i; i < 1; ++i) {}
+  for (int i; i < 1; ) {}  // expected-warning {{variables used in loop condition not modified in loop body}}
+  for (int i; i < 1; ) { ++i; }
+  for (int i; i < 1; ) { return; }
+  for (int i; i < 1; ) { break; }
+  for (int i; i < 1; ) { goto exit_loop; }
+exit_loop:
+  for (int i; i < 1; ) { by_ref(i); }
+  for (int i; i < 1; ) { by_value(i); }  // expected-warning {{variables used in loop condition not modified in loop body}}
+  for (int i; i < 1; ) { by_pointer(&i); }
+
+  for (int i; i < 1; ++i)
+    for (int j; j < 1; ++j)
+      { }
+  for (int i; i < 1; ++i)
+    for (int j; j < 1; ++i)  // expected-warning {{variables used in loop condition not modified in loop body}}
+      { }
+  for (int i; i < 1; ++i)
+    for (int j; i < 1; ++j)  // expected-warning {{variables used in loop condition not modified in loop body}}
+      { }
+}
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 149887)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -14,6 +14,11 @@
 let Component = "Sema" in {
 let CategoryName = "Semantic Issue" in {
 
+// For loop analysis
+def warn_variables_not_in_loop_body : Warning<
+  "variables used in loop condition not modified in loop body">,
+  InGroup<DiagGroup<"loop-analysis">>, DefaultIgnore;
+
 // Constant expressions
 def err_expr_not_ice : Error<
   "expression is not an %select{integer|integral}0 constant expression">;
Index: lib/Sema/SemaStmt.cpp
===================================================================
--- lib/Sema/SemaStmt.cpp	(revision 149887)
+++ lib/Sema/SemaStmt.cpp	(working copy)
@@ -19,6 +19,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/CharUnits.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/StmtObjC.h"
@@ -1009,6 +1010,153 @@
   return Owned(new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen));
 }
 
+namespace {
+  // This visitor will traverse a conditional statement and store all
+  // the evaluated decls into a vector.  Simple is set to true if none
+  // of the excluded constructs are used.
+  class DeclExtractor : public EvaluatedExprVisitor<DeclExtractor> {
+    SmallVectorImpl<ValueDecl*> &Decls;
+    bool Simple;
+public:
+  typedef EvaluatedExprVisitor<DeclExtractor> Inherited;
+
+  DeclExtractor(Sema &S, SmallVectorImpl<ValueDecl*> &Decls) :
+      Inherited(S.Context), Decls(Decls), Simple(true) {}
+
+  bool isSimple() { return Simple; }
+
+  void VisitMemberExpr(MemberExpr *E) {
+    // Don't do analysis on classes.
+    Simple = false;
+    return;
+  }
+
+  void VisitCastExpr(CastExpr *E) {
+    // Don't do analysis on function calls or arrays.
+    if (E->getCastKind() == CK_FunctionToPointerDecay ||
+        E->getCastKind() == CK_ArrayToPointerDecay) {
+      Simple = false;
+      return;
+    }
+
+     Visit(E->getSubExpr());
+  }
+
+  void VisitDeclRefExpr(DeclRefExpr *E) {
+    ValueDecl *VD = E->getDecl();
+    // Dont do analysis on pointers.
+    if (VD->getType()->isAnyPointerType()) {
+      Simple = false;
+      return;
+    }
+
+    // Make sure not to add duplicate Decls.
+    for (SmallVectorImpl<ValueDecl*>::iterator I = Decls.begin(),
+                                               E = Decls.end();
+         I != E; ++I)
+      if (*I == VD) return;
+
+    Decls.push_back(VD);
+  }
+
+  }; // end class DeclExtractor
+
+  // DeclMatcher checks to see if the decls are used in a non-evauluated
+  // context.  
+  class DeclMatcher : public StmtVisitor<DeclMatcher> {
+    SmallVectorImpl<ValueDecl*> &Decls;
+    bool FoundDecl;
+
+public:
+  DeclMatcher(SmallVectorImpl<ValueDecl*> &Decls, Stmt *Statement) :
+      Decls(Decls), FoundDecl(false) {
+    if (!Statement) return;
+    if (Expr *E = dyn_cast<Expr>(Statement)) {
+      VisitExpr(E);
+      return;
+    }
+
+    Visit(Statement);
+  }
+
+  void VisitStmt(Stmt *S) {
+    for (Stmt::child_range C = S->children(); C; ++C)
+      if (*C)
+        this->Visit(*C);
+  }
+
+  void VisitReturnStmt(ReturnStmt *S) {
+    FoundDecl = true;
+  }
+
+  void VisitBreakStmt(BreakStmt *S) {
+    FoundDecl = true;
+  }
+
+  void VisitGotoStmt(GotoStmt *S) {
+    FoundDecl = true;
+  }
+
+  void VisitCastExpr(CastExpr *E) {
+    // Decl is evaluated, not updated.  Ignore.
+    if (E->getCastKind() == CK_LValueToRValue)
+      if (isa<DeclRefExpr>(E->getSubExpr()))
+        return;
+
+    VisitExpr(E->getSubExpr());
+  }
+
+  void VisitDeclRefExpr(DeclRefExpr *E) {
+    ValueDecl *VD = E->getDecl();
+    for (SmallVectorImpl<ValueDecl*>::iterator I = Decls.begin(),
+                                               E = Decls.end();
+         I != E; ++I)
+      if (*I == VD) {
+        FoundDecl = true;
+        return;
+      }
+  }
+
+  bool FoundDeclInUse() { return FoundDecl; }
+
+  };  // end class DeclMatcher
+
+  void CheckForLoopConditionalStatement(Sema &S, Expr *Second,
+                                        Expr *Third, Stmt *Body) {
+    // Condition is empty
+    if (!Second) return;
+
+    if (S.Diags.getDiagnosticLevel(diag::warn_variables_not_in_loop_body,
+                                   Second->getLocStart())
+        == DiagnosticsEngine::Ignored)
+      return;
+
+    SmallVector<ValueDecl*, 4> Decls;
+    DeclExtractor DE(S, Decls);
+    DE.Visit(Second);
+
+    // Don't analyze complex conditionals.
+    if (!DE.isSimple()) return;
+
+    // No decls found.
+    if (Decls.size() == 0) return;
+
+    // Don't warn on volatile decls.
+    for (SmallVector<ValueDecl*, 4>::iterator I = Decls.begin(),
+                                              E = Decls.end();
+         I != E; ++I)
+      if ((*I)->getType().isVolatileQualified()) return;
+
+    if (!DeclMatcher(Decls, Second).FoundDeclInUse() &&
+        !DeclMatcher(Decls, Third).FoundDeclInUse() &&
+        !DeclMatcher(Decls, Body).FoundDeclInUse()) {
+      S.Diag(Second->getLocStart(), diag::warn_variables_not_in_loop_body)
+          << Second->getSourceRange();
+    }
+  }
+
+} // end namespace
+
 StmtResult
 Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
                    Stmt *First, FullExprArg second, Decl *secondVar,
@@ -1031,6 +1179,8 @@
     }
   }
 
+  CheckForLoopConditionalStatement(*this, second.get(), third.get(), Body);
+
   ExprResult SecondResult(second.release());
   VarDecl *ConditionVar = 0;
   if (secondVar) {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to