On Wed, Jan 25, 2012 at 9:56 PM, Argyrios Kyrtzidis <[email protected]> wrote:
> I don't think it make sense to warn on template instantiations, since these 
> are syntactic warnings. For template instantiations they would only 
> contribute noise [...]

Agreed, see attached patch.

Dmitri

-- 
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <[email protected]>*/
Index: test/Analysis/PR9741.cpp
===================================================================
--- test/Analysis/PR9741.cpp	(revision 148970)
+++ test/Analysis/PR9741.cpp	(working copy)
@@ -4,5 +4,5 @@
   int a[] = { 1, 2, 3 };
   unsigned int u = 0;
   for (auto x : a)
-    ;
+    ; // expected-warning{{range-based for loop has empty body}}
 }
Index: test/Analysis/misc-ps.m
===================================================================
--- test/Analysis/misc-ps.m	(revision 148970)
+++ test/Analysis/misc-ps.m	(working copy)
@@ -1248,7 +1248,7 @@
   struct s { char *bar[10]; } baz[2] = { 0 };
   unsigned i = 0;
   for (i = 0;
-  (* ({ while(0); ({ &baz[0]; }); })).bar[0] != 0;
+  (* ({ while(0); ({ &baz[0]; }); })).bar[0] != 0; // expected-warning {{while loop has empty body}} expected-note {{put the semicolon on a separate line to silence this warning}}
        ++i) {}
 }
 
