sepavloff created this revision.
sepavloff added reviewers: rsmith, doug.gregor.
sepavloff added a subscriber: cfe-commits.

If a declaration references a function from global namespace by its
qualified name and if that function return type is a class or enum, there
is possible ambiguity. The declaration:
```
    friend A::B::C();
```
may be considered as a declaration of a function `::C()` that returns
`A::B`, or a function `::B::C()` that returns `A`.

With this change when the compiler sees 'A::B' while parsing decl-spec, it
tries to find `B` within 'A'. If it finds, 'A::B' is treated as a part of
qualified name. If it doesn't and the current declaration declares a
function, '::B' is assumed to be a part of declarator. For non-function
declarations 'B' can be searched for in the global namespace to improve
diagnostics.

This changes fixes https://llvm.org/bugs/show_bug.cgi?id=28422.

https://reviews.llvm.org/D23684

Files:
  include/clang/Parse/Parser.h
  include/clang/Sema/Sema.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseExprCXX.cpp
  lib/Parse/Parser.cpp
  lib/Parse/RAIIObjectsForParser.h
  lib/Sema/SemaCXXScopeSpec.cpp
  lib/Sema/TreeTransform.h
  test/CXX/drs/dr1xx.cpp
  test/CXX/drs/dr2xx.cpp
  test/Parser/cxx-decl.cpp
  test/SemaCXX/nested-name-spec2.cpp
  test/SemaCXX/pr18284-crash-on-invalid.cpp
  test/SemaCXX/typo-correction.cpp

Index: test/SemaCXX/typo-correction.cpp
===================================================================
--- test/SemaCXX/typo-correction.cpp
+++ test/SemaCXX/typo-correction.cpp
@@ -478,22 +478,17 @@
 }
 }
 
