reapply my patch for PR4451, which improves diagnostics for :: vs : confusion.
This time with a fix to bail out when in a dependent context.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90730 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index d852127..238048f 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -132,7 +132,7 @@
   
   CXXScopeSpec SS;
   // Parse (optional) nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false, false);
 
   if (SS.isInvalid() || Tok.isNot(tok::identifier)) {
     Diag(Tok, diag::err_expected_namespace_name);
@@ -260,7 +260,7 @@
   
   CXXScopeSpec SS;
   // Parse (optional) nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false, false);
 
   IdentifierInfo *NamespcName = 0;
   SourceLocation IdentLoc = SourceLocation();
@@ -322,7 +322,7 @@
     IsTypeName = false;
 
   // Parse nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false, false);
 
   AttributeList *AttrList = 0;
 
@@ -601,7 +601,7 @@
   // Parse the (optional) nested-name-specifier.
   CXXScopeSpec SS;
   if (getLang().CPlusPlus &&
-      ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true))
+      ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true, true))
     if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
       Diag(Tok, diag::err_expected_ident);
 
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 4eb6cd5..ae2a47b 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -45,10 +45,14 @@
 /// \param EnteringContext whether we will be entering into the context of
 /// the nested-name-specifier after parsing it.
 ///
+/// \param ColonIsSacred - If this is true, then a colon is valid after the
+/// specifier, so we should not try to recover from colons aggressively.
+///
 /// \returns true if a scope specifier was parsed.
 bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
                                             Action::TypeTy *ObjectType,
-                                            bool EnteringContext) {
+                                            bool EnteringContext,
+                                            bool ColonIsSacred) {
   assert(getLang().CPlusPlus &&
          "Call sites of this function should be guarded by checking for C++");
 
@@ -214,11 +218,29 @@
     //   namespace-name '::'
     //   nested-name-specifier identifier '::'
     Token Next = NextToken();
+    
+    // If we get foo:bar, this is almost certainly a typo for foo::bar.  Recover
+    // and emit a fixit hint for it.
+    if (Next.is(tok::colon) && !ColonIsSacred &&
+        Actions.IsInvalidUnlessNestedName(CurScope, SS, II, ObjectType,
+                                          EnteringContext) &&
+        // If the token after the colon isn't an identifier, it's still an
+        // error, but they probably meant something else strange so don't
+        // recover like this.
+        PP.LookAhead(1).is(tok::identifier)) {
+      Diag(Next, diag::err_unexected_colon_in_nested_name_spec)
+        << CodeModificationHint::CreateReplacement(Next.getLocation(), "::");
+      
+      // Recover as if the user wrote '::'.
+      Next.setKind(tok::coloncolon);
+    }
+    
     if (Next.is(tok::coloncolon)) {
       // We have an identifier followed by a '::'. Lookup this name
       // as the name in a nested-name-specifier.
       SourceLocation IdLoc = ConsumeToken();
-      assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
+      assert((Tok.is(tok::coloncolon) || Tok.is(tok::colon)) &&
+             "NextToken() not working properly!");
       SourceLocation CCLoc = ConsumeToken();
 
       if (!HasScopeSpecifier) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 9606821..2fb3176 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2000,7 +2000,8 @@
                                           IdentifierInfo &II,
                                           QualType ObjectType,
                                           NamedDecl *ScopeLookupResult,
-                                          bool EnteringContext);
+                                          bool EnteringContext,
+                                          bool ErrorRecoveryLookup);
 
   virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
                                                   const CXXScopeSpec &SS,
@@ -2010,6 +2011,12 @@
                                                   TypeTy *ObjectType,
                                                   bool EnteringContext);
 