Index: test/Sema/statements.c
===================================================================
--- test/Sema/statements.c	(revision 148970)
+++ test/Sema/statements.c	(working copy)
@@ -36,7 +36,7 @@
 
 // PR6034
 void test11(int bit) {
-  switch (bit)
+  switch (bit) // expected-warning {{switch statement has empty body}}
   switch (env->fpscr)  // expected-error {{use of undeclared identifier 'env'}}
   {
   }
Index: test/Sema/switch.c
===================================================================
--- test/Sema/switch.c	(revision 148970)
+++ test/Sema/switch.c	(working copy)
@@ -24,7 +24,7 @@
 
 void test3(void) { 
   // empty switch;
-  switch (0); // expected-warning {{no case matching constant switch condition '0'}}
+  switch (0); // expected-warning {{no case matching constant switch condition '0'}} expected-warning {{switch statement has empty body}}
 }
 
 extern int g();
Index: test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp
===================================================================
--- test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp	(revision 148970)
+++ test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp	(working copy)
@@ -24,7 +24,7 @@
   new (auto) (0.0);
 
   int arr[] = {1, 2, 3};
-  for (auto i : arr) {
+  for (auto i : arr) { // expected-warning {{range-based for loop has empty body}}
   }
 }
 
Index: test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
===================================================================
--- test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp	(revision 148970)
+++ test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp	(working copy)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -Wno-empty-body -fsyntax-only -verify %s
 
 namespace std {
   template<typename T>
Index: test/SemaCXX/warn-empty-body.cpp
===================================================================
--- test/SemaCXX/warn-empty-body.cpp	(revision 0)
+++ test/SemaCXX/warn-empty-body.cpp	(revision 0)
@@ -0,0 +1,265 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+void a(int i);
+int b();
+int c();
+
+void test1(int x, int y) {
+  while(true) {
+    if (x); // expected-warning {{if statement has empty body}}
+
+    int i;
+    // PR11329
+    for (i = 0; i < x; i++); { // expected-warning{{for loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+      a(i);
+      b();
+    }
+
+    for (i = 0; i < x; i++); // expected-warning{{for loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    {
+      a(i);
+    }
+
+    for (i = 0;
+         i < x;
+         i++); // expected-warning{{for loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    {
+      a(i);
+    }
+
+    int arr[3] = { 1, 2, 3 };
+    for (int j : arr); // expected-warning{{range-based for loop has empty body}}
+      a(i);
+
+    for (int j : arr)
+    {} // expected-warning{{range-based for loop has empty body}}
+
+    for (int j :
+         arr); // expected-warning{{range-based for loop has empty body}}
+      a(i);
+
+    while (b() == 0); // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+      a(i);
+
+    while (b() == 0); { // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+      a(i);
+    }
+
+    while (b() == 0); // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    {
+      a(i);
+    }
+
+    while (b() == 0 ||
+           c() == 0); // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    {
+      a(i);
+    }
+
+    switch(x) // no-warning
+    {
+      switch(y); // expected-warning{{switch statement has empty body}}
+      {
+        case 0:
+          a(10);
+          break;
+        default:
+          a(20);
+          break;
+      }
+    }
+  }
+}
+
+/// There should be no warning in `for' and `while' when null statement is
+/// placed on its own line.  Range-based `for' and `switch' should warn.
+void test2(int x, int y) {
+  int i;
+  for (i = 0; i < x; i++) // no-warning
+    ; // no-warning
+
+  for (i = 0;
+       i < x;
+       i++) // no-warning
+    ; // no-warning
+
+  int arr[3] = { 1, 2, 3 };
+  for (int j : arr)
+    ; // expected-warning{{range-based for loop has empty body}}
+
+  while (b() == 0) // no-warning
+    ; // no-warning
+
+  while (b() == 0 ||
+         c() == 0) // no-warning
+    ; // no-warning
+
+  switch(x)
+  {
+    switch(y)
+      ; // expected-warning{{switch statement has empty body}}
+  }
+
+  // Last `for' or `while' statement in compound statement shouldn't warn.
+  while(b() == 0); // no-warning
+}
+
+/// There should be no warning for a null statement resulting from an empty macro.
+#define EMPTY(a)
+void test3(int x, int y) {
+  if (x)
+    EMPTY(x); // no-warning
+
+  int i;
+  for (i = 0; i < x; i++) EMPTY(i); // no-warning
+
+  for (i = 0;
+       i < x;
+       i++) EMPTY(i); // no-warning
+
+  int arr[3] = { 1, 2, 3 };
+  for (int j : arr) EMPTY(j); // no-warning
+
+  for (int j :
+       arr) EMPTY(j); // no-warning
+
+  while (b() == 0) EMPTY(i); // no-warning
+
+  while (b() == 0 ||
+         c() == 0) EMPTY(i); // no-warning
+
+  switch (x) {
+    switch (y)
+      EMPTY(i); // no-warning
+  }
+}
+
+/// There should be no warning for a common for/while idiom when it is obvious
+/// from indentation that next statement wasn't meant to be a body.
+void test4(int x, int y) {
+  int i;
+  for (i = 0; i < x; i++); // expected-warning{{for loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    a(i);
+
+  for (i = 0; i < x; i++); // no-warning
+  a(i);
+
+  for (i = 0;
+       i < x;
+       i++); // expected-warning{{for loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    a(i);
+
+  for (i = 0;
+       i < x;
+       i++); // no-warning
+  a(i);
+
+  while (b() == 0); // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    a(i);
+
+  while (b() == 0); // no-warning
+  a(i);
+
+  while (b() == 0 ||
+         c() == 0); // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    a(i);
+
+  while (b() == 0 ||
+         c() == 0); // no-warning
+  a(i);
+
+}
+
+/// There should be no warning for a statement with a non-null body.
+void test5(int x, int y) {
+  if (x) {} // no-warning
+
+  if (x)
+    a(x); // no-warning
+
+  int i;
+  for (i = 0; i < x; i++) // no-warning
+    a(i); // no-warning
+
+  for (i = 0; i < x; i++) { // no-warning
+    a(i); // no-warning
+  }
+
+  for (i = 0;
+       i < x;
+       i++) // no-warning
+    a(i); // no-warning
+
+  int arr[3] = { 1, 2, 3 };
+  for (int j : arr) // no-warning
+    a(j);
+
+  for (int j :
+       arr) // no-warning
+    a(j);
+
+  while (b() == 0) // no-warning
+    a(i); // no-warning
+
+  while (b() == 0) { // no-warning
+    a(i); // no-warning
+  }
+
+  while (b() == 0 ||
+         c() == 0) // no-warning
+    a(i); // no-warning
+
+  while (b() == 0 ||
+         c() == 0) { // no-warning
+    a(i); // no-warning
+  }
+
+  switch(x) // no-warning
+  {
+    switch(y) // no-warning
+    {
+      case 0:
+        a(10);
+        break;
+      default:
+        a(20);
+        break;
+    }
+  }
+}
+
+void test_errors(int x) {
+  if (1)
+    aa; // expected-error{{use of undeclared identifier}}
+        // no empty body warning.
+
+  int i;
+  for (i = 0; i < x; i++)
+    bb; // expected-error{{use of undeclared identifier}}
+
+  int arr[3] = { 1, 2, 3 };
+  for (int j : arr)
+    cc; // expected-error{{use of undeclared identifier}}
+
+  while (b() == 0)
+    dd; // expected-error{{use of undeclared identifier}}
+}
+
+// Warnings for statements in templates shouldn't be duplicated for all
+// instantiations.
+template <typename T>
+void test_template(int x) {
+  if (x); // expected-warning{{if statement has empty body}}
+
+  if (x)
+    EMPTY(x); // no-warning
+
+  while (b() == 0); // expected-warning{{while loop has empty body}} expected-note{{put the semicolon on a separate line to silence this warning}}
+    a(x);
+}
+
+void test_template_inst(int x) {
+  test_template<int>(x);
+  test_template<double>(x);
+}
+
Index: test/SemaCXX/if-empty-body.cpp
===================================================================
--- test/SemaCXX/if-empty-body.cpp	(revision 148970)
+++ test/SemaCXX/if-empty-body.cpp	(working copy)
@@ -1,35 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-
-void f1(int a) {
-    if (a); // expected-warning {{if statement has empty body}}
-}
-
-void f2(int a) {
-    if (a) {}
-}
-
-void f3() {
-  if (1)
-    xx;      // expected-error {{use of undeclared identifier}}
-  return;    // no empty body warning.
-}
-
-// Don't warn about an empty body if is expanded from a macro.
-void f4(int i) {
-  #define BODY(x)
-  if (i == i) // expected-warning{{self-comparison always evaluates to true}}
-    BODY(0);
-  #undef BODY
-}
-
-template <typename T>
-void tf() {
-  #define BODY(x)
-  if (0)
-    BODY(0);
-  #undef BODY
-}
-
-void f5() {
-    tf<int>();
-}
Index: test/SemaCXX/for-range-no-std.cpp
===================================================================
--- test/SemaCXX/for-range-no-std.cpp	(revision 148970)
+++ test/SemaCXX/for-range-no-std.cpp	(working copy)
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98 -Wno-c++11-extensions
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wno-empty-body
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98 -Wno-c++11-extensions -Wno-empty-body
 
 struct S {
   int *begin();
Index: test/SemaCXX/PR10458.cpp
===================================================================
--- test/SemaCXX/PR10458.cpp	(revision 148970)
+++ test/SemaCXX/PR10458.cpp	(working copy)
@@ -1,7 +1,10 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98
 
+void g(int);
+
 void f() {
   int arr[] = { 1, 2, 3 };
   for (auto &i : arr) { // expected-warning {{'auto' type specifier is a C++11 extension}} expected-warning {{range-based for loop is a C++11 extension}}
+    g(i);
   }
 }
Index: test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp
===================================================================
--- test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp	(revision 148970)
+++ test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp	(working copy)
@@ -120,5 +120,5 @@
   static_assert(same_type<decltype(l), std::initializer_list<int>>::value, "");
   auto bl = {1, 2.0}; // expected-error {{cannot deduce}}
 
-  for (int i : {1, 2, 3, 4}) {}
+  for (int i : {1, 2, 3, 4}) {} // expected-warning {{range-based for loop has empty body}}
 }