-namespace PR18213 {  // expected-note {{'PR18213' declared here}}
+namespace PR18213 {
 struct WrapperInfo {
   int i;
 };
 
 template <typename T> struct Wrappable {
   static WrapperInfo kWrapperInfo;
 };
 
-// Note the space before "::PR18213" is intended and needed, as it highlights
-// the actual typo, which is the leading "::".
-// TODO: Suggest removing the "::" from "::PR18213" (the right correction)
-// instead of incorrectly suggesting dropping "PR18213::WrapperInfo::".
 template <>
-PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 };  // expected-error {{no member named 'PR18213' in 'PR18213::WrapperInfo'; did you mean simply 'PR18213'?}} \
-                                                                       // expected-error {{C++ requires a type specifier for all declarations}}
+PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 };
 }
 
 namespace PR18651 {
Index: test/SemaCXX/pr18284-crash-on-invalid.cpp
===================================================================
--- test/SemaCXX/pr18284-crash-on-invalid.cpp
+++ test/SemaCXX/pr18284-crash-on-invalid.cpp
@@ -5,7 +5,7 @@
 class A { };
 class C { A a; };
 
-A::RunTest() {} // expected-error {{C++ requires a type specifier for all declarations}}
+A::RunTest() {} // expected-error {{definition or redeclaration of 'RunTest' cannot name the global scope}}
 
 void f() {
   new C;
@@ -16,7 +16,7 @@
 class A { };
 class C : public A { };
 
-A::RunTest() {} // expected-error {{C++ requires a type specifier for all declarations}}
+A::RunTest() {} // expected-error {{definition or redeclaration of 'RunTest' cannot name the global scope}}
 
 void f() {
   new C;
Index: test/SemaCXX/nested-name-spec2.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/nested-name-spec2.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+enum E1 { ABCD };
+
+struct C1 {
+  struct C2 {};
+  enum E2 { XYZ };
+};
+
+E1   func_01();
+E1 ::func_01();  // expected-warning{{extra qualification on member}}
+
+C1   func_02();
+C1 ::func_02();  // expected-warning{{extra qualification on member}}
+
+C1::C2   func_03();
+C1::C2 ::func_03();  // expected-warning{{extra qualification on member}}
+
+C1::E2   func_04();
+C1::E2 ::func_04();  // expected-warning{{extra qualification on member}}
+
+class C3 {
+  friend E1     ::func_01();
+  friend C1     ::func_02();
+  friend C1::C2 ::func_03();
+  friend C1::E2 ::func_04();
+};
+
+
+namespace N1 {
+  class C3 {
+    friend ::E1     ::func_01();
+    friend ::C1     ::func_02();
+    friend ::C1::C2 ::func_03();
+    friend ::C1::E2 ::func_04();
+  };
+}
+
+
+namespace N2 {
+  enum E1 { ABCD };
+
+  struct C1 {
+    struct C2 {};
+    enum E2 { XYZ };
+  };
+
+  E1     func_01();
+  C1     func_02();
+  C1::C2 func_03();
+  C1::E2 func_04();
+}
+
+namespace N3 {
+  class C4 {
+    friend ::N2::E1     ::N2::func_01();
+    friend ::N2::C1     ::N2::func_02();
+    friend ::N2::C1::C2 ::N2::func_03();
+    friend ::N2::C1::E2 ::N2::func_04();
+  };
+}
Index: test/Parser/cxx-decl.cpp
===================================================================
--- test/Parser/cxx-decl.cpp
+++ test/Parser/cxx-decl.cpp
@@ -249,8 +249,11 @@
     FooBar(); // expected-error {{missing return type for function 'FooBar'; did you mean the constructor name 'Foobar'?}}
     ~FooBar(); // expected-error {{expected the class name after '~' to name a destructor}}
   };
-  FooBar::FooBar() {} // expected-error {{undeclared}} expected-error {{missing return type}}
-  FooBar::~FooBar() {} // expected-error {{undeclared}} expected-error {{expected the class name}}
+  FooBar::FooBar() {}  // expected-error {{unknown type name 'FooBar'; did you mean 'Foobar'?}}
+                       // expected-error@-1{{definition or redeclaration of 'FooBar' cannot name the global scope}}
+                       // expected-error@-2{{use of undeclared identifier 'FooBar'; did you mean 'Foobar'?}}
+                       // expected-note@-3{{'FooBar' declared here}}
+  FooBar::~FooBar() {} // expected-error {{'FooBar' is not a class, namespace, or enumeration}}
 }
 
 namespace DuplicateFriend {
Index: test/CXX/drs/dr2xx.cpp
===================================================================
--- test/CXX/drs/dr2xx.cpp
+++ test/CXX/drs/dr2xx.cpp
@@ -1038,7 +1038,7 @@
   struct B b; // expected-error {{refers to a typedef}}
   struct C c; // expected-error {{refers to a typedef}}
 
-  B::B() {} // expected-error {{requires a type specifier}}
+  B::B() {} // expected-error{{definition or redeclaration of 'B' cannot name the global scope}}
   B::A() {} // ok
   C::~C() {} // expected-error {{destructor cannot be declared using a typedef 'C' (aka 'const dr298::A') of the class name}}
 
Index: test/CXX/drs/dr1xx.cpp
===================================================================
--- test/CXX/drs/dr1xx.cpp
+++ test/CXX/drs/dr1xx.cpp
@@ -226,18 +226,14 @@
 // dr124: dup 201
 
 // dr125: yes
-struct dr125_A { struct dr125_B {}; }; // expected-note {{here}}
+struct dr125_A { struct dr125_B {}; };
 dr125_A::dr125_B dr125_C();
 namespace dr125_B { dr125_A dr125_C(); }
 namespace dr125 {
   struct X {
     friend dr125_A::dr125_B (::dr125_C)(); // ok
     friend dr125_A (::dr125_B::dr125_C)(); // ok
-    friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}}
-    // expected-error@-1 {{missing exception specification}}
-#if __cplusplus >= 201103L
-    // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}}
-#endif
+    friend dr125_A::dr125_B ::dr125_C();
   };
 }
 
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -3442,7 +3442,7 @@
       Sema::NestedNameSpecInfo IdInfo(QNNS->getAsIdentifier(),
                           Q.getLocalBeginLoc(), Q.getLocalEndLoc(), ObjectType);
       if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, false,
