Better diagnostics for range-based for loops with bad range types.
authorSam Panzer <espanz@gmail.com>
Tue, 21 Aug 2012 00:52:01 +0000 (00:52 +0000)
committerSam Panzer <espanz@gmail.com>
Tue, 21 Aug 2012 00:52:01 +0000 (00:52 +0000)
The old error message stating that 'begin' was an undeclared identifier
is replaced with a new message explaining that the error is in the range
expression, along with which of the begin() and end() functions was
problematic if relevant.

Additionally, if the range was a pointer type or defines operator*,
attempt to dereference the range, and offer a FixIt if the modified range
works.

llvm-svn: 162248

clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseStmt.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/TreeTransform.h
clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
clang/test/SemaCXX/for-range-dereference.cpp [new file with mode: 0644]
clang/test/SemaCXX/for-range-no-std.cpp
clang/test/SemaCXX/typo-correction.cpp

index 065b5b1..aaa022e 100644 (file)
@@ -1415,7 +1415,15 @@ def err_for_range_member_begin_end_mismatch : Error<
   "range type %0 has '%select{begin|end}1' member but no '%select{end|begin}1' member">;
 def err_for_range_begin_end_types_differ : Error<
   "'begin' and 'end' must return the same type (got %0 and %1)">;
-def note_for_range_type : Note<"range has type %0">;
+def note_in_for_range: Note<
+  "when looking up '%select{begin|end}0' function for range expression "
+  "of type %1">;
+def err_for_range_invalid: Error<
+  "invalid range expression of type %0; no viable '%select{begin|end}1' "
+  "function available">;
+def err_for_range_dereference : Error<
+  "invalid range expression of type %0; did you mean to dereference it "
+  "with '*'?">;
 def note_for_range_begin_end : Note<
   "selected '%select{begin|end}0' %select{function|template }1%2 with iterator type %3">;
 
index 4be532c..4e2a971 100644 (file)
@@ -1924,6 +1924,30 @@ public:
                                    OverloadCandidateSet &CandidateSet,
                                    bool PartialOverloading = false);
 
+  // An enum used to represent the different possible results of building a
+  // range-based for loop.
+  enum ForRangeStatus {
+    FRS_Success,
+    FRS_NoViableFunction,
+    FRS_DiagnosticIssued
+  };
+
+  // An enum to represent whether something is dealing with a call to begin()
+  // or a call to end() in a range-based for loop.
+  enum BeginEndFunction {
+    BEF_begin,
+    BEF_end
+  };
+
+  ForRangeStatus BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc,
+                                           SourceLocation RangeLoc,
+                                           VarDecl *Decl,
+                                           BeginEndFunction BEF,
+                                           const DeclarationNameInfo &NameInfo,
+                                           LookupResult &MemberLookup,
+                                           OverloadCandidateSet *CandidateSet,
+                                           Expr *Range, ExprResult *CallExpr);
+
   ExprResult BuildOverloadedCallExpr(Scope *S, Expr *Fn,
                                      UnresolvedLookupExpr *ULE,
                                      SourceLocation LParenLoc,
@@ -1932,6 +1956,12 @@ public:
                                      Expr *ExecConfig,
                                      bool AllowTypoCorrection=true);
 
+  bool buildOverloadedCallSet(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
+                              Expr **Args, unsigned NumArgs,
+                              SourceLocation RParenLoc,
+                              OverloadCandidateSet *CandidateSet,
+                              ExprResult *Result);
+
   ExprResult CreateOverloadedUnaryOp(SourceLocation OpLoc,
                                      unsigned Opc,
                                      const UnresolvedSetImpl &Fns,
@@ -2512,13 +2542,15 @@ public:
 
   StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *LoopVar,
                                   SourceLocation ColonLoc, Expr *Collection,
-                                  SourceLocation RParenLoc);
+                                  SourceLocation RParenLoc,
+                                  bool ShouldTryDeref);
   StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc,
                                   SourceLocation ColonLoc,
                                   Stmt *RangeDecl, Stmt *BeginEndDecl,
                                   Expr *Cond, Expr *Inc,
                                   Stmt *LoopVarDecl,
-                                  SourceLocation RParenLoc);
+                                  SourceLocation RParenLoc,
+                                  bool ShouldTryDeref);
   StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body);
 
   StmtResult ActOnGotoStmt(SourceLocation GotoLoc,
index 0716f6f..091d4aa 100644 (file)
@@ -1441,7 +1441,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
     ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, FirstPart.take(),
                                                 ForRangeInit.ColonLoc,
                                                 ForRangeInit.RangeExpr.get(),
-                                                T.getCloseLocation());
+                                                T.getCloseLocation(), true);
 
 
   // Similarly, we need to do the semantic analysis for a for-range
index 9382f7d..efc5b6e 100644 (file)
@@ -9695,20 +9695,15 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
                                RParenLoc);
 }
 
