Updated patch according to reviewer's notes.

Hi rsmith,

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

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2870?vs=7574&id=7862#toc

Files:
  include/clang/Basic/DiagnosticParseKinds.td
  include/clang/Sema/Sema.h
  lib/Parse/ParseExprCXX.cpp
  lib/Parse/ParseStmt.cpp
  lib/Parse/ParseTemplate.cpp
  lib/Sema/SemaCXXScopeSpec.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/TreeTransform.h
  test/Parser/recovery.cpp
  test/SemaCXX/nested-name-spec.cpp
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -339,6 +339,11 @@
   "cannot template a using declaration">;
 def err_unexected_colon_in_nested_name_spec : Error<
   "unexpected ':' in nested name specifier; did you mean '::'?">;
+def err_unexected_token_in_nested_name_spec : Error<
+  "unexpected '%0' in nested name specifier; did you mean ':'?">;
+def err_nested_name_spec_is_not_class : Error<
+  "%0 cannot appear before '::' because it is not a class"
+  "%select{ or namespace|, namespace, or scoped enumeration}1; did you mean ':'?">;
 def err_bool_redeclaration : Error<
   "redeclaration of C++ built-in type 'bool'">;
 def ext_c11_static_assert : Extension<
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4363,15 +4363,25 @@
                                     IdentifierInfo &II,
                                     ParsedType ObjectType);
 
+  /// \brief Enumerates possible kinds of action taken if an error is found
+  /// while building nested name specifier.
+  enum ErrorRecoveryMode {
+    ReportAll,            ///< Report all errors.
+    DontReport,           ///< Called for error recovery, no diagnostics.
+    DontReportWrongMember ///< No error reported if id is found but is not a
+                          /// namespace/class/enum.
+  };
+
   bool BuildCXXNestedNameSpecifier(Scope *S,
                                    IdentifierInfo &Identifier,
                                    SourceLocation IdentifierLoc,
                                    SourceLocation CCLoc,
                                    QualType ObjectType,
                                    bool EnteringContext,
                                    CXXScopeSpec &SS,
                                    NamedDecl *ScopeLookupResult,
-                                   bool ErrorRecoveryLookup);
+                                   ErrorRecoveryMode RecoveryMode,
+                                   NamedDecl **IdDecl = 0);
 
   /// \brief The parser has parsed a nested-name-specifier 'identifier::'.
   ///
@@ -4394,14 +4404,23 @@
   /// output parameter (containing the full nested-name-specifier,
   /// including this new type).
   ///
+  /// \param RecoveryMode Specifies what kind of error recovery the method is
+  /// called for.
+  ///
+  /// \param IdDeclPtr If not null, it points to a pointer that is assigned
+  /// declaration of the last identifier before ::, if it is not a
+  /// namespace, class or scoped enum.
+  ///
   /// \returns true if an error occurred, false otherwise.
   bool ActOnCXXNestedNameSpecifier(Scope *S,
                                    IdentifierInfo &Identifier,
                                    SourceLocation IdentifierLoc,
                                    SourceLocation CCLoc,
                                    ParsedType ObjectType,
                                    bool EnteringContext,
-                                   CXXScopeSpec &SS);
+                                   CXXScopeSpec &SS,
+                                   ErrorRecoveryMode RecoveryMode = ReportAll,
+                                   NamedDecl **IdDeclPtr = 0);
 
   ExprResult ActOnDecltypeExpression(Expr *E);
 
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -365,12 +365,12 @@
 
       // Consume the template-id token.
       ConsumeToken();
-      
+
       assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
       SourceLocation CCLoc = ConsumeToken();
 
       HasScopeSpecifier = true;
-      
+
       ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
                                          TemplateId->NumArgs);
       
@@ -393,7 +393,6 @@
       continue;
     }
 
-
     // The rest of the nested-name-specifier possibilities start with
     // tok::identifier.
     if (Tok.isNot(tok::identifier))
@@ -434,23 +433,57 @@
         return false;
       }
 
+      if (ColonIsSacred) {
+        const Token &Next2 = GetLookAheadToken(2);
+        if (Next2.is(tok::kw_private) || Next2.is(tok::kw_protected) ||
+            Next2.is(tok::kw_public) || Next2.is(tok::kw_virtual)) {
+          Diag(Next2, diag::err_unexected_token_in_nested_name_spec)
+              << Next2.getName()
+              << FixItHint::CreateReplacement(Next.getLocation(), ":");
+          Token ColonColon;
+          PP.Lex(ColonColon);
+          ColonColon.setKind(tok::colon);
+          PP.EnterToken(ColonColon);
+          break;
+        }
+      }
+
       if (LastII)
         *LastII = &II;
 
       // We have an identifier followed by a '::'. Lookup this name
       // as the name in a nested-name-specifier.