Index: test/SemaCXX/for-range-unused.cpp
===================================================================
--- test/SemaCXX/for-range-unused.cpp	(revision 148970)
+++ test/SemaCXX/for-range-unused.cpp	(working copy)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wunused
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wunused -Wno-empty-body
 
 // PR9968: We used to warn that __range is unused in a dependent for-range.
 
Index: test/SemaCXX/cxx98-compat.cpp
===================================================================
--- test/SemaCXX/cxx98-compat.cpp	(revision 148970)
+++ test/SemaCXX/cxx98-compat.cpp	(working copy)
@@ -65,9 +65,11 @@
 
 auto f() -> int; // expected-warning {{trailing return types are incompatible with C++98}}
 
+void g(int);
 void RangeFor() {
   int xs[] = {1, 2, 3};
   for (int &a : xs) { // expected-warning {{range-based for loop is incompatible with C++98}}
+    g(a);
   }
 }
 
Index: test/SemaCXX/condition.cpp
===================================================================
--- test/SemaCXX/condition.cpp	(revision 148970)
+++ test/SemaCXX/condition.cpp	(working copy)
@@ -20,7 +20,7 @@
   while (struct S {} *x=0) ; // expected-error {{types may not be defined in conditions}}
   while (struct {} *x=0) ; // expected-error {{types may not be defined in conditions}}
   switch (enum {E} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize}} \