-/// ResolveOverloadedCallFn - Given the call expression that calls Fn
-/// (which eventually refers to the declaration Func) and the call
-/// arguments Args/NumArgs, attempt to resolve the function call down
-/// to a specific function. If overload resolution succeeds, returns
-/// the function declaration produced by overload
-/// resolution. Otherwise, emits diagnostics, deletes all of the
-/// arguments and Fn, and returns NULL.
-ExprResult
-Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
-                              SourceLocation LParenLoc,
-                              Expr **Args, unsigned NumArgs,
-                              SourceLocation RParenLoc,
-                              Expr *ExecConfig,
-                              bool AllowTypoCorrection) {
+/// \brief Constructs and populates an OverloadedCandidateSet from
+/// the given function.
+/// \returns true when an the ExprResult output parameter has been set.
+bool Sema::buildOverloadedCallSet(Scope *S, Expr *Fn,
+                                  UnresolvedLookupExpr *ULE,
+                                  Expr **Args, unsigned NumArgs,
+                                  SourceLocation RParenLoc,
+                                  OverloadCandidateSet *CandidateSet,
+                                  ExprResult *Result) {
 #ifndef NDEBUG
   if (ULE->requiresADL()) {
     // To do ADL, we must have found an unqualified name.
@@ -9730,20 +9725,20 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
 #endif
 
   UnbridgedCastsSet UnbridgedCasts;
-  if (checkArgPlaceholdersForOverload(*this, Args, NumArgs, UnbridgedCasts))
-    return ExprError();
-
-  OverloadCandidateSet CandidateSet(Fn->getExprLoc());
+  if (checkArgPlaceholdersForOverload(*this, Args, NumArgs, UnbridgedCasts)) {
+    *Result = ExprError();
+    return true;
+  }
 
   // Add the functions denoted by the callee to the set of candidate
   // functions, including those from argument-dependent lookup.
   AddOverloadedCallCandidates(ULE, llvm::makeArrayRef(Args, NumArgs),
-                              CandidateSet);
+                              *CandidateSet);
 
   // If we found nothing, try to recover.
   // BuildRecoveryCallExpr diagnoses the error itself, so we just bail
   // out if it fails.
-  if (CandidateSet.empty()) {
+  if (CandidateSet->empty()) {
     // In Microsoft mode, if we are inside a template class member function then
     // create a type dependent CallExpr. The goal is to postpone name lookup
     // to instantiation time to be able to search into type dependent base
@@ -9754,32 +9749,50 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
                                           Context.DependentTy, VK_RValue,
                                           RParenLoc);
       CE->setTypeDependent(true);
-      return Owned(CE);
+      *Result = Owned(CE);
+      return true;
     }
-    return BuildRecoveryCallExpr(*this, S, Fn, ULE, LParenLoc,
-                                 llvm::MutableArrayRef<Expr *>(Args, NumArgs),
-                                 RParenLoc, /*EmptyLookup=*/true,
-                                 AllowTypoCorrection);
+    return false;
   }
 
   UnbridgedCasts.restore();
+  return false;
+}
 