+      Token Identifier = Tok;
       SourceLocation IdLoc = ConsumeToken();
       assert((Tok.is(tok::coloncolon) || Tok.is(tok::colon)) &&
              "NextToken() not working properly!");
+      Token ColonColon = Tok;
       SourceLocation CCLoc = ConsumeToken();
 
       CheckForLParenAfterColonColon();
 
-      HasScopeSpecifier = true;
+      Sema::ErrorRecoveryMode ErrMode =
+          ColonIsSacred ? Sema::DontReportWrongMember : Sema::ReportAll;
+      NamedDecl *IdDecl = 0;
       if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), II, IdLoc, CCLoc,
-                                              ObjectType, EnteringContext, SS))
+                                              ObjectType, EnteringContext, SS,
+                                              ErrMode, &IdDecl)) {
+        // Identifier is not recognized as a nested name, but we can have
+        // mistyped '::' instead of ':'.
+        if (ColonIsSacred && IdDecl) {
+          Diag(CCLoc, diag::err_nested_name_spec_is_not_class)
+              << &II << getLangOpts().CPlusPlus
+              << FixItHint::CreateReplacement(CCLoc, ":");
+          Diag(IdDecl->getLocation(), diag::note_declared_at);
+          ColonColon.setKind(tok::colon);
+          PP.EnterToken(Tok);
+          PP.EnterToken(ColonColon);
+          Tok = Identifier;
+          break;
+        }
         SS.SetInvalid(SourceRange(IdLoc, CCLoc));
-      
+      }
+      HasScopeSpecifier = true;
       continue;
     }
 
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp
+++ lib/Parse/ParseStmt.cpp
@@ -639,8 +639,9 @@
     ColonProtection.restore();
 
     if (TryConsumeToken(tok::colon, ColonLoc)) {
-    } else if (TryConsumeToken(tok::semi, ColonLoc)) {
-      // Treat "case blah;" as a typo for "case blah:".
+    } else if (TryConsumeToken(tok::semi, ColonLoc) ||
+               TryConsumeToken(tok::coloncolon, ColonLoc)) {
+      // Treat "case blah;" or "case blah::" as a typo for "case blah:".
       Diag(ColonLoc, diag::err_expected_after)
           << "'case'" << tok::colon
           << FixItHint::CreateReplacement(ColonLoc, ":");
Index: lib/Parse/ParseTemplate.cpp
===================================================================
--- lib/Parse/ParseTemplate.cpp
+++ lib/Parse/ParseTemplate.cpp
@@ -1161,6 +1161,7 @@
 Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) {
   // Template argument lists are constant-evaluation contexts.
   EnterExpressionEvaluationContext EvalContext(Actions,Sema::ConstantEvaluated);
+  ColonProtectionRAIIObject ColonProtection(*this, false);
 
   do {
     ParsedTemplateArgument Arg = ParseTemplateArgument();
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp
+++ lib/Sema/SemaCXXScopeSpec.cpp
@@ -377,25 +377,48 @@
 /// by ActOnCXXNestedNameSpecifier.
 ///
 /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in
-/// 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 definition time.
+/// that it contains an extra parameter \p ScopeLookupResult.
 ///
-/// 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 true 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. Nor will it extend \p SS with the scope
-/// specifier.
+/// \param S Scope in which the nested-name-specifier occurs.
+/// \param Identifier Identifier in the sequence "identifier" "::".
+/// \param IdentifierLoc Location of the \p Identifier.
+/// \param CCLoc Location of "::" following Identifier.
+/// \param ObjectType Type of postfix expression if the nested-name-specifier
+///        occurs in construct like: <tt>ptr->nns::f</tt>.
+/// \param EnteringContext If true, enter the context specified by the
+///        nested-name-specifier.
+/// \param SS Optional nested name specifier preceding the identifier.
+/// \param ScopeLookupResult Provides the result of name lookup within the
+///        scope of the nested-name-specifier that was computed at template
+///        definition time.
+/// \param RecoveryMode Specifies if the method is called to improve error
+///        recovery and what kind of recovery is performed.
+/// \param IdDeclPtr If not null, it points to a pointer that is assigned
+///        declaration of the last identifier before ::, if it is not a
+///        namespace, class or scoped enum.
+///
+/// If \p RecoveryMode equals to \p DontReport, then this call is used to
+/// improve error recovery.  This means that it should not emit diagnostics, it
+/// should just return true 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. Nor will it extend \p SS with the
+/// scope specifier.
+/// If \p RecoveryMode is \p ReportAll, all errors are diagnosed. Value
+/// \p DontReportWrongMember acts similarly except when identifier is not a
+/// namespace/class/enum, but is found as other entity. In this case diagnostics
+/// is not emitted, this should be done by caller. This mode is helpful for
+/// error recovery in cases when ':' is can be mistyped as '::', for instance in
+/// case statement: <tt>case AClass::AVal:: break;</tt>.
 bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
                                        IdentifierInfo &Identifier,
                                        SourceLocation IdentifierLoc,
                                        SourceLocation CCLoc,
                                        QualType ObjectType,
                                        bool EnteringContext,
                                        CXXScopeSpec &SS,
                                        NamedDecl *ScopeLookupResult,
-                                       bool ErrorRecoveryLookup) {
+                                       ErrorRecoveryMode RecoveryMode,
+                                       NamedDecl **IdDeclPtr) {
   LookupResult Found(*this, &Identifier, IdentifierLoc, 
                      LookupNestedNameSpecifierName);
 
@@ -416,7 +439,6 @@
     Found.setContextRange(SS.getRange());
   }
 
-
   bool ObjectTypeSearchedInScope = false;
   if (LookupCtx) {
     // Perform "qualified" name lookup into the declaration context we
@@ -471,20 +493,45 @@
         (!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
          !cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
     // Don't speculate if we're just trying to improve error recovery.
-    if (ErrorRecoveryLookup)
+    if (RecoveryMode == DontReport)
       return true;
-    
+
     // 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
     // a dependent nested-name-specifier.
     SS.Extend(Context, &Identifier, IdentifierLoc, CCLoc);
     return false;
-  } 
-  
+  }
+
   // FIXME: Deal with ambiguities cleanly.
 
-  if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) {
+  if (Found.empty() && RecoveryMode != DontReport) {
+    // If identifier is not found as class-name-or-namespace-name, but is found
+    // as other entity, don't look for typos if caller processes such errors.
+    LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName);
+    if (LookupCtx)
+      LookupQualifiedName(R, LookupCtx);
+    else if (S && !isDependent)
+      LookupName(R, S);
+    if (!R.empty()) {
+      if (RecoveryMode == ReportAll)
+        Diag(R.getNameLoc(), diag::err_expected_class_or_namespace)
+            << &Identifier << getLangOpts().CPlusPlus;
+      if (NamedDecl *ND = R.getAsSingle<NamedDecl>()) {
+        if (RecoveryMode == ReportAll)
+          Diag(ND->getLocation(),
+               diag::note_expected_class_or_namespace_declared_here)
+              << &Identifier;
+        if (IdDeclPtr)
+          *IdDeclPtr = ND;
+      }
+      return true;
+    }
+  }
+
+  if (Found.empty() && RecoveryMode != DontReport &&
+      !getLangOpts().MSVCCompat) {
     // We haven't found anything, and we're not recovering from a
     // different kind of error, so look for typos.
     DeclarationName Name = Found.getLookupName();
@@ -543,7 +590,7 @@
            !Context.hasSameType(
                             Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
                                Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
-         if (ErrorRecoveryLookup)
+         if (RecoveryMode == DontReport)
            return true;
 
          Diag(IdentifierLoc, 
@@ -558,9 +605,9 @@
        }
     }
 
-    // If we're just performing this lookup for error-recovery purposes, 
+    // If we're just performing this lookup for error-recovery purposes,
     // don't extend the nested-name-specifier. Just return now.
-    if (ErrorRecoveryLookup)
+    if (RecoveryMode == DontReport)
       return false;
     
     if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) {
@@ -618,7 +665,7 @@
 
   // Otherwise, we have an error case.  If we don't want diagnostics, just
   // return an error now.
-  if (ErrorRecoveryLookup)
+  if (RecoveryMode == DontReport)
     return true;
 
   // If we didn't find anything during our lookup, try again with
@@ -681,14 +728,17 @@
                                        SourceLocation CCLoc,
                                        ParsedType ObjectType,
                                        bool EnteringContext,
-                                       CXXScopeSpec &SS) {
+                                       CXXScopeSpec &SS,
+                                       ErrorRecoveryMode ErrMode,
+                                       NamedDecl **IdDeclPtr) {
   if (SS.isInvalid())
     return true;
-  
+
   return BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, CCLoc,
                                      GetTypeFromParser(ObjectType),
                                      EnteringContext, SS, 
-                                     /*ScopeLookupResult=*/0, false);
+                                     /*ScopeLookupResult=*/0, ErrMode,
+                                     IdDeclPtr);
 }
 
 bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