-  // expected-warning{{enumeration value 'E' not handled in switch}}
+  // expected-warning{{enumeration value 'E' not handled in switch}} expected-warning {{switch statement has empty body}}
 
   if (int x=0) { // expected-note 2 {{previous definition is here}}
     int x;  // expected-error {{redefinition of 'x'}}
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 148970)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -5009,8 +5009,20 @@
   "switch condition type %0 requires explicit conversion to %1">;
 def err_switch_incomplete_class_type : Error<
   "switch condition has incomplete class type %0">;
+
 def warn_empty_if_body : Warning<
   "if statement has empty body">, InGroup<EmptyBody>;
+def warn_empty_for_body : Warning<
+  "for loop has empty body">, InGroup<EmptyBody>;
+def warn_empty_range_based_for_body : Warning<
+  "range-based for loop has empty body">, InGroup<EmptyBody>;
+def warn_empty_while_body : Warning<
+  "while loop has empty body">, InGroup<EmptyBody>;
+def warn_empty_switch_body : Warning<
+  "switch statement has empty body">, InGroup<EmptyBody>;
+def note_empty_body_on_separate_line : Note<
+  "put the semicolon on a separate line to silence this warning">,
+  InGroup<EmptyBody>;
 
 def err_va_start_used_in_non_variadic_function : Error<
   "'va_start' used in function with fixed args">;
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 148970)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -162,6 +162,7 @@
 namespace sema {
   class AccessedEntity;
   class BlockScopeInfo;
+  class CompoundScopeInfo;
   class DelayedDiagnostic;
   class FunctionScopeInfo;
   class LambdaScopeInfo;
@@ -735,6 +736,11 @@
     return FunctionScopes.back();
   }
 
+  void PushCompoundScope();
+  void PopCompoundScope();
+
+  sema::CompoundScopeInfo &getCurCompoundScope() const;
+
   bool hasAnyUnrecoverableErrorsInThisFunction() const;
 
   /// \brief Retrieve the current block, if any.
@@ -2062,9 +2068,28 @@
 
   StmtResult ActOnNullStmt(SourceLocation SemiLoc,
                            bool HasLeadingEmptyMacro = false);
+
+  void ActOnStartOfCompoundStmt();
+  void ActOnFinishOfCompoundStmt();
   StmtResult ActOnCompoundStmt(SourceLocation L, SourceLocation R,
                                        MultiStmtArg Elts,
                                        bool isStmtExpr);
+
+  /// \brief A RAII object to enter scope of a compound statement.
+  class CompoundScopeRAII {
+  public:
+    CompoundScopeRAII(Sema &S): S(S) {
+      S.ActOnStartOfCompoundStmt();
+    }
+
+    ~CompoundScopeRAII() {
+      S.ActOnFinishOfCompoundStmt();
+    }
+
+  private:
+    Sema &S;
+  };
+
   StmtResult ActOnDeclStmt(DeclGroupPtrTy Decl,
                                    SourceLocation StartLoc,
                                    SourceLocation EndLoc);
@@ -2213,6 +2238,30 @@
   void DiagnoseUnusedExprResult(const Stmt *S);
   void DiagnoseUnusedDecl(const NamedDecl *ND);
 
+  /// Return true if statement located on \p StmtLoc has a suspicious null
+  /// statement as a \p Body.
+  ///
+  /// If \p QuietIfBodyOnSeparateLine is true, warn only if body is on the same
+  /// line.  If \p QuietIfBodyOnSeparateLine is false, always warn.
+  ///
+  /// This helps prevent bugs due to typos, such as:
+  ///     if (condition);
+  ///       do_stuff();
+  bool ShouldDiagnoseEmptyStmtBody(SourceLocation StmtLoc,
+                                   const NullStmt *Body,
+                                   bool QuietIfBodyOnSeparateLine);
+
+  /// Emit \p DiagID if \ref Sema::ShouldDiagnoseEmptyStmtBody returns true.
+  void DiagnoseEmptyStmtBody(SourceLocation StmtLoc,
+                             const Stmt *Body,
+                             bool QuietIfBodyOnSeparateLine,
+                             unsigned DiagID);
+
+  /// Warn if a for/while loop statement \p S, which is followed by
+  /// \p PossibleBody, has a suspicious null statement as a body.
+  void DiagnoseEmptyLoopBody(const Stmt *S,
+                             const Stmt *PossibleBody);
+
   ParsingDeclState PushParsingDeclaration() {
     return DelayedDiagnostics.pushParsingDecl();
   }