-  OverloadCandidateSet::iterator Best;
-  switch (CandidateSet.BestViableFunction(*this, Fn->getLocStart(), Best)) {
+/// FinishOverloadedCallExpr - given an OverloadCandidateSet, builds and returns
+/// the completed call expression. If overload resolution fails, emits
+/// diagnostics and returns ExprError()
+static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
+                                           UnresolvedLookupExpr *ULE,
+                                           SourceLocation LParenLoc,
+                                           Expr **Args, unsigned NumArgs,
+                                           SourceLocation RParenLoc,
+                                           Expr *ExecConfig,
+                                           OverloadCandidateSet *CandidateSet,
+                                           OverloadCandidateSet::iterator *Best,
+                                           OverloadingResult OverloadResult,
+                                           bool AllowTypoCorrection) {
+  if (CandidateSet->empty())
+    return BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc,
+                                 llvm::MutableArrayRef<Expr *>(Args, NumArgs),
+                                 RParenLoc, /*EmptyLookup=*/true,
+                                 AllowTypoCorrection);
+
+  switch (OverloadResult) {
   case OR_Success: {
-    FunctionDecl *FDecl = Best->Function;
-    MarkFunctionReferenced(Fn->getExprLoc(), FDecl);
-    CheckUnresolvedLookupAccess(ULE, Best->FoundDecl);
-    DiagnoseUseOfDecl(FDecl, ULE->getNameLoc());
-    Fn = FixOverloadedFunctionReference(Fn, Best->FoundDecl, FDecl);
-    return BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs, RParenLoc,
-                                 ExecConfig);
+    FunctionDecl *FDecl = (*Best)->Function;
+    SemaRef.MarkFunctionReferenced(Fn->getExprLoc(), FDecl);
+    SemaRef.CheckUnresolvedLookupAccess(ULE, (*Best)->FoundDecl);
+    SemaRef.DiagnoseUseOfDecl(FDecl, ULE->getNameLoc());
+    Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
+    return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs,
+                                         RParenLoc, ExecConfig);
   }
 
   case OR_No_Viable_Function: {
     // Try to recover by looking for viable functions which the user might
     // have meant to call.
-    ExprResult Recovery = BuildRecoveryCallExpr(*this, S, Fn, ULE, LParenLoc,
+    ExprResult Recovery = BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc,
                                   llvm::MutableArrayRef<Expr *>(Args, NumArgs),
                                                 RParenLoc,
                                                 /*EmptyLookup=*/false,
@@ -9787,44 +9800,73 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
     if (!Recovery.isInvalid())
       return Recovery;
 
-    Diag(Fn->getLocStart(),
+    SemaRef.Diag(Fn->getLocStart(),
          diag::err_ovl_no_viable_function_in_call)
       << ULE->getName() << Fn->getSourceRange();
-    CandidateSet.NoteCandidates(*this, OCD_AllCandidates,
-                                llvm::makeArrayRef(Args, NumArgs));
+    CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates,
+                                 llvm::makeArrayRef(Args, NumArgs));
     break;
   }
 
   case OR_Ambiguous:
-    Diag(Fn->getLocStart(), diag::err_ovl_ambiguous_call)
+    SemaRef.Diag(Fn->getLocStart(), diag::err_ovl_ambiguous_call)
       << ULE->getName() << Fn->getSourceRange();
-    CandidateSet.NoteCandidates(*this, OCD_ViableCandidates,
-                                llvm::makeArrayRef(Args, NumArgs));
+    CandidateSet->NoteCandidates(SemaRef, OCD_ViableCandidates,
+                                 llvm::makeArrayRef(Args, NumArgs));
     break;
 
-  case OR_Deleted:
-    {
-      Diag(Fn->getLocStart(), diag::err_ovl_deleted_call)
-        << Best->Function->isDeleted()
-        << ULE->getName()
-        << getDeletedOrUnavailableSuffix(Best->Function)
-        << Fn->getSourceRange();
-      CandidateSet.NoteCandidates(*this, OCD_AllCandidates,
-                                  llvm::makeArrayRef(Args, NumArgs));
+  case OR_Deleted: {
+    SemaRef.Diag(Fn->getLocStart(), diag::err_ovl_deleted_call)
+      << (*Best)->Function->isDeleted()
+      << ULE->getName()
+      << SemaRef.getDeletedOrUnavailableSuffix((*Best)->Function)
+      << Fn->getSourceRange();
+    CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates,
+                                 llvm::makeArrayRef(Args, NumArgs));
 
-      // We emitted an error for the unvailable/deleted function call but keep
-      // the call in the AST.
-      FunctionDecl *FDecl = Best->Function;
-      Fn = FixOverloadedFunctionReference(Fn, Best->FoundDecl, FDecl);
-      return BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs,
-                                   RParenLoc, ExecConfig);
-    }
+    // We emitted an error for the unvailable/deleted function call but keep
+    // the call in the AST.
+    FunctionDecl *FDecl = (*Best)->Function;
+    Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
+    return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs,
+                                 RParenLoc, ExecConfig);
+  }
   }
 
   // Overload resolution failed.
   return ExprError();
 }
 
+/// BuildOverloadedCallExpr - Given the call expression that calls Fn
+/// (which eventually refers to the declaration Func) and the call
+/// arguments Args/NumArgs, attempt to resolve the function call down
+/// to a specific function. If overload resolution succeeds, returns
+/// the call expression produced by overload resolution.
+/// Otherwise, emits diagnostics and returns ExprError.
+ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
+                                         UnresolvedLookupExpr *ULE,
+                                         SourceLocation LParenLoc,
+                                         Expr **Args, unsigned NumArgs,
+                                         SourceLocation RParenLoc,
+                                         Expr *ExecConfig,
+                                         bool AllowTypoCorrection) {
+  OverloadCandidateSet CandidateSet(Fn->getExprLoc());
+  ExprResult result;
+
+  if (buildOverloadedCallSet(S, Fn, ULE, Args, NumArgs, LParenLoc,
+                             &CandidateSet, &result))
+    return result;
+
+  OverloadCandidateSet::iterator Best;
+  OverloadingResult OverloadResult =
+      CandidateSet.BestViableFunction(*this, Fn->getLocStart(), Best);
+
+  return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args, NumArgs,
+                                  RParenLoc, ExecConfig, &CandidateSet,
+                                  &Best, OverloadResult,
+                                  AllowTypoCorrection);
+}
+
 static bool IsOverloaded(const UnresolvedSetImpl &Functions) {
   return Functions.size() > 1 ||
     (Functions.size() == 1 && isa<FunctionTemplateDecl>(*Functions.begin()));
@@ -11199,6 +11241,83 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R,
   return MaybeBindToTemporary(UDL);
 }
 