-                                              SS, FirstQualifierInScope, false))
+                  SS, FirstQualifierInScope, false) != Sema::NNSResult::Success)
         return NestedNameSpecifierLoc();
     }
       break;
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp
+++ lib/Sema/SemaCXXScopeSpec.cpp
@@ -473,13 +473,13 @@
 /// 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.
-bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
-                                       NestedNameSpecInfo &IdInfo,
-                                       bool EnteringContext,
-                                       CXXScopeSpec &SS,
-                                       NamedDecl *ScopeLookupResult,
-                                       bool ErrorRecoveryLookup,
-                                       bool *IsCorrectedToColon) {
+Sema::NNSResult Sema::BuildCXXNestedNameSpecifier(Scope *S,
+                                                  NestedNameSpecInfo &IdInfo,
+                                                  bool EnteringContext,
+                                                  CXXScopeSpec &SS,
+                                                  NamedDecl *ScopeLookupResult,
+                                                  bool ErrorRecoveryLookup,
+                                                  bool *IsCorrectedToColon) {
   LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
                      LookupNestedNameSpecifierName);
   QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);
@@ -513,7 +513,7 @@
     // The declaration context must be complete.
     if (!LookupCtx->isDependentContext() &&
         RequireCompleteDeclContext(SS, LookupCtx))
-      return true;
+      return NNSResult::Failure;
 
     LookupQualifiedName(Found, LookupCtx);
 
@@ -551,7 +551,7 @@
   }
 
   if (Found.isAmbiguous())
-    return true;
+    return NNSResult::Failure;
 
   // If we performed lookup into a dependent context and did not find anything,
   // that's fine: just build a dependent nested-name-specifier.
@@ -561,14 +561,14 @@
          !cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
     // Don't speculate if we're just trying to improve error recovery.
     if (ErrorRecoveryLookup)
-      return true;
+      return NNSResult::Failure;
 
     // 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, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
-    return false;
+    return NNSResult::Success;
   }
 
   if (Found.empty() && !ErrorRecoveryLookup) {
@@ -591,15 +591,15 @@
             << FixItHint::CreateReplacement(IdInfo.CCLoc, ":");
         if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
           Diag(ND->getLocation(), diag::note_declared_at);
-        return true;
+        return NNSResult::Failure;
       }
       // Replacement '::' -> ':' is not allowed, just issue respective error.
       Diag(R.getNameLoc(), diag::err_expected_class_or_namespace)
           << IdInfo.Identifier << getLangOpts().CPlusPlus;
       if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
         Diag(ND->getLocation(), diag::note_entity_declared_at)
             << IdInfo.Identifier;
-      return true;
+      return NNSResult::Failure;
     }
   }
 
@@ -641,10 +641,50 @@
       Found.isSingleResult() ? Found.getRepresentativeDecl() : nullptr;
   bool IsExtension = false;
   bool AcceptSpec = isAcceptableNestedNameSpecifier(SD, &IsExtension);
-  if (!AcceptSpec && IsExtension) {
+  if (!AcceptSpec && IsExtension)
     AcceptSpec = true;
-    Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum);
+  if (AcceptSpec && IdInfo.InDeclSpec && IdInfo.NextIdentifier &&
+      !IdInfo.ObjectType) {
+    // Need to resolve possible ambiguity in construct 'T::A' - whether 'T::A'
+    // is a part of nested-name-spec or '::A' is a part of declarator.
+
+    NamedDecl *FoundDecl = SD;
+    if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl)) {
+      QualType QT = TD->getUnderlyingType();
+      if (const TagType *TT = QT->getAs<TagType>())
+        if (TagDecl *TD = TT->getDecl())
+          FoundDecl = TD;
+    }
+
+    if (TagDecl *TD = dyn_cast<TagDecl>(FoundDecl)) {
+      // In the construct 'T::A' tokens 'T::' are part of nested-name-spec if A
+      // is a member of 'T'.
+      if (TagDecl *Def = TD->getDefinition()) {
+        LookupResult Member(*this, IdInfo.NextIdentifier,
+                            IdInfo.NextIdentifierLoc, LookupMemberName);
+        LookupQualifiedName(Member, Def);
+        LookupResult::LookupResultKind Res = Member.getResultKind();
+        Member.clear();
+        if (Res == LookupResult::NotFound) {
+          if (IdInfo.FunctionExpected)
+            return NNSResult::Stop;
+          // Variable must be declare in its namespace, this declaration can be
+          // found by lookup.
+          LookupResult Global(*this, IdInfo.NextIdentifier,
+                              IdInfo.NextIdentifierLoc, LookupOrdinaryName);
+          LookupQualifiedName(Global, Context.getTranslationUnitDecl());
+          LookupResult::LookupResultKind Res = Global.getResultKind();
+          Global.clear();
+          if (Res != LookupResult::NotFound)
+            return NNSResult::Stop;
+          // Otherwise consider 'A' as a typo of a nested-name-spec.
+        }
+      }
+    }
   }