Index: include/clang/Sema/ScopeInfo.h
===================================================================
--- include/clang/Sema/ScopeInfo.h	(revision 148970)
+++ include/clang/Sema/ScopeInfo.h	(working copy)
@@ -31,6 +31,22 @@
 
 namespace sema {
 
+/// \brief Contains information about the compound statement currently being
+/// parsed.
+class CompoundScopeInfo {
+public:
+  CompoundScopeInfo()
+    : HasEmptyLoopBodies(false) { }
+
+  /// \brief Whether this compound stamement contains `for' or `while' loops
+  /// with empty bodies.
+  bool HasEmptyLoopBodies;
+
+  void setHasEmptyLoopBodies() {
+    HasEmptyLoopBodies = true;
+  }
+};
+
 class PossiblyUnreachableDiag {
 public:
   PartialDiagnostic PD;
@@ -78,7 +94,11 @@
   /// block, if there is any chance of applying the named return value
   /// optimization.
   SmallVector<ReturnStmt*, 4> Returns;
-  
+
+  /// \brief The stack of currently active compound stamement scopes in the
+  /// function.
+  SmallVector<CompoundScopeInfo, 4> CompoundScopes;
+
   /// \brief A list of PartialDiagnostics created but delayed within the
   /// current function scope.  These diagnostics are vetted for reachability
   /// prior to being emitted.
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp	(revision 148970)
+++ lib/Sema/SemaDeclCXX.cpp	(working copy)
@@ -8160,10 +8160,14 @@
     CopyAssignOperator->setInvalidDecl();
     return;
   }
-  
-  StmtResult Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements),
-                                            /*isStmtExpr=*/false);
-  assert(!Body.isInvalid() && "Compound statement creation cannot fail");
+
+  StmtResult Body;
+  {
+    CompoundScopeRAII CompoundScope(*this);
+    Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements),
+                             /*isStmtExpr=*/false);
+    assert(!Body.isInvalid() && "Compound statement creation cannot fail");
+  }
   CopyAssignOperator->setBody(Body.takeAs<Stmt>());
 
   if (ASTMutationListener *L = getASTMutationListener()) {
@@ -8590,10 +8594,14 @@
     MoveAssignOperator->setInvalidDecl();
     return;
   }
-  
-  StmtResult Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements),
-                                            /*isStmtExpr=*/false);
-  assert(!Body.isInvalid() && "Compound statement creation cannot fail");
+
+  StmtResult Body;
+  {
+    CompoundScopeRAII CompoundScope(*this);
+    Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements),
+                             /*isStmtExpr=*/false);
+    assert(!Body.isInvalid() && "Compound statement creation cannot fail");
+  }
   MoveAssignOperator->setBody(Body.takeAs<Stmt>());
 
   if (ASTMutationListener *L = getASTMutationListener()) {
@@ -8792,9 +8800,10 @@
       << CXXCopyConstructor << Context.getTagDeclType(ClassDecl);
     CopyConstructor->setInvalidDecl();
   }  else {
+    Sema::CompoundScopeRAII CompoundScope(*this);
     CopyConstructor->setBody(ActOnCompoundStmt(CopyConstructor->getLocation(),
                                                CopyConstructor->getLocation(),
-                                               MultiStmtArg(*this, 0, 0), 
+                                               MultiStmtArg(*this, 0, 0),
                                                /*isStmtExpr=*/false)
                                                               .takeAs<Stmt>());
     CopyConstructor->setImplicitlyDefined(true);
@@ -8946,9 +8955,10 @@
       << CXXMoveConstructor << Context.getTagDeclType(ClassDecl);
     MoveConstructor->setInvalidDecl();
   }  else {
+    Sema::CompoundScopeRAII CompoundScope(*this);
     MoveConstructor->setBody(ActOnCompoundStmt(MoveConstructor->getLocation(),
                                                MoveConstructor->getLocation(),
-                                               MultiStmtArg(*this, 0, 0), 
+                                               MultiStmtArg(*this, 0, 0),
                                                /*isStmtExpr=*/false)
                                                               .takeAs<Stmt>());
     MoveConstructor->setImplicitlyDefined(true);
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h	(revision 148970)
+++ lib/Sema/TreeTransform.h	(working copy)
@@ -4952,6 +4952,8 @@
 StmtResult
 TreeTransform<Derived>::TransformCompoundStmt(CompoundStmt *S,
                                               bool IsStmtExpr) {
+  Sema::CompoundScopeRAII CompoundScope(getSema());
+
   bool SubStmtInvalid = false;
   bool SubStmtChanged = false;
   ASTOwningVector<Stmt*> Statements(getSema());
Index: lib/Sema/SemaStmt.cpp
===================================================================
--- lib/Sema/SemaStmt.cpp	(revision 148970)
+++ lib/Sema/SemaStmt.cpp	(working copy)
@@ -225,6 +225,18 @@
   DiagRuntimeBehavior(Loc, 0, PDiag(DiagID) << R1 << R2);
 }
 
+void Sema::ActOnStartOfCompoundStmt() {
+  PushCompoundScope();
+}
+
+void Sema::ActOnFinishOfCompoundStmt() {
+  PopCompoundScope();
+}
+
+sema::CompoundScopeInfo &Sema::getCurCompoundScope() const {
+  return getCurFunction()->CompoundScopes.back();
+}
+
 StmtResult
 Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
                         MultiStmtArg elts, bool isStmtExpr) {
@@ -257,6 +269,14 @@
     DiagnoseUnusedExprResult(Elts[i]);
   }
 