@@ -732,9 +782,11 @@
   return !BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, ColonLoc,
                                       GetTypeFromParser(ObjectType),
                                       EnteringContext, SS, 
-                                      /*ScopeLookupResult=*/0, true);
+                                      /*ScopeLookupResult=*/0,
+                                      DontReport);
 }
 
+
 bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
                                        CXXScopeSpec &SS,
                                        SourceLocation TemplateKWLoc,
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -584,16 +584,15 @@
                                             CorrectionCandidateCallback *CCC) {
   DeclarationNameInfo NameInfo(Name, NameLoc);
   ObjCMethodDecl *CurMethod = getCurMethodDecl();
-  
+
   if (NextToken.is(tok::coloncolon)) {
     BuildCXXNestedNameSpecifier(S, *Name, NameLoc, NextToken.getLocation(),
-                                QualType(), false, SS, 0, false);
-    
+                                QualType(), false, SS, 0, ReportAll);
   }
-      
+
   LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName);
   LookupParsedName(Result, S, &SS, !CurMethod);
-  
+
   // Perform lookup for Objective-C instance variables (including automatically 
   // synthesized instance variables), if we're in an Objective-C method.
   // FIXME: This lookup really, really needs to be folded in to the normal
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -2919,7 +2919,8 @@
                                               Q.getLocalBeginLoc(),
                                               Q.getLocalEndLoc(),
                                               ObjectType, false, SS,