+  if (IsExtension)
+    Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum);
+
   if (AcceptSpec) {
     if (!ObjectType.isNull() && !ObjectTypeSearchedInScope &&
         !getLangOpts().CPlusPlus11) {
@@ -674,7 +714,7 @@
                             Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
                                Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
         if (ErrorRecoveryLookup)
-          return true;
+          return NNSResult::Failure;
 
          Diag(IdInfo.IdentifierLoc,
               diag::err_nested_name_member_ref_lookup_ambiguous)
@@ -694,19 +734,19 @@
     // If we're just performing this lookup for error-recovery purposes,
     // don't extend the nested-name-specifier. Just return now.
     if (ErrorRecoveryLookup)
-      return false;
+      return NNSResult::Success;
 
     // The use of a nested name specifier may trigger deprecation warnings.
     DiagnoseUseOfDecl(SD, IdInfo.CCLoc);
 
     if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) {
       SS.Extend(Context, Namespace, IdInfo.IdentifierLoc, IdInfo.CCLoc);
-      return false;
+      return NNSResult::Success;
     }
 
     if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) {
       SS.Extend(Context, Alias, IdInfo.IdentifierLoc, IdInfo.CCLoc);
-      return false;
+      return NNSResult::Success;
     }
 
     QualType T =
@@ -750,13 +790,13 @@
 
     SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T),
               IdInfo.CCLoc);
-    return false;
+    return NNSResult::Success;
   }
 
   // Otherwise, we have an error case.  If we don't want diagnostics, just
   // return an error now.
   if (ErrorRecoveryLookup)
-    return true;
+    return NNSResult::Failure;
 
   // If we didn't find anything during our lookup, try again with
   // ordinary name lookup, which can help us produce better error
@@ -793,7 +833,7 @@
             << IdInfo.Identifier << ContainingClass;
         SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc,
                   IdInfo.CCLoc);
-        return false;
+        return NNSResult::Success;
       }
     }
   }
@@ -816,17 +856,17 @@
     Diag(IdInfo.IdentifierLoc, diag::err_undeclared_var_use)
         << IdInfo.Identifier;
 
-  return true;
+  return NNSResult::Failure;
 }
 
-bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
-                                       NestedNameSpecInfo &IdInfo,
-                                       bool EnteringContext,
-                                       CXXScopeSpec &SS,
-                                       bool ErrorRecoveryLookup,
-                                       bool *IsCorrectedToColon) {
+Sema::NNSResult Sema::ActOnCXXNestedNameSpecifier(Scope *S,
+                                                  NestedNameSpecInfo &IdInfo,
+                                                  bool EnteringContext,
+                                                  CXXScopeSpec &SS,
+                                                  bool ErrorRecoveryLookup,
+                                                  bool *IsCorrectedToColon) {
   if (SS.isInvalid())
-    return true;
+    return NNSResult::Failure;
 
   return BuildCXXNestedNameSpecifier(S, IdInfo,
                                      EnteringContext, SS,
@@ -869,8 +909,8 @@
   if (SS.isInvalid())
     return false;
 
-  return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
-                                      /*ScopeLookupResult=*/nullptr, true);
+  return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
+                     /*ScopeLookupResult=*/nullptr, true) == NNSResult::Success;
 }
 
 bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