+  // Check for suspicious empty body (null statement) in `for' and `while'
+  // statements.
+  if (NumElts != 0 && !CurrentInstantiationScope &&
+      getCurCompoundScope().HasEmptyLoopBodies) {
+    for (unsigned i = 0; i != NumElts - 1; ++i)
+      DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]);
+  }
+
   return Owned(new (Context) CompoundStmt(Context, Elts, NumElts, L, R));
 }
 
@@ -353,21 +373,9 @@
 
   DiagnoseUnusedExprResult(thenStmt);
 
-  // Warn if the if block has a null body without an else value.
-  // this helps prevent bugs due to typos, such as
-  // if (condition);
-  //   do_stuff();
-  //
   if (!elseStmt) {
-    if (NullStmt* stmt = dyn_cast<NullStmt>(thenStmt))
-      // But do not warn if the body is a macro that expands to nothing, e.g:
-      //
-      // #define CALL(x)
-      // if (condition)
-      //   CALL(0);
-      //
-      if (!stmt->hasLeadingEmptyMacro())
-        Diag(stmt->getSemiLoc(), diag::warn_empty_if_body);
+    DiagnoseEmptyStmtBody(SourceLocation(), thenStmt, false,
+                          diag::warn_empty_if_body);
   }
 
   DiagnoseUnusedExprResult(elseStmt);
@@ -958,6 +966,9 @@
     }
   }
 
+  DiagnoseEmptyStmtBody(SourceLocation(), BodyStmt, false,
+                        diag::warn_empty_switch_body);
+
   // FIXME: If the case list was broken is some way, we don't have a good system
   // to patch it up.  Instead, just return the whole substmt as broken.
   if (CaseListIsErroneous)
@@ -984,6 +995,9 @@
 
   DiagnoseUnusedExprResult(Body);
 
+  if (isa<NullStmt>(Body))
+    getCurCompoundScope().setHasEmptyLoopBodies();
+
   return Owned(new (Context) WhileStmt(Context, ConditionVar, ConditionExpr,
                                        Body, WhileLoc));
 }
@@ -1047,6 +1061,9 @@
   DiagnoseUnusedExprResult(Third);
   DiagnoseUnusedExprResult(Body);
 
+  if (isa<NullStmt>(Body))
+    getCurCompoundScope().setHasEmptyLoopBodies();
+
   return Owned(new (Context) ForStmt(Context, First,
                                      SecondResult.take(), ConditionVar,
                                      Third, Body, ForLoc, LParenLoc,
@@ -1587,6 +1604,17 @@
     return StmtError();
 
   cast<CXXForRangeStmt>(S)->setBody(B);
+
+  DiagnoseEmptyStmtBody(SourceLocation(), B, false,
+                        diag::warn_empty_range_based_for_body);
+
+  // Range-based for with empty body doesn't make sense in any case.
+  if (CompoundStmt *Body = dyn_cast<CompoundStmt>(B)) {
+    if (Body->body_empty())
+      Diag(Body->getLBracLoc(), diag::warn_empty_range_based_for_body)
+        << Body->getSourceRange();
+  }
+
   return S;
 }
 
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp	(revision 148970)
+++ lib/Sema/SemaChecking.cpp	(working copy)
@@ -4710,3 +4710,130 @@
     }
   }
 }