+/// Build a call to 'begin' or 'end' for a C++11 for-range statement. If the
+/// given LookupResult is non-empty, it is assumed to describe a member which
+/// will be invoked. Otherwise, the function will be found via argument
+/// dependent lookup.
+/// CallExpr is set to a valid expression and FRS_Success returned on success,
+/// otherwise CallExpr is set to ExprError() and some non-success value
+/// is returned.
+Sema::ForRangeStatus
+Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc,
+                                SourceLocation RangeLoc, VarDecl *Decl,
+                                BeginEndFunction BEF,
+                                const DeclarationNameInfo &NameInfo,
+                                LookupResult &MemberLookup,
+                                OverloadCandidateSet *CandidateSet,
+                                Expr *Range, ExprResult *CallExpr) {
+  CandidateSet->clear();
+  if (!MemberLookup.empty()) {
+    ExprResult MemberRef =
+        BuildMemberReferenceExpr(Range, Range->getType(), Loc,
+                                 /*IsPtr=*/false, CXXScopeSpec(),
+                                 /*TemplateKWLoc=*/SourceLocation(),
+                                 /*FirstQualifierInScope=*/0,
+                                 MemberLookup,
+                                 /*TemplateArgs=*/0);
+    if (MemberRef.isInvalid()) {
+      *CallExpr = ExprError();
+      Diag(Range->getLocStart(), diag::note_in_for_range)
+          << RangeLoc << BEF << Range->getType();
+      return FRS_DiagnosticIssued;
+    }
+    *CallExpr = ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(), Loc, 0);
+    if (CallExpr->isInvalid()) {
+      *CallExpr = ExprError();
+      Diag(Range->getLocStart(), diag::note_in_for_range)
+          << RangeLoc << BEF << Range->getType();
+      return FRS_DiagnosticIssued;
+    }
+  } else {
+    UnresolvedSet<0> FoundNames;
+    // C++11 [stmt.ranged]p1: For the purposes of this name lookup, namespace
+    // std is an associated namespace.
+    UnresolvedLookupExpr *Fn =
+      UnresolvedLookupExpr::Create(Context, /*NamingClass=*/0,
+                                   NestedNameSpecifierLoc(), NameInfo,
+                                   /*NeedsADL=*/true, /*Overloaded=*/false,
+                                   FoundNames.begin(), FoundNames.end(),
+                                   /*LookInStdNamespace=*/true);
+
+    bool CandidateSetError = buildOverloadedCallSet(S, Fn, Fn, &Range, 1, Loc,
+                                                    CandidateSet, CallExpr);
+    if (CandidateSet->empty() || CandidateSetError) {
+      *CallExpr = ExprError();
+      return FRS_NoViableFunction;
+    }
+    OverloadCandidateSet::iterator Best;
+    OverloadingResult OverloadResult =
+        CandidateSet->BestViableFunction(*this, Fn->getLocStart(), Best);
+
+    if (OverloadResult == OR_No_Viable_Function) {
+      *CallExpr = ExprError();
+      return FRS_NoViableFunction;
+    }
+    *CallExpr = FinishOverloadedCallExpr(*this, S, Fn, Fn, Loc, &Range, 1,
+                                         Loc, 0, CandidateSet, &Best,
+                                         OverloadResult,
+                                         /*AllowTypoCorrection=*/false);
+    if (CallExpr->isInvalid() || OverloadResult != OR_Success) {
+      *CallExpr = ExprError();
+      Diag(Range->getLocStart(), diag::note_in_for_range)
+          << RangeLoc << BEF << Range->getType();
+      return FRS_DiagnosticIssued;
+    }
+  }
+  return FRS_Success;
+}
+
+
 /// FixOverloadedFunctionReference - E is an expression that refers to
 /// a C++ overloaded function (possibly with some parentheses and
 /// perhaps a '&' around it). We have resolved the overloaded function
index bb025a4..8367c1a 100644 (file)
@@ -1549,25 +1549,6 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
                                                    ForLoc, RParenLoc));
 }
 
-namespace {
-
-enum BeginEndFunction {
-  BEF_begin,
-  BEF_end
-};
-
-/// Build a variable declaration for a for-range statement.
-static VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc,
-                                     QualType Type, const char *Name) {
-  DeclContext *DC = SemaRef.CurContext;
-  IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name);
-  TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc);
-  VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type,
-                                  TInfo, SC_Auto, SC_None);
-  Decl->setImplicit();
-  return Decl;
-}
-
 /// Finish building a variable declaration for a for-range statement.
 /// \return true if an error occurs.
 static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init,
@@ -1600,12 +1581,14 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init,
   return false;
 }
 
+namespace {
+
 /// Produce a note indicating which begin/end function was implicitly called
-/// by a C++0x for-range statement. This is often not obvious from the code,
+/// by a C++11 for-range statement. This is often not obvious from the code,
 /// nor from the diagnostics produced when analysing the implicit expressions
 /// required in a for-range statement.
 void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
-                                  BeginEndFunction BEF) {
+                                  Sema::BeginEndFunction BEF) {
   CallExpr *CE = dyn_cast<CallExpr>(E);
   if (!CE)
     return;
@@ -1626,56 +1609,16 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
     << BEF << IsTemplate << Description << E->getType();
 }
 