-                                              FirstQualifierInScope, false))
+                                              FirstQualifierInScope,
+                                              Sema::ReportAll))
         return NestedNameSpecifierLoc();
 
       break;
Index: test/Parser/recovery.cpp
===================================================================
--- test/Parser/recovery.cpp
+++ test/Parser/recovery.cpp
@@ -135,3 +135,70 @@
 template <typename> struct TempID;
 template <> struct TempID<BadType> : BadType, EnumID::Garbage; // expected-error{{use of undeclared identifier 'BadType'}}
 }
+
+namespace pr15133 {
+  namespace ns {
+    const int V1 = 1;   // expected-note {{declared here}}
+  }
+  struct C1 {
+    enum E1 { V2 = 2 }; // expected-note {{declared here}}
+    static const int V3 = 3; // expected-note {{declared here}}
+  };
+  enum E2 {
+    V4 = 4,   // expected-note {{declared here}}
+    V6        // expected-note {{declared here}}
+  };
+  enum class EC3 { V0 = 0, V5 = 5 }; // expected-note {{declared here}}
+  void func_3();
+
+  void func_1(int x) {
+    switch(x) {
+    case 0: break;
+    case ns::V1:: break; // expected-error{{'V1' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+    case C1::V2:: break; // expected-error{{'V2' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+    case C1::V3:: break; // expected-error{{'V3' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+    case V4:: break; // expected-error{{'V4' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+    case V6:: func_3();   // expected-error{{'V6' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+    }
+  }
+  void func_2(EC3 x) {
+    switch(x) {
+    case EC3::V0:  break;
+    case EC3::V5:: break; // expected-error{{'V5' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+    }
+  }
+
+  template<class T> struct TS1 {
+    typedef int A;
+  };
+  template<class T> void func(int x) {
+    switch(x) {
+    case TS1<T>::A:: break;  // expected-error{{expected unqualified-id}}
+    }
+  };
+  void mainf() {
+    func<int>(1);
+  }
+
+  struct S {
+    static int n;  // expected-note{{declared here}}
+    int nn;        // expected-note 2 {{declared here}}
+  };
+
+  int func_3(int x) {
+    return x ? S::n :: 0;  // expected-error{{'n' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+  }
+  int func_4(int x, S &s) {
+    return x ? s.nn :: x;  // expected-error{{'nn' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+  }
+  int func_5(int x, S &s) {
+    return x ? s.nn :: S::n;  // expected-error{{'nn' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}}
+  }
+
+  struct S2 {
+    struct S3;
+  };
+
+  struct S2 :: S3 :: public S2 {  // expected-error{{unexpected 'public' in nested name specifier; did you mean ':'?}}
+  };
+}
Index: test/SemaCXX/nested-name-spec.cpp
===================================================================
--- test/SemaCXX/nested-name-spec.cpp
+++ test/SemaCXX/nested-name-spec.cpp
@@ -8,13 +8,12 @@
     static int Ag1();
     static int Ag2();
   };
-  int ax;
+  int ax; // expected-note {{'ax' declared here}}
   void Af();
 }
 
 A:: ; // expected-error {{expected unqualified-id}}
-// FIXME: there is a member 'ax'; it's just not a class.
-::A::ax::undef ex3; // expected-error {{no member named 'ax'}}
+::A::ax::undef ex3; // expected-error {{'ax' is not a class, namespace, or scoped enumeration}}
 A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}}
 
 int A::C::Ag1() { return 0; }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to