+
+//===--- CHECK: Empty statement body (-Wempty-body) ---------------------===//
+
+bool Sema::ShouldDiagnoseEmptyStmtBody(SourceLocation StmtLoc,
+                                       const NullStmt *Body,
+                                       bool QuietIfBodyOnSeparateLine) {
+  // Since this is a syntactic check, don't emit diagnostic for template
+  // instantiations, this just adds noise.
+  if (CurrentInstantiationScope)
+    return false;
+
+  // Do not warn if the body is a macro that expands to nothing, e.g:
+  //
+  // #define CALL(x)
+  // if (condition)
+  //   CALL(0);
+  //
+  if (Body->hasLeadingEmptyMacro())
+    return false;
+
+  if (QuietIfBodyOnSeparateLine) {
+    // Get line numbers of statement and body.
+    bool StmtLineInvalid;
+    unsigned StmtLine = SourceMgr.getSpellingLineNumber(StmtLoc,
+                                                        &StmtLineInvalid);
+    if (StmtLineInvalid)
+      return false;
+
+    bool BodyLineInvalid;
+    unsigned BodyLine = SourceMgr.getSpellingLineNumber(Body->getSemiLoc(),
+                                                        &BodyLineInvalid);
+    if (BodyLineInvalid)
+      return false;
+
+    // Warn if null statement and body are on the same line.
+    if (StmtLine != BodyLine)
+      return false;
+  }
+
+  return true;
+}
+
+void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc,
+                                 const Stmt *Body,
+                                 bool QuietIfBodyOnSeparateLine,
+                                 unsigned DiagID) {
+  // The body should be a null statement.
+  const NullStmt *NBody = dyn_cast<NullStmt>(Body);
+  if (!NBody)
+    return;
+
+  // Do the usual checks.
+  if (!ShouldDiagnoseEmptyStmtBody(StmtLoc, NBody, QuietIfBodyOnSeparateLine))
+    return;
+
+  Diag(NBody->getSemiLoc(), DiagID);
+  if (QuietIfBodyOnSeparateLine)
+     Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
+}
+
+void Sema::DiagnoseEmptyLoopBody(const Stmt *S,
+                                 const Stmt *PossibleBody) {
+  SourceLocation StmtLoc;
+  const Stmt *Body;
+  unsigned DiagID;
+  if (const ForStmt *FS = dyn_cast<ForStmt>(S)) {
+    StmtLoc = FS->getRParenLoc();
+    Body = FS->getBody();
+    DiagID = diag::warn_empty_for_body;
+  } else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) {
+    StmtLoc = WS->getCond()->getSourceRange().getEnd();
+    Body = WS->getBody();
+    DiagID = diag::warn_empty_while_body;
+  } else
+    return; // Neither `for' nor `while'.
+
+  // The body should be a null statement.
+  const NullStmt *NBody = dyn_cast<NullStmt>(Body);
+  if (!NBody)
+    return;
+
+  // Skip expensive checks if warning is disabled
+  if (Diags.getDiagnosticLevel(DiagID, NBody->getSemiLoc()) ==
+          DiagnosticsEngine::Ignored)
+    return;
+
+  // Do the usual checks.
+  if (!ShouldDiagnoseEmptyStmtBody(StmtLoc, NBody, true))
+    return;
+
+  // `for(...);' and `while(...);' are popular idioms, so in order to keep
+  // noise level low, emit diagnostics only if for/while are followed by a
+  // CompoundStmt, e.g.:
+  //    for (int i = 0; i < n; i++);
+  //    {
+  //      a(i);
+  //    }
+  // or if for/while is followed by a statement with more indentation
+  // than for/while itself:
+  //    for (int i = 0; i < n; i++);
+  //      a(i);
+  bool ProbableTypo = isa<CompoundStmt>(PossibleBody);
+  if (!ProbableTypo) {
+    bool BodyColInvalid;
+    unsigned BodyCol = SourceMgr.getPresumedColumnNumber(
+                             PossibleBody->getLocStart(),
+                             &BodyColInvalid);
+    if (BodyColInvalid)
+      return;
+
+    bool StmtColInvalid;
+    unsigned StmtCol = SourceMgr.getPresumedColumnNumber(
+                             S->getLocStart(),
+                             &StmtColInvalid);
+    if (StmtColInvalid)
+      return;
+
+    if (BodyCol > StmtCol)
+      ProbableTypo = true;
+  }
+
+  if (ProbableTypo) {
+    Diag(NBody->getSemiLoc(), DiagID);
+    Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
+  }
+}
+
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp	(revision 148970)
+++ lib/Sema/Sema.cpp	(working copy)
@@ -856,6 +856,17 @@
   }
 }
 