-/// Build a call to 'begin' or 'end' for a C++0x for-range statement. If the
-/// given LookupResult is non-empty, it is assumed to describe a member which
-/// will be invoked. Otherwise, the function will be found via argument
-/// dependent lookup.
-static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S,
-                                            SourceLocation Loc,
-                                            VarDecl *Decl,
-                                            BeginEndFunction BEF,
-                                            const DeclarationNameInfo &NameInfo,
-                                            LookupResult &MemberLookup,
-                                            Expr *Range) {
-  ExprResult CallExpr;
-  if (!MemberLookup.empty()) {
-    ExprResult MemberRef =
-      SemaRef.BuildMemberReferenceExpr(Range, Range->getType(), Loc,
-                                       /*IsPtr=*/false, CXXScopeSpec(),
-                                       /*TemplateKWLoc=*/SourceLocation(),
-                                       /*FirstQualifierInScope=*/0,
-                                       MemberLookup,
-                                       /*TemplateArgs=*/0);
-    if (MemberRef.isInvalid())
-      return ExprError();
-    CallExpr = SemaRef.ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(),
-                                     Loc, 0);
-    if (CallExpr.isInvalid())
-      return ExprError();
-  } else {
-    UnresolvedSet<0> FoundNames;
-    // C++0x [stmt.ranged]p1: For the purposes of this name lookup, namespace
-    // std is an associated namespace.
-    UnresolvedLookupExpr *Fn =
-      UnresolvedLookupExpr::Create(SemaRef.Context, /*NamingClass=*/0,
-                                   NestedNameSpecifierLoc(), NameInfo,
-                                   /*NeedsADL=*/true, /*Overloaded=*/false,
-                                   FoundNames.begin(), FoundNames.end(),
-                                   /*LookInStdNamespace=*/true);
-    CallExpr = SemaRef.BuildOverloadedCallExpr(S, Fn, Fn, Loc, &Range, 1, Loc,
-                                               0, /*AllowTypoCorrection=*/false);
-    if (CallExpr.isInvalid()) {
-      SemaRef.Diag(Range->getLocStart(), diag::note_for_range_type)
-        << Range->getType();
-      return ExprError();
-    }
-  }
-  if (FinishForRangeVarDecl(SemaRef, Decl, CallExpr.get(), Loc,
-                            diag::err_for_range_iter_deduction_failure)) {
-    NoteForRangeBeginEndFunction(SemaRef, CallExpr.get(), BEF);
-    return ExprError();
-  }
-  return CallExpr;
+/// Build a variable declaration for a for-range statement.
+VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc,
+                              QualType Type, const char *Name) {
+  DeclContext *DC = SemaRef.CurContext;
+  IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name);
+  TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc);
+  VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type,
+                                  TInfo, SC_Auto, SC_None);
+  Decl->setImplicit();
+  return Decl;
 }
 
 }
@@ -1706,7 +1649,7 @@ static bool ObjCEnumerationCollection(Expr *Collection) {
 StmtResult
 Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc,
                            Stmt *First, SourceLocation ColonLoc, Expr *Range,
-                           SourceLocation RParenLoc) {
+                           SourceLocation RParenLoc, bool ShouldTryDeref) {
   if (!First || !Range)
     return StmtError();
 
@@ -1744,7 +1687,111 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc,
 
   return BuildCXXForRangeStmt(ForLoc, ColonLoc, RangeDecl.get(),
                               /*BeginEndDecl=*/0, /*Cond=*/0, /*Inc=*/0, DS,
-                              RParenLoc);
+                              RParenLoc, ShouldTryDeref);
+}
+
+/// \brief Create the initialization, compare, and increment steps for
+/// the range-based for loop expression.
+/// This function does not handle array-based for loops,
+/// which are created in Sema::BuildCXXForRangeStmt.
+///
+/// \returns a ForRangeStatus indicating success or what kind of error occurred.
+/// BeginExpr and EndExpr are set and FRS_Success is returned on success;
+/// CandidateSet and BEF are set and some non-success value is returned on
+/// failure.
+static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,
+                                            Expr *BeginRange, Expr *EndRange,
+                                            QualType RangeType,
+                                            VarDecl *BeginVar,
+                                            VarDecl *EndVar,
+                                            SourceLocation ColonLoc,
+                                            OverloadCandidateSet *CandidateSet,
+                                            ExprResult *BeginExpr,
+                                            ExprResult *EndExpr,
+                                            Sema::BeginEndFunction *BEF) {
+  DeclarationNameInfo BeginNameInfo(
+      &SemaRef.PP.getIdentifierTable().get("begin"), ColonLoc);
+  DeclarationNameInfo EndNameInfo(&SemaRef.PP.getIdentifierTable().get("end"),
+                                  ColonLoc);
+
+  LookupResult BeginMemberLookup(SemaRef, BeginNameInfo,
+                                 Sema::LookupMemberName);
+  LookupResult EndMemberLookup(SemaRef, EndNameInfo, Sema::LookupMemberName);
+
+  if (CXXRecordDecl *D = RangeType->getAsCXXRecordDecl()) {
+    // - if _RangeT is a class type, the unqualified-ids begin and end are
+    //   looked up in the scope of class _RangeT as if by class member access
+    //   lookup (3.4.5), and if either (or both) finds at least one
+    //   declaration, begin-expr and end-expr are __range.begin() and
+    //   __range.end(), respectively;
+    SemaRef.LookupQualifiedName(BeginMemberLookup, D);
+    SemaRef.LookupQualifiedName(EndMemberLookup, D);
+
+    if (BeginMemberLookup.empty() != EndMemberLookup.empty()) {
+      SourceLocation RangeLoc = BeginVar->getLocation();
+      *BEF = BeginMemberLookup.empty() ? Sema::BEF_end : Sema::BEF_begin;
+
+      SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch)
+          << RangeLoc << BeginRange->getType() << *BEF;
+      return Sema::FRS_DiagnosticIssued;
+    }
+  } else {
+    // - otherwise, begin-expr and end-expr are begin(__range) and
+    //   end(__range), respectively, where begin and end are looked up with
+    //   argument-dependent lookup (3.4.2). For the purposes of this name
+    //   lookup, namespace std is an associated namespace.
+
+  }
+
+  *BEF = Sema::BEF_begin;
+  Sema::ForRangeStatus RangeStatus =
+      SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, BeginVar,
+                                        Sema::BEF_begin, BeginNameInfo,
+                                        BeginMemberLookup, CandidateSet,
+                                        BeginRange, BeginExpr);
+
+  if (RangeStatus != Sema::FRS_Success)
+    return RangeStatus;
+  if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc,
+                            diag::err_for_range_iter_deduction_failure)) {
+    NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF);
+    return Sema::FRS_DiagnosticIssued;
+  }
+
+  *BEF = Sema::BEF_end;
+  RangeStatus =
+      SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, EndVar,
+                                        Sema::BEF_end, EndNameInfo,
+                                        EndMemberLookup, CandidateSet,
+                                        EndRange, EndExpr);
+  if (RangeStatus != Sema::FRS_Success)
+    return RangeStatus;
+  if (FinishForRangeVarDecl(SemaRef, EndVar, EndExpr->get(), ColonLoc,
+                            diag::err_for_range_iter_deduction_failure)) {
+    NoteForRangeBeginEndFunction(SemaRef, EndExpr->get(), *BEF);
+    return Sema::FRS_DiagnosticIssued;
+  }
+  return Sema::FRS_Success;
+}
+
+/// Speculatively attempt to dereference an invalid range expression.
+/// This function will not emit diagnostics, but returns StmtError if
+/// an error occurs.
+static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
+                                                 SourceLocation ForLoc,
+                                                 Stmt *LoopVarDecl,
+                                                 SourceLocation ColonLoc,
+                                                 Expr *Range,
+                                                 SourceLocation RangeLoc,
+                                                 SourceLocation RParenLoc) {
+  Sema::SFINAETrap Trap(SemaRef);
+  ExprResult AdjustedRange = SemaRef.BuildUnaryOp(S, RangeLoc, UO_Deref, Range);
+  StmtResult SR =
+    SemaRef.ActOnCXXForRangeStmt(ForLoc, LoopVarDecl, ColonLoc,
+                                 AdjustedRange.get(), RParenLoc, false);
+  if (Trap.hasErrorOccurred())
+    return StmtError();
+  return SR;
 }
 
 /// BuildCXXForRangeStmt - Build or instantiate a C++0x for-range statement.