Index: lib/Parse/RAIIObjectsForParser.h
===================================================================
--- lib/Parse/RAIIObjectsForParser.h
+++ lib/Parse/RAIIObjectsForParser.h
@@ -88,7 +88,7 @@
   /// variable's initializer, but not when parsing the body of a
   /// class or function definition.
   class ParsingDeclRAIIObject {
-    Sema &Actions;
+    Parser &TheParser;
     sema::DelayedDiagnosticPool DiagnosticPool;
     Sema::ParsingDeclState State;
     bool Popped;
@@ -99,22 +99,22 @@
   public:
     enum NoParent_t { NoParent };
     ParsingDeclRAIIObject(Parser &P, NoParent_t _)
-        : Actions(P.getActions()), DiagnosticPool(nullptr) {
+        : TheParser(P), DiagnosticPool(nullptr) {
       push();
     }
 
     /// Creates a RAII object whose pool is optionally parented by another.
     ParsingDeclRAIIObject(Parser &P,
                           const sema::DelayedDiagnosticPool *parentPool)
-        : Actions(P.getActions()), DiagnosticPool(parentPool) {
+        : TheParser(P), DiagnosticPool(parentPool) {
       push();
     }
 
     /// Creates a RAII object and, optionally, initialize its
     /// diagnostics pool by stealing the diagnostics from another
     /// RAII object (which is assumed to be the current top pool).
     ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *other)
-        : Actions(P.getActions()),
+        : TheParser(P),
           DiagnosticPool(other ? other->DiagnosticPool.getParent() : nullptr) {
       if (other) {
         DiagnosticPool.steal(other->DiagnosticPool);
@@ -127,6 +127,8 @@
       abort();
     }
 
+    Parser &getParser() const { return TheParser; }
+
     sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() {
       return DiagnosticPool;
     }
@@ -159,13 +161,13 @@
 
   private:
     void push() {
-      State = Actions.PushParsingDeclaration(DiagnosticPool);
+      State = TheParser.getActions().PushParsingDeclaration(DiagnosticPool);
       Popped = false;
     }
 
     void pop(Decl *D) {
       if (!Popped) {
-        Actions.PopParsingDeclaration(State, D);
+        TheParser.getActions().PopParsingDeclaration(State, D);
         Popped = true;
       }
     }
@@ -178,10 +180,17 @@
   public:
     ParsingDeclSpec(Parser &P)
       : DeclSpec(P.getAttrFactory()),
-        ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {}
+        ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {
+      P.InDeclSpec = true;
+    }
     ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII)
       : DeclSpec(P.getAttrFactory()),
-        ParsingRAII(P, RAII) {}
+        ParsingRAII(P, RAII) {
+      P.InDeclSpec = true;
+    }
+    ~ParsingDeclSpec() {
+      leave();
+    }
 
     const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const {
       return ParsingRAII.getDelayedDiagnosticPool();
@@ -191,6 +200,10 @@
       ParsingRAII.complete(D);
     }
 
+    void leave() {
+      ParsingRAII.getParser().InDeclSpec = false;
+    }
+
     void abort() {
       ParsingRAII.abort();
     }
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -69,8 +69,8 @@
 
 Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
   : PP(pp), Actions(actions), Diags(PP.getDiagnostics()),
-    GreaterThanIsOperator(true), ColonIsSacred(false), 
-    InMessageExpression(false), TemplateParameterDepth(0),
+    GreaterThanIsOperator(true), ColonIsSacred(false),
+    InMessageExpression(false), InDeclSpec(false), TemplateParameterDepth(0),
     ParsingInObjCContainer(false) {
   SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies;
   Tok.startToken();
@@ -853,6 +853,34 @@
          Tok.is(tok::kw_try);          // X() try { ... }
 }
 
+/// \brief Checks if the current token finishes decl-spec and next '::' starts a
+/// function declarator.
+///
+/// This function is used to assist in parsing constructs 'A::B' where 'A' and
+/// 'B' are identifiers. So it expects that the current token is 'A'.
+/// FIXME: we must support all kinds of nested-name-specifiers.
+///
+bool Parser::isFunctionDeclaratorInGlobalNamespace() {
+  assert(Tok.is(tok::identifier));
+  assert(NextToken().isOneOf(tok::coloncolon, tok::colon));
+  unsigned Displ = 2;
+  while (true) {
+    const Token CurTok = GetLookAheadToken(Displ);
+    if (CurTok.is(tok::identifier)) {
+      const Token &NextTok = GetLookAheadToken(Displ + 1);
+      if (NextTok.isOneOf(tok::coloncolon, tok::colon)) {
+        Displ += 2;
+        continue;
+      }
+      if (NextTok.is(tok::l_paren))
+        return true;
+    }
+    break;
+  }
+  return false;
+}
+
+
 /// ParseDeclarationOrFunctionDefinition - Parse either a function-definition or
 /// a declaration.  We can't tell which we have until we read up to the
 /// compound-statement in function-definition. TemplateParams, if
@@ -875,6 +903,7 @@
                                        AccessSpecifier AS) {
   // Parse the common declaration-specifiers piece.
   ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level);
+  DS.leave();
 
   // If we had a free-standing type definition with a missing semicolon, we
   // may get this far before the problem becomes obvious.
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -429,6 +429,33 @@
     Token Next = NextToken();
     Sema::NestedNameSpecInfo IdInfo(&II, Tok.getLocation(), Next.getLocation(),
                                     ObjectType);
+    if (Next.isOneOf(tok::coloncolon, tok::colon)) {
+      Token TokenAfterCC = GetLookAheadToken(2);
+      if (TokenAfterCC.is(tok::identifier)) {
+        // We have sequence of tokens: 'identifier' '::' 'identifier'. If decl-
+        // spec is parsed, these tokens may compose a qualified name or the
+        // decl-spec might finish before the double colon:
+        //
+        // friend A::B func();
+        // friend A ::B();
+        //
+        // Sema can distinguish between the two cases by looking 'B' in 'A'.
+        IdInfo.InDeclSpec = InDeclSpec;
+        IdInfo.NextIdentifier = TokenAfterCC.getIdentifierInfo();
+        IdInfo.NextIdentifierLoc = TokenAfterCC.getLocation();
+
+        // If 'B' is not a member of 'A' in 'A::B', it may mean:
+        // - decl-spec is finished, '::B' starts a declarator,
+        // - 'B' is a typo.
+        // To distinguish between these cases Sema could search global scope for
+        // 'B', but it doesn't help if this declaration declares a function, as
+        // it may be the first declaration. If this declaration declares a
+        // variable, it must be preceded by a declaration in the global
+        // namespace and can be found by lookup.
+        IdInfo.FunctionExpected = InDeclSpec &&
+            isFunctionDeclaratorInGlobalNamespace();
+      }
+    }
 
     // If we get foo:bar, this is almost certainly a typo for foo::bar.  Recover
     // and emit a fixit hint for it.
@@ -458,18 +485,18 @@
     }
 
     if (Next.is(tok::coloncolon)) {
-      if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) &&
+      Token TokenAfterCC = GetLookAheadToken(2);
+      if (CheckForDestructor && TokenAfterCC.is(tok::tilde) &&
           !Actions.isNonTypeNestedNameSpecifier(getCurScope(), SS, IdInfo)) {
         *MayBePseudoDestructor = true;
         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_unexpected_token_in_nested_name_spec)