+  virtual bool IsInvalidUnlessNestedName(Scope *S,
+                                         const CXXScopeSpec &SS,
+                                         IdentifierInfo &II,
+                                         TypeTy *ObjectType,
+                                         bool EnteringContext);
+  
   /// ActOnCXXNestedNameSpecifier - Called during parsing of a
   /// nested-name-specifier that involves a template-id, e.g.,
   /// "foo::bar<int, float>::", and now we need to build a scope
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 14db774..3c5dd54 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -330,6 +330,12 @@
 /// that it contains an extra parameter \p ScopeLookupResult, which provides
 /// the result of name lookup within the scope of the nested-name-specifier
 /// that was computed at template definitino time.
+///
+/// If ErrorRecoveryLookup is true, then this call is used to improve error
+/// recovery.  This means that it should not emit diagnostics, it should
+/// just return null on failure.  It also means it should only return a valid
+/// scope if it *knows* that the result is correct.  It should not return in a
+/// dependent context, for example.
 Sema::CXXScopeTy *Sema::BuildCXXNestedNameSpecifier(Scope *S,
                                                     const CXXScopeSpec &SS,
                                                     SourceLocation IdLoc,
@@ -337,7 +343,8 @@
                                                     IdentifierInfo &II,
                                                     QualType ObjectType,
                                                   NamedDecl *ScopeLookupResult,
-                                                    bool EnteringContext) {
+                                                    bool EnteringContext,
+                                                    bool ErrorRecoveryLookup) {
   NestedNameSpecifier *Prefix
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
 
@@ -403,6 +410,10 @@
       ObjectTypeSearchedInScope = true;
     }
   } else if (isDependent) {
+    // Don't speculate if we're just trying to improve error recovery.
+    if (ErrorRecoveryLookup)
+      return 0;
+    
     // We were not able to compute the declaration context for a dependent
     // base object type or prior nested-name-specifier, so this
     // nested-name-specifier refers to an unknown specialization. Just build
@@ -442,14 +453,17 @@
            !Context.hasSameType(
                             Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
                                Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
+             if (ErrorRecoveryLookup)
+               return 0;
+             
              Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
                << &II;
              Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
                << ObjectType;
              Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);
 
-             // Fall through so that we'll pick the name we found in the object type,
-             // since that's probably what the user wanted anyway.
+             // Fall through so that we'll pick the name we found in the object
+             // type, since that's probably what the user wanted anyway.
            }
     }
 
@@ -469,6 +483,11 @@
                                        T.getTypePtr());
   }
 
+  // Otherwise, we have an error case.  If we don't want diagnostics, just
+  // return an error now.
+  if (ErrorRecoveryLookup)
+    return 0;
+
   // If we didn't find anything during our lookup, try again with
   // ordinary name lookup, which can help us produce better error
   // messages.
@@ -509,7 +528,23 @@
                                                     bool EnteringContext) {
   return BuildCXXNestedNameSpecifier(S, SS, IdLoc, CCLoc, II,
                                      QualType::getFromOpaquePtr(ObjectTypePtr),
-                                     /*ScopeLookupResult=*/0, EnteringContext);
+                                     /*ScopeLookupResult=*/0, EnteringContext,
+                                     false);
+}
+
+/// IsInvalidUnlessNestedName - This method is used for error recovery
+/// purposes to determine whether the specified identifier is only valid as
+/// a nested name specifier, for example a namespace name.  It is
+/// conservatively correct to always return false from this method.
+///
+/// The arguments are the same as those passed to ActOnCXXNestedNameSpecifier.
+bool Sema::IsInvalidUnlessNestedName(Scope *S, const CXXScopeSpec &SS,
+                                     IdentifierInfo &II, TypeTy *ObjectType,
+                                     bool EnteringContext) {
+  return BuildCXXNestedNameSpecifier(S, SS, SourceLocation(), SourceLocation(),
+                                     II, QualType::getFromOpaquePtr(ObjectType),
+                                     /*ScopeLookupResult=*/0, EnteringContext,
+                                     true);
 }
 
 Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index b029a37..8c251e7 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -5407,7 +5407,7 @@
                                                         Range.getEnd(), II,
                                                         ObjectType,
                                                         FirstQualifierInScope,
-                                                        false));
+                                                        false, false));
 }
 
 template<typename Derived>