@@ -1752,7 +1799,7 @@ StmtResult
 Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
                            Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond,
                            Expr *Inc, Stmt *LoopVarDecl,
-                           SourceLocation RParenLoc) {
+                           SourceLocation RParenLoc, bool ShouldTryDeref) {
   Scope *S = getCurScope();
 
   DeclStmt *RangeDS = cast<DeclStmt>(RangeDecl);
@@ -1838,50 +1885,49 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
         return StmtError();
       }
     } else {
-      DeclarationNameInfo BeginNameInfo(&PP.getIdentifierTable().get("begin"),
-                                        ColonLoc);
-      DeclarationNameInfo EndNameInfo(&PP.getIdentifierTable().get("end"),
-                                      ColonLoc);
-
-      LookupResult BeginMemberLookup(*this, BeginNameInfo, LookupMemberName);
-      LookupResult EndMemberLookup(*this, EndNameInfo, LookupMemberName);
-
-      if (CXXRecordDecl *D = RangeType->getAsCXXRecordDecl()) {
-        // - if _RangeT is a class type, the unqualified-ids begin and end are
-        //   looked up in the scope of class _RangeT as if by class member access
-        //   lookup (3.4.5), and if either (or both) finds at least one
-        //   declaration, begin-expr and end-expr are __range.begin() and
-        //   __range.end(), respectively;
-        LookupQualifiedName(BeginMemberLookup, D);
-        LookupQualifiedName(EndMemberLookup, D);
-
-        if (BeginMemberLookup.empty() != EndMemberLookup.empty()) {
-          Diag(ColonLoc, diag::err_for_range_member_begin_end_mismatch)
-            << RangeType << BeginMemberLookup.empty();
-          return StmtError();
+      OverloadCandidateSet CandidateSet(RangeLoc);
+      Sema::BeginEndFunction BEFFailure;
+      ForRangeStatus RangeStatus =
+          BuildNonArrayForRange(*this, S, BeginRangeRef.get(),
+                                EndRangeRef.get(), RangeType,
+                                BeginVar, EndVar, ColonLoc, &CandidateSet,
+                                &BeginExpr, &EndExpr, &BEFFailure);
+
+      // If building the range failed, try dereferencing the range expression
+      // unless a diagnostic was issued or the end function is problematic.
+      if (ShouldTryDeref && RangeStatus == FRS_NoViableFunction &&
+          BEFFailure == BEF_begin) {
+        StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc,
+                                                       LoopVarDecl, ColonLoc,
+                                                       Range, RangeLoc,
+                                                       RParenLoc);
+        if (!SR.isInvalid()) {
+          // The attempt to dereference would succeed; return the result of
+          // recovery.
+          Diag(RangeLoc, diag::err_for_range_dereference)
+              << RangeLoc << RangeType
+              << FixItHint::CreateInsertion(RangeLoc, "*");
+          return SR;
         }
-      } else {
-        // - otherwise, begin-expr and end-expr are begin(__range) and
-        //   end(__range), respectively, where begin and end are looked up with
-        //   argument-dependent lookup (3.4.2). For the purposes of this name
-        //   lookup, namespace std is an associated namespace.
       }
 
-      BeginExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, BeginVar,
-                                            BEF_begin, BeginNameInfo,
-                                            BeginMemberLookup,
-                                            BeginRangeRef.get());
-      if (BeginExpr.isInvalid())
-        return StmtError();
-
-      EndExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, EndVar,
-                                          BEF_end, EndNameInfo,
-                                          EndMemberLookup, EndRangeRef.get());
-      if (EndExpr.isInvalid())
+      // Otherwise, emit diagnostics if we haven't already.
+      if (RangeStatus == FRS_NoViableFunction) {
+        Expr *Range = BEFFailure ?  EndRangeRef.get() : BeginRangeRef.get();
+        Diag(Range->getLocStart(), diag::err_for_range_invalid)
+            << RangeLoc << Range->getType() << BEFFailure;
+        CandidateSet.NoteCandidates(*this, OCD_AllCandidates,
+                                    llvm::makeArrayRef(&Range, /*NumArgs=*/1));
+      }
+      // Return an error if no fix was discovered.
+      if (RangeStatus != FRS_Success)
         return StmtError();
     }
 