-              << Next2.getName()
+        if (TokenAfterCC.isOneOf(tok::kw_private, tok::kw_protected,
+                                 tok::kw_public, tok::kw_virtual)) {
+          Diag(TokenAfterCC, diag::err_unexpected_token_in_nested_name_spec)
+              << TokenAfterCC.getName()
               << FixItHint::CreateReplacement(Next.getLocation(), ":");
           Token ColonColon;
           PP.Lex(ColonColon);
@@ -484,33 +511,44 @@
 
       // 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.isOneOf(tok::coloncolon, tok::colon) &&
-             "NextToken() not working properly!");
-      Token ColonColon = Tok;
-      SourceLocation CCLoc = ConsumeToken();
 
-      CheckForLParenAfterColonColon();
+      if (TokenAfterCC.is(tok::l_paren)) {
+        ConsumeToken(); // identifier
+        ConsumeToken(); // ::
+        CheckForLParenAfterColonColon();
+      } else {
+        bool IsCorrectedToColon = false;
+        bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr;
+        Sema::NNSResult NSSRes = Actions.ActOnCXXNestedNameSpecifier(
+          getCurScope(), IdInfo, EnteringContext, SS, false, CorrectionFlagPtr);
+        if (NSSRes == Sema::NNSResult::Stop) {
+          // Current identifier followed by '::' is not a part of
+          // nested-name-spec. Stop parsing it.
+          return false;
+        }
 
-      bool IsCorrectedToColon = false;
-      bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr;
-      if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), IdInfo,
-                                              EnteringContext, SS,
-                                              false, CorrectionFlagPtr)) {
-        // Identifier is not recognized as a nested name, but we can have
-        // mistyped '::' instead of ':'.
-        if (CorrectionFlagPtr && IsCorrectedToColon) {
-          ColonColon.setKind(tok::colon);
-          PP.EnterToken(Tok);
-          PP.EnterToken(ColonColon);
-          Tok = Identifier;
-          break;
+        Token Identifier = Tok;
+        SourceLocation IdLoc = ConsumeToken();
+        assert(Tok.isOneOf(tok::coloncolon, tok::colon) &&
+               "NextToken() not working properly!");
+        Token ColonColon = Tok;
+        SourceLocation CCLoc = ConsumeToken();
+
+        if (NSSRes == Sema::NNSResult::Failure) {
+          // Identifier is not recognized as a nested name, but we can have
+          // mistyped '::' instead of ':'.
+          if (CorrectionFlagPtr && IsCorrectedToColon) {
+            ColonColon.setKind(tok::colon);
+            PP.EnterToken(Tok);
+            PP.EnterToken(ColonColon);
+            Tok = Identifier;
+            break;
+          }
+          SS.SetInvalid(SourceRange(IdLoc, CCLoc));
         }
-        SS.SetInvalid(SourceRange(IdLoc, CCLoc));
+        HasScopeSpecifier = true;
+        continue;
       }
-      HasScopeSpecifier = true;
-      continue;
     }
 
     CheckForTemplateAndDigraph(Next, ObjectType, EnteringContext, II, SS);
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -517,6 +517,7 @@
 
   // Parse nested-name-specifier.
   IdentifierInfo *LastII = nullptr;
