[cfe-commits] [PATCH] Better diagnostics for iterators missing ++, !=, and * in range-based for loops.

Sam Panzer reviews at llvm-reviews.chandlerc.com
Wed Aug 29 12:16:02 PDT 2012


Hi rsmith,

This patch adds an additional note when ++__begin, !=__begin, *__begin, or the loop variable declaration is invalid, stating the high-level source of the error (e.g. "cannot use type T as an iterator; no operator ++ is defined"). Ideally, this would be the error message rather than a note, but this should be suffcient to clarify the existing errors.

http://llvm-reviews.chandlerc.com/D32

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaStmt.cpp
  test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp

Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -1424,6 +1424,11 @@
 def err_for_range_dereference : Error<
   "invalid range expression of type %0; did you mean to dereference it "
   "with '*'?">;
+def note_for_range_invalid_iterator : Note <
+  "cannot use type %0 as iterator in range-based for loop; "
+  "no viable operator %select{!=|*|++}1 is defined">;
+def note_for_range_loop_variable_type : Note <
+  "loop variable %0 has type %1">;
 def note_for_range_begin_end : Note<
   "selected '%select{begin|end}0' %select{function|template }1%2 with iterator type %3">;
 
Index: lib/Sema/SemaStmt.cpp
===================================================================
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -1959,6 +1959,8 @@
     NotEqExpr = ActOnBooleanCondition(S, ColonLoc, NotEqExpr.get());
     NotEqExpr = ActOnFinishFullExpr(NotEqExpr.get());
     if (NotEqExpr.isInvalid()) {
+      Diag(RangeLoc, diag::note_for_range_invalid_iterator)
+        << RangeLoc << BeginRangeRef.get()->getType() << 0 ;
       NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
       if (!Context.hasSameType(BeginType, EndType))
         NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
@@ -1974,6 +1976,8 @@
     IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get());
     IncrExpr = ActOnFinishFullExpr(IncrExpr.get());
     if (IncrExpr.isInvalid()) {
+      Diag(RangeLoc, diag::note_for_range_invalid_iterator)
+        << RangeLoc << BeginRangeRef.get()->getType() << 2;
       NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
       return StmtError();
     }
@@ -1986,16 +1990,21 @@
 
     ExprResult DerefExpr = ActOnUnaryOp(S, ColonLoc, tok::star, BeginRef.get());
     if (DerefExpr.isInvalid()) {
+      Diag(RangeLoc, diag::note_for_range_invalid_iterator)
+        << RangeLoc << BeginRangeRef.get()->getType() << 1;
       NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
       return StmtError();
     }
 
     // Attach  *__begin  as initializer for VD.
     if (!LoopVar->isInvalidDecl()) {
       AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false,
                            /*TypeMayContainAuto=*/true);
-      if (LoopVar->isInvalidDecl())
+      if (LoopVar->isInvalidDecl()) {
+        Diag(RangeLoc, diag::note_for_range_loop_variable_type)
+          << RangeLoc << LoopVar->getName() << DerefExpr.get()->getType();
         NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+      }
     }
   } else {
     // The range is implicitly used as a placeholder when it is dependent.
Index: test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
===================================================================
--- test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
+++ test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
@@ -47,21 +47,28 @@
 void g() {
   for (int a : A())
     A __begin;
-  for (char *a : A()) { // expected-error {{cannot initialize a variable of type 'char *' with an lvalue of type 'int'}}
+  for (char *a : A()) { // expected-error {{cannot initialize a variable of type 'char *' with an lvalue of type 'int'}} \
+    expected-note {{loop variable a has type 'int'}}
   }
-  for (char *a : B()) { // expected-error {{cannot initialize a variable of type 'char *' with an lvalue of type 'int'}}
+  for (char *a : B()) { // expected-error {{cannot initialize a variable of type 'char *' with an lvalue of type 'int'}} \
+    expected-note {{loop variable a has type 'int'}}
+
   }
   // FIXME: Terrible diagnostic here. auto deduction should fail, but does not!
   for (double a : f) { // expected-error {{cannot use type '<overloaded function type>' as a range}}
   }
   for (auto a : A()) {
   }
   for (auto a : B()) {
   }
-  for (auto *a : A()) { // expected-error {{variable 'a' with type 'auto *' has incompatible initializer of type 'int'}}
+  for (auto *a : A()) { // expected-error {{variable 'a' with type 'auto *' has incompatible initializer of type 'int'}} \
+    expected-note {{loop variable a has type 'int'}}
+
   }
   // : is not a typo for :: here.
-  for (A NS:A()) { // expected-error {{no viable conversion from 'int' to 'A'}}
+  for (A NS:A()) { // expected-error {{no viable conversion from 'int' to 'A'}} \
+    expected-note {{loop variable NS has type 'int'}}
+
   }
   for (auto not_in_scope : not_in_scope) { // expected-error {{use of undeclared identifier 'not_in_scope'}}
   }
@@ -129,22 +136,35 @@
   };
   for (auto u : NoBegin()) { // expected-error {{range type 'NoBegin' has 'end' member but no 'begin' member}}
   }
-  for (auto u : NoEnd()) { // expected-error {{range type 'NoEnd' has 'begin' member but no 'end' member}}
+  for (auto u : NoEnd()) { // expected-error {{range type 'NoEnd' has 'begin' member but no 'end' member}} 
   }
 
   struct NoIncr {
     void *begin(); // expected-note {{selected 'begin' function with iterator type 'void *'}}
     void *end();
   };
-  for (auto u : NoIncr()) { // expected-error {{arithmetic on a pointer to void}}
+  for (auto u : NoIncr()) { // expected-error {{arithmetic on a pointer to void}}\
+    expected-note {{cannot use type 'NoIncr' as iterator in range-based for loop; no viable operator ++ is defined}}
   }
 
   struct NoNotEq {
     NoNotEq begin(); // expected-note {{selected 'begin' function with iterator type 'NoNotEq'}}
     NoNotEq end();
     void operator++();
   };
-  for (auto u : NoNotEq()) { // expected-error {{invalid operands to binary expression}}
+  for (auto u : NoNotEq()) { // expected-error {{invalid operands to binary expression}}\
+    expected-note {{cannot use type 'NoNotEq' as iterator in range-based for loop; no viable operator != is defined}}
+  }
+
+  struct NoDeref {
+    NoDeref begin(); // expected-note {{selected 'begin' function}}
+    NoDeref end();
+    void operator++();
+    bool operator!=(NoDeref &);
+  };
+
+  for (auto u : NoDeref()) { // expected-error {{indirection requires pointer operand}} \
+    expected-note {{cannot use type 'NoDeref' as iterator in range-based for loop; no viable operator * is defined}}
   }
 
   struct NoCopy {
@@ -165,7 +185,8 @@
 
 template<typename T, typename U>
 void h(T t) {
-  for (U u : t) { // expected-error {{no viable conversion from 'A' to 'int'}}
+  for (U u : t) { // expected-error {{no viable conversion from 'A' to 'int'}} \
+    expected-note {{loop variable u has type 'A'}}
   }
   for (auto u : t) {
   }
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D32.1.patch
Type: text/x-patch
Size: 6627 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20120829/85883e1a/attachment.bin>


More information about the cfe-commits mailing list