-    // C++0x [decl.spec.auto]p6: BeginType and EndType must be the same.
+    assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
+           "invalid range expression in for loop");
+
+    // C++11 [dcl.spec.auto]p7: BeginType and EndType must be the same.
     QualType BeginType = BeginVar->getType(), EndType = EndVar->getType();
     if (!Context.hasSameType(BeginType, EndType)) {
       Diag(RangeLoc, diag::err_for_range_begin_end_types_differ)
index 05a3c61..391c0b3 100644 (file)
@@ -1338,7 +1338,7 @@ public:
                                     Stmt *LoopVar,
                                     SourceLocation RParenLoc) {
     return getSema().BuildCXXForRangeStmt(ForLoc, ColonLoc, Range, BeginEnd,
-                                          Cond, Inc, LoopVar, RParenLoc);
+                                          Cond, Inc, LoopVar, RParenLoc, false);
   }
 
   /// \brief Build a new C++0x range-based for statement.
index 96bb472..66e30f5 100644 (file)
@@ -3,7 +3,7 @@
 struct pr12960 {
   int begin;
   void foo(int x) {
-    for (int& it : x) { // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type 'int'}}
+    for (int& it : x) { // expected-error {{invalid range expression of type 'int'; no viable 'begin' function available}}
     }
   }
 };
@@ -116,9 +116,9 @@ void g() {
   struct NoEndADL {
     null_t alt_begin();
   };
-  for (auto u : NoBeginADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type 'NoBeginADL'}}
+  for (auto u : NoBeginADL()) { // expected-error {{invalid range expression of type 'NoBeginADL'; no viable 'begin' function available}}
   }
-  for (auto u : NoEndADL()) { // expected-error {{no matching function for call to 'end'}} expected-note {{range has type 'NoEndADL'}}
+  for (auto u : NoEndADL()) { // expected-error {{invalid range expression of type 'NoEndADL'; no viable 'end' function available}}
   }
 
   struct NoBegin {
@@ -156,8 +156,7 @@ void g() {
   for (int n : NoCopy()) { // ok
   }
 
-  for (int n : 42) { // expected-error {{no matching function for call to 'begin'}} \
-                        expected-note {{range has type 'int'}}
+  for (int n : 42) { // expected-error {{invalid range expression of type 'int'; no viable 'begin' function available}}
   }
 
   for (auto a : *also_incomplete) { // expected-error {{cannot use incomplete type 'struct Incomplete' as a range}}
@@ -179,9 +178,10 @@ template void h<A(&)[13], int>(A(&)[13]); // expected-note {{requested here}}
 
 template<typename T>
 void i(T t) {
-  for (auto u : t) { // expected-error {{no matching function for call to 'begin'}} \
+  for (auto u : t) { // expected-error {{invalid range expression of type 'A *'; no viable 'begin' function available}} \
                         expected-error {{member function 'begin' not viable}} \
-                        expected-note {{range has type}}
+                        expected-note {{when looking up 'begin' function}}
+
   }
 }
 template void i<A[13]>(A*); // expected-note {{requested here}}
@@ -204,9 +204,10 @@ void end(VoidBeginADL);
 void j() {
   for (auto u : NS::ADL()) {
   }
-  for (auto u : NS::NoADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}}
+  for (auto u : NS::NoADL()) { // expected-error {{invalid range expression of type 'NS::NoADL'; no viable 'begin' function available}}
   }
   for (auto a : VoidBeginADL()) { // expected-error {{cannot use type 'void' as an iterator}}
+
   }
 }
 
@@ -215,4 +216,3 @@ void example() {
   for (int &x : array)
     x *= 2;
 }
-
diff --git a/clang/test/SemaCXX/for-range-dereference.cpp b/clang/test/SemaCXX/for-range-dereference.cpp
new file mode 100644 (file)
index 0000000..3bf50f3
--- /dev/null
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+struct Data { };
+struct T {
+  Data *begin();
+  Data *end();
+};
+
+struct NoBegin {
+  Data *end();
+};
+
+struct DeletedEnd : public T {
+  Data *begin();
+  Data *end() = delete; //expected-note {{function has been explicitly marked deleted here}}
+};
+
+struct DeletedADLBegin { };
+
+int* begin(DeletedADLBegin) = delete; //expected-note {{candidate function has been explicitly deleted}} \
+ expected-note 6 {{candidate function not viable: no known conversion}}
+
+struct PrivateEnd {
+  Data *begin();
+
+ private:
+  Data *end(); // expected-note 1 {{declared private here}}
+};
+
+struct ADLNoEnd { };
+Data * begin(ADLNoEnd); // expected-note 7 {{candidate function not viable: no known conversion}}
+
+struct OverloadedStar {
+  T operator*();
+};
+
+void f() {
+  T t;
+  for (auto i : t) { }
+  T *pt;
+  for (auto i : pt) { } // expected-error{{invalid range expression of type 'T *'; did you mean to dereference it with '*'?}}
+
+  int arr[10];
+  for (auto i : arr) { }
+  int (*parr)[10];
+  for (auto i : parr) { }// expected-error{{invalid range expression of type 'int (*)[10]'; did you mean to dereference it with '*'?}}
+
+  NoBegin NB;
+  for (auto i : NB) { }// expected-error{{range type 'NoBegin' has 'end' member but no 'begin' member}}
+  NoBegin *pNB;
+  for (auto i : pNB) { }// expected-error{{invalid range expression of type 'NoBegin *'; no viable 'begin' function available}}
+  NoBegin **ppNB;
+  for (auto i : ppNB) { }// expected-error{{invalid range expression of type 'NoBegin **'; no viable 'begin' function available}}
+  NoBegin *****pppppNB;
+  for (auto i : pppppNB) { }// expected-error{{invalid range expression of type 'NoBegin *****'; no viable 'begin' function available}}
+
+  ADLNoEnd ANE;
+  for (auto i : ANE) { } // expected-error{{invalid range expression of type 'ADLNoEnd'; no viable 'end' function available}}
+  ADLNoEnd *pANE;
+  for (auto i : pANE) { } // expected-error{{invalid range expression of type 'ADLNoEnd *'; no viable 'begin' function available}}
+
+  DeletedEnd DE;
+  for (auto i : DE) { } // expected-error{{attempt to use a deleted function}} \
+expected-note {{when looking up 'end' function for range expression of type 'DeletedEnd'}}
+  DeletedEnd *pDE;
+
+  for (auto i : pDE) { } // expected-error {{invalid range expression of type 'DeletedEnd *'; no viable 'begin' function available}}
+
+  PrivateEnd PE;
+  // FIXME: This diagnostic should be improved, as it does not specify that
+  // the range is invalid.
+  for (auto i : PE) { } // expected-error{{'end' is a private member of 'PrivateEnd'}}
+
+  // FIXME: This diagnostic should be improved as well. It should not mention a
+  // deleted function, and we should not issue a FixIt suggesting a dereference.
+  PrivateEnd *pPE;
+  for (auto i : pPE) { }// expected-error {{invalid range expression of type 'PrivateEnd *'}}
+
+  DeletedADLBegin DAB;
+  for (auto i : DAB) { } // expected-error {{call to deleted function 'begin'}}\
+  expected-note {{when looking up 'begin' function for range expression of type 'DeletedADLBegin'}}
+
+  OverloadedStar OS;
+  for (auto i : *OS) { }
+
+  for (auto i : OS) { } // expected-error {{invalid range expression of type 'OverloadedStar'; did you mean to dereference it with '*'?}}
+}
index fa42ca4..66b445e 100644 (file)
@@ -31,10 +31,10 @@ NS::iter end(NS::NoADL);
 void f() {
   int a[] = {1, 2, 3};
   for (auto b : S()) {} // ok
-  for (auto b : T()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}}
+  for (auto b : T()) {} // expected-error {{invalid range expression of type 'T'}}
   for (auto b : a) {} // ok
   for (int b : NS::ADL()) {} // ok
-  for (int b : NS::NoADL()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}}
+  for (int b : NS::NoADL()) {} // expected-error {{invalid range expression of type 'NS::NoADL'}}
 }
 
 void PR11601() {
index 919edca..c21ef51 100644 (file)
@@ -155,7 +155,7 @@ void Test3() {
 struct R {};
 bool begun(R);
 void RangeTest() {
-  for (auto b : R()) {} // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type}}
+  for (auto b : R()) {} // expected-error {{invalid range expression of type 'R'}}
 }
 
 // PR 12019 - Avoid infinite mutual recursion in DiagnoseInvalidRedeclaration