+  InDeclSpec = false;
   ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false,
                                  /*MayBePseudoDtor=*/nullptr,
                                  /*IsTypename=*/false,
@@ -2405,6 +2406,7 @@
 
   ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
                              &CommonLateParsedAttrs);
+  DS.leave();
 
   // Turn off colon protection that was set for declspec.
   X.restore();
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -1548,6 +1548,7 @@
 
   DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
   ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext);
+  DS.leave();
 
   // If we had a free-standing type definition with a missing semicolon, we
   // may get this far before the problem becomes obvious.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4964,7 +4964,8 @@
                                        bool *CanCorrect = nullptr);
   NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
 
-  /// \brief Keeps information about an identifier in a nested-name-spec.
+  /// \brief Keeps information about identifier that is examined if it is a part
+  /// of a nested-name-spec.
   ///
   struct NestedNameSpecInfo {
     /// \brief The type of the object, if we're parsing nested-name-specifier in
@@ -4980,30 +4981,56 @@
     /// \brief The location of the '::'.
     SourceLocation CCLoc;
 
-    /// \brief Creates info object for the most typical case.
+    /// \brief If not null, the identifier following the '::'.
+    IdentifierInfo *NextIdentifier;
+
+    /// \brief The location of the identifier following '::'.
+    SourceLocation NextIdentifierLoc;
+
+    /// \brief True if the identifier was found during parsing of decl-spec.
+    bool InDeclSpec;
+
+    /// \brief True if the declaration seems to be a function declaration.
+    bool FunctionExpected;
+
     NestedNameSpecInfo(IdentifierInfo *II, SourceLocation IdLoc,
              SourceLocation ColonColonLoc, ParsedType ObjectType = ParsedType())
       : ObjectType(ObjectType), Identifier(II), IdentifierLoc(IdLoc),
-        CCLoc(ColonColonLoc) {
+        CCLoc(ColonColonLoc), NextIdentifier(nullptr),
+        InDeclSpec(false), FunctionExpected(false) {
     }
 
     NestedNameSpecInfo(IdentifierInfo *II, SourceLocation IdLoc,
                        SourceLocation ColonColonLoc, QualType ObjectType)
       : ObjectType(ParsedType::make(ObjectType)), Identifier(II),
-        IdentifierLoc(IdLoc), CCLoc(ColonColonLoc) {
+        IdentifierLoc(IdLoc), CCLoc(ColonColonLoc), NextIdentifier(nullptr),
+      InDeclSpec(false), FunctionExpected(false) {
     }
   };
 
   bool isNonTypeNestedNameSpecifier(Scope *S, CXXScopeSpec &SS,
                                     NestedNameSpecInfo &IdInfo);
 
-  bool BuildCXXNestedNameSpecifier(Scope *S,
-                                   NestedNameSpecInfo &IdInfo,
-                                   bool EnteringContext,
-                                   CXXScopeSpec &SS,
-                                   NamedDecl *ScopeLookupResult,
-                                   bool ErrorRecoveryLookup,
-                                   bool *IsCorrectedToColon = nullptr);
+  /// \brief Enumerates possible results that may be observed when adding an
+  /// identifier to nested-name-spec.
+  enum NNSResult {
+    /// \brief The identifier was successfully added to the nested-name-spec.
+    Success,
+
+    /// \brief The provided identifier makes the nested-name-spec invalid.
+    Failure,
+
+    // \brief The identifier is not a part of the nested-name-spec.
+    Stop
+  };
+
+  NNSResult BuildCXXNestedNameSpecifier(Scope *S,
+                                        NestedNameSpecInfo &IdInfo,
+                                        bool EnteringContext,
+                                        CXXScopeSpec &SS,
+                                        NamedDecl *ScopeLookupResult,
+                                        bool ErrorRecoveryLookup,
+                                        bool *IsCorrectedToColon = nullptr);
 
   /// \brief The parser has parsed a nested-name-specifier 'identifier::'.
   ///
@@ -5028,12 +5055,12 @@
   /// if the identifier is treated as if it was followed by ':', not '::'.
   ///
   /// \returns true if an error occurred, false otherwise.
-  bool ActOnCXXNestedNameSpecifier(Scope *S,
-                                   NestedNameSpecInfo &IdInfo,
-                                   bool EnteringContext,
-                                   CXXScopeSpec &SS,
-                                   bool ErrorRecoveryLookup = false,
-                                   bool *IsCorrectedToColon = nullptr);
+  NNSResult ActOnCXXNestedNameSpecifier(Scope *S,
+                                        NestedNameSpecInfo &IdInfo,
+                                        bool EnteringContext,
+                                        CXXScopeSpec &SS,
+                                        bool ErrorRecoveryLookup = false,
+                                        bool *IsCorrectedToColon = nullptr);
 
   ExprResult ActOnDecltypeExpression(Expr *E);
 
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -61,6 +61,7 @@
   friend class ObjCDeclContextSwitch;
   friend class ParenBraceBracketBalancer;
   friend class BalancedDelimiterTracker;
+  friend class ParsingDeclSpec;
 
   Preprocessor &PP;
 
@@ -198,6 +199,9 @@
   /// should not be set directly.
   bool InMessageExpression;
 
+  /// \brief When true, parser processes decl-spec, or tries to recognize it.
+  bool InDeclSpec;
+
   /// The "depth" of the template parameters currently being parsed.
   unsigned TemplateParameterDepth;
 
@@ -1257,6 +1261,7 @@
                                           ParsingDeclSpec *DS = nullptr);
   bool isDeclarationAfterDeclarator();
   bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
+  bool isFunctionDeclaratorInGlobalNamespace();
   DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
                                                   ParsedAttributesWithRange &attrs,
                                                   ParsingDeclSpec *DS = nullptr,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to