+void Sema::PushCompoundScope() {
+  getCurFunction()->CompoundScopes.push_back(CompoundScopeInfo());
+}
+
+void Sema::PopCompoundScope() {
+  FunctionScopeInfo *CurFunction = getCurFunction();
+  assert(!CurFunction->CompoundScopes.empty() && "mismatched push/pop");
+
+  CurFunction->CompoundScopes.pop_back();
+}
+
 /// \brief Determine whether any errors occurred within this function/method/
 /// block.
 bool Sema::hasAnyUnrecoverableErrorsInThisFunction() const {
Index: lib/Parse/ParseObjc.cpp
===================================================================
--- lib/Parse/ParseObjc.cpp	(revision 148970)
+++ lib/Parse/ParseObjc.cpp	(working copy)
@@ -2612,9 +2612,11 @@
   StmtResult FnBody(ParseCompoundStatementBody());
     
   // If the function body could not be parsed, make a bogus compoundstmt.
-  if (FnBody.isInvalid())
+  if (FnBody.isInvalid()) {
+    Sema::CompoundScopeRAII CompoundScope(Actions);
     FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc,
                                        MultiStmtArg(Actions), false);
+  }
     
   // Leave the function body scope.
   BodyScope.Exit();
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp	(revision 148970)
+++ lib/Parse/ParseStmt.cpp	(working copy)
@@ -700,7 +700,6 @@
   return ParseCompoundStatementBody(isStmtExpr);
 }
 
-
 /// ParseCompoundStatementBody - Parse a sequence of statements and invoke the
 /// ActOnCompoundStmt action.  This expects the '{' to be the current token, and
 /// consume the '}' at the end of the block.  It does not manipulate the scope
@@ -714,6 +713,8 @@
   if (T.consumeOpen())
     return StmtError();
 
+  Sema::CompoundScopeRAII CompoundScope(Actions);
+
   StmtVector Stmts(Actions);
 
   // "__label__ X, Y, Z;" is the GNU "Local Label" extension.  These are
@@ -1084,10 +1085,16 @@
   InnerScope.Exit();
   SwitchScope.Exit();
 
-  if (Body.isInvalid())
+  if (Body.isInvalid()) {
     // FIXME: Remove the case statement list from the Switch statement.
-    Body = Actions.ActOnNullStmt(Tok.getLocation());
 
+    // Put the synthesized null statement on the same line as the end of switch
+    // condition.
+    SourceLocation SynthesizedNullStmtLocation
+      = Cond.get()->getSourceRange().getEnd();
+    Body = Actions.ActOnNullStmt(SynthesizedNullStmtLocation);
+  }
+
   return Actions.ActOnFinishSwitchStmt(SwitchLoc, Switch.get(), Body.get());
 }
 
@@ -1956,9 +1963,11 @@
   StmtResult FnBody(ParseCompoundStatementBody());
 
   // If the function body could not be parsed, make a bogus compoundstmt.
-  if (FnBody.isInvalid())
+  if (FnBody.isInvalid()) {
+    Sema::CompoundScopeRAII CompoundScope(Actions);
     FnBody = Actions.ActOnCompoundStmt(LBraceLoc, LBraceLoc,
                                        MultiStmtArg(Actions), false);
+  }
 
   BodyScope.Exit();
   return Actions.ActOnFinishFunctionBody(Decl, FnBody.take());
@@ -1993,9 +2002,11 @@
   StmtResult FnBody(ParseCXXTryBlockCommon(TryLoc));
   // If we failed to parse the try-catch, we just give the function an empty
   // compound statement as the body.
-  if (FnBody.isInvalid())
+  if (FnBody.isInvalid()) {
+    Sema::CompoundScopeRAII CompoundScope(Actions);
     FnBody = Actions.ActOnCompoundStmt(LBraceLoc, LBraceLoc,
                                        MultiStmtArg(Actions), false);
+  }
 
   BodyScope.Exit();
   return Actions.ActOnFinishFunctionBody(Decl, FnBody.take());
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to