Fixed parsing unnamed bit fields

Thank you for example, it represents a case missed from the patch.
Adding colon protector indeed helps for the code:

    const int m = 4;
    struct A {
      struct n {
        int m;
      };
      int A::n : m;
    };

I found another example, that is not MS-specific and is currently
misparsed by clang:

    struct A {
      enum class B {
        C
      };
    };
    const int C = 4;
    struct D {
      A::B : C;
    };

Fixing this case is a bit trickier because declspec is parsed with colon
protection turned off, otherwise we cannot recover typos like:

    A:B c;

To recogneze that A::B:C is not a typo for A::B::C we must know that the
parsed scope spec is a part of type specifier.

In the updated patch TryAnnotateCXXScopeToken has additional parameter,
that provide information whether a type is expected, with this change the
above example is parsed correctly.

http://reviews.llvm.org/D3653

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/Sema/SemaCXXScopeSpec.cpp
  test/SemaCXX/MicrosoftExtensions.cpp
  test/SemaCXX/enum-bitfield.cpp
  test/SemaCXX/nested-name-spec.cpp
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -569,7 +569,8 @@
                                                  bool NeedType,
                                                  CXXScopeSpec &SS,
                                                  bool IsNewScope);
-  bool TryAnnotateCXXScopeToken(bool EnteringContext = false);
+  bool TryAnnotateCXXScopeToken(bool EnteringContext = false,
+                                bool NeedType = false);
 
 private:
   enum AnnotatedNameKind {
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4455,7 +4455,8 @@
                                    CXXScopeSpec &SS,
                                    NamedDecl *ScopeLookupResult,
                                    bool ErrorRecoveryLookup,
-                                   bool *IsCorrectedToColon = nullptr);
+                                   bool *IsCorrectedToColon = nullptr,
+                                   IdentifierInfo *NextIdPtr = nullptr);
 
   /// \brief The parser has parsed a nested-name-specifier 'identifier::'.
   ///
@@ -4507,7 +4508,8 @@
                                  SourceLocation IdentifierLoc,
                                  SourceLocation ColonLoc,
                                  ParsedType ObjectType,
-                                 bool EnteringContext);
+                                 bool EnteringContext,
+                                 IdentifierInfo *NextIdPtr = nullptr);
 
   /// \brief The parser has parsed a nested-name-specifier
   /// 'template[opt] template-name < template-args >::'.
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -2722,24 +2722,23 @@
       // typedef-name
     case tok::kw_decltype:
     case tok::identifier: {
+      // This identifier can only be a typedef name if we haven't already seen
+      // a type-specifier.  Without this check we misparse:
+      //  typedef int X; struct Y { short X; };  as 'short int'.
+      if (DS.hasTypeSpecifier())
+        goto DoneWithDeclSpec;
+
       // In C++, check to see if this is a scope specifier like foo::bar::, if
       // so handle it as such.  This is important for ctor parsing.
       if (getLangOpts().CPlusPlus) {
         if (TryAnnotateCXXScopeToken(EnteringContext)) {
-          if (!DS.hasTypeSpecifier())
-            DS.SetTypeSpecError();
+          DS.SetTypeSpecError();
           goto DoneWithDeclSpec;
         }
         if (!Tok.is(tok::identifier))
           continue;
       }
 
-      // This identifier can only be a typedef name if we haven't already seen
-      // a type-specifier.  Without this check we misparse:
-      //  typedef int X; struct Y { short X; };  as 'short int'.
-      if (DS.hasTypeSpecifier())
-        goto DoneWithDeclSpec;
-
       // Check for need to substitute AltiVec keyword tokens.
       if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid))
         break;
@@ -4530,6 +4529,10 @@
        Tok.is(tok::annot_cxxscope))) {
     bool EnteringContext = D.getContext() == Declarator::FileContext ||
                            D.getContext() == Declarator::MemberContext;
+    // If inside a class, do not parse ':' as typo for '::', in this context
+    // it is a bitfield.
+    ColonProtectionRAIIObject X(*this,
+                                D.getContext() == Declarator::MemberContext);
     CXXScopeSpec SS;
     ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext);
 
@@ -4719,6 +4722,11 @@
   DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
 
   if (getLangOpts().CPlusPlus && D.mayHaveIdentifier()) {
+    // Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
+    // is a bitfield.
+    ColonProtectionRAIIObject X(*this,
+                                D.getContext() == Declarator::MemberContext);
+
     // ParseDeclaratorInternal might already have parsed the scope.
     if (D.getCXXScopeSpec().isEmpty()) {
       bool EnteringContext = D.getContext() == Declarator::FileContext ||
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -1229,10 +1229,12 @@
   // Parse the (optional) nested-name-specifier.
   CXXScopeSpec &SS = DS.getTypeSpecScope();
   if (getLangOpts().CPlusPlus) {
-    // "FOO : BAR" is not a potential typo for "FOO::BAR".
+    // "FOO : BAR" is not a potential typo for "FOO::BAR".  In this context it
+    // is base-specifier-list.
     ColonProtectionRAIIObject X(*this);
 
-    if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext))
+    if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext,
+                                       nullptr, true))
       DS.SetTypeSpecError();
     if (SS.isSet())
       if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
@@ -1916,14 +1918,8 @@
   //   declarator pure-specifier[opt]
   //   declarator brace-or-equal-initializer[opt]
   //   identifier[opt] ':' constant-expression
-  if (Tok.isNot(tok::colon)) {
-    // Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
-    // is a bitfield.
-    // FIXME: This should only apply when parsing the id-expression (see
-    // PR18587).
-    ColonProtectionRAIIObject X(*this);
+  if (Tok.isNot(tok::colon))
     ParseDeclarator(DeclaratorInfo);
-  }
 
   if (!DeclaratorInfo.isFunctionDeclarator() && TryConsumeToken(tok::colon)) {
     BitfieldSize = ParseConstantExpression();
@@ -2009,7 +2005,7 @@
   bool MalformedTypeSpec = false;
   if (!TemplateInfo.Kind &&
       (Tok.is(tok::identifier) || Tok.is(tok::coloncolon))) {
-    if (TryAnnotateCXXScopeToken())
+    if (TryAnnotateCXXScopeToken(/*EnteringContext=*/false, /*IsTypename*/true))
       MalformedTypeSpec = true;
 
     bool isAccessDecl;
@@ -2118,13 +2114,8 @@
   if (MalformedTypeSpec)
     DS.SetTypeSpecError();
 
-  {
-    // Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
-    // is a bitfield.
-    ColonProtectionRAIIObject X(*this);
-    ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
-                               &CommonLateParsedAttrs);
-  }
+  ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
+                             &CommonLateParsedAttrs);
 
   // 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
@@ -409,14 +409,21 @@
     // 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) {
-      if (Actions.IsInvalidUnlessNestedName(getCurScope(), SS, II, 
-                                            Tok.getLocation(), 
+      IdentifierInfo *NextIdPtr = nullptr;
+      if (PP.LookAhead(1).is(tok::identifier))
+        NextIdPtr = PP.LookAhead(1).getIdentifierInfo();
+      // 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.
+      if (NextIdPtr &&
+          // If we expect type name, do not recover foo:bar as foo::bar if the
+          // latter is a value, as we may have unnamed bit field.  Non-null
+          // pointer to the next identifier indicates need of such check.
+          Actions.IsInvalidUnlessNestedName(getCurScope(), SS, II,
+                                            Tok.getLocation(),
                                             Next.getLocation(), 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)) {
+                                            EnteringContext,
+                                            IsTypename ? NextIdPtr : nullptr)) {
         Diag(Next, diag::err_unexpected_colon_in_nested_name_spec)
           << FixItHint::CreateReplacement(Next.getLocation(), "::");
         // Recover as if the user wrote '::'.
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -1683,21 +1683,33 @@
   return false;
 }
 
-/// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
-/// annotates C++ scope specifiers and template-ids.  This returns
-/// true if there was an error that could not be recovered from.
+/// \brief Like TryAnnotateTypeOrScopeToken but only annotates C++ scope
+/// specifiers and template-ids.
+/// \return true if there was an error that could not be recovered from.
+/// \param NeedType If true, scope specifier is parsed a part of type
+/// specifuer.
+///
+/// Setting NeedType influences error recovery. If it is set, parser avoids
+/// replacements ':' -> '::' if it produces non-type entity. With it we can
+/// parse correctly code like this:
+/// \code
+///     struct A { enum class B { C }; };
+///     const int C = 4;
+///     struct D { A::B : C; };
+/// \endcode
 ///
 /// Note that this routine emits an error if you call it with ::new or ::delete
 /// as the current tokens, so only call it in contexts where these are invalid.
-bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
+bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext, bool NeedType) {
   assert(getLangOpts().CPlusPlus &&
          "Call sites of this function should be guarded by checking for C++");
   assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
           (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) ||
          Tok.is(tok::kw_decltype)) && "Cannot be a type or scope token!");
 
   CXXScopeSpec SS;
-  if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext))
+  if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext, nullptr,
+                                     NeedType))
     return true;
   if (SS.isEmpty())
     return false;
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp
+++ lib/Sema/SemaCXXScopeSpec.cpp
@@ -418,7 +418,8 @@
                                        CXXScopeSpec &SS,
                                        NamedDecl *ScopeLookupResult,
                                        bool ErrorRecoveryLookup,
-                                       bool *IsCorrectedToColon) {
+                                       bool *IsCorrectedToColon,
+                                       IdentifierInfo *NextIdPtr) {
   LookupResult Found(*this, &Identifier, IdentifierLoc, 
                      LookupNestedNameSpecifierName);
 
@@ -596,20 +597,35 @@
           (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
            !Context.hasSameType(
                             Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
-                               Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
+                            Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
         if (ErrorRecoveryLookup)
           return true;
 
-         Diag(IdentifierLoc, 
-              diag::err_nested_name_member_ref_lookup_ambiguous)
-           << &Identifier;
-         Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
-           << ObjectType;
-         Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);
+        Diag(IdentifierLoc,
+             diag::err_nested_name_member_ref_lookup_ambiguous)
+          << &Identifier;
+        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.
+      }
+    }
+
+    // If the next identifier is supplied, we are parsing for type and try
+    // treating ':' as typo for '::'. If the parsed nested name specifier and
+    // the next identifier form a value, do not treat ':' as '::', probably we
+    // have unnamed bit field.
+    if (ErrorRecoveryLookup && NextIdPtr) {
+      LookupResult FoundNext(*this, &Identifier, IdentifierLoc,
+                             LookupOrdinaryName);
+      LookupQualifiedName(FoundNext, cast<DeclContext>(SD));
+      // Do recovery only if the declaration of the next identifier is found and
+      // it is not a value.
+      if (FoundNext.getResultKind() != LookupResult::Found ||
+          isa<ValueDecl>(FoundNext.getFoundDecl()))
+        return true;
     }
 
     // If we're just performing this lookup for error-recovery purposes,
@@ -742,7 +758,8 @@
   return BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, CCLoc,
                                      GetTypeFromParser(ObjectType),
                                      EnteringContext, SS, 
-                                     /*ScopeLookupResult=*/nullptr, false,
+                                     /*ScopeLookupResult=*/ nullptr,
+                                     /*ErrorRecoveryLookup=*/ false,
                                      IsCorrectedToColon);
 }
 
@@ -780,14 +797,18 @@
                                      SourceLocation IdentifierLoc,
                                      SourceLocation ColonLoc,
                                      ParsedType ObjectType,
-                                     bool EnteringContext) {
+                                     bool EnteringContext,
+                                     IdentifierInfo *NextIdPtr) {
   if (SS.isInvalid())
     return false;
 
   return !BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, ColonLoc,
                                       GetTypeFromParser(ObjectType),
-                                      EnteringContext, SS, 
-                                      /*ScopeLookupResult=*/nullptr, true);
+                                      EnteringContext, SS,
+                                      /*ScopeLookupResult=*/ nullptr,
+                                      /*ErrorRecoveryLookup=*/ true,
+                                      /*IsCorrectedToColon=*/ nullptr,
+                                      NextIdPtr);
 }
 
 bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
Index: test/SemaCXX/MicrosoftExtensions.cpp
===================================================================
--- test/SemaCXX/MicrosoftExtensions.cpp
+++ test/SemaCXX/MicrosoftExtensions.cpp
@@ -418,3 +418,13 @@
   _Static_assert(__alignof(s1) == 8, "");
   _Static_assert(__alignof(s2) == 4, "");
 }
+
+namespace pr18587 {
+const int m = 4;
+struct A {
+  struct n {
+    int m;
+  };
+  int A::n : m; // expected-warning{{extra qualification on member 'n'}}
+};
+}
Index: test/SemaCXX/enum-bitfield.cpp
===================================================================
--- test/SemaCXX/enum-bitfield.cpp
+++ test/SemaCXX/enum-bitfield.cpp
@@ -16,3 +16,15 @@
   enum E : int(2);
   enum E : Z(); // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'Z'}}
 };
+
+namespace pr18587 {
+struct A {
+  enum class B {
+    C
+  };
+};
+const int C = 4;
+struct D {
+  A::B : C;
+};
+}
Index: test/SemaCXX/nested-name-spec.cpp
===================================================================
--- test/SemaCXX/nested-name-spec.cpp
+++ test/SemaCXX/nested-name-spec.cpp
@@ -311,3 +311,128 @@
 
 namespace TypedefNamespace { typedef int F; };
 TypedefNamespace::F::NonexistentName BadNNSWithCXXScopeSpec; // expected-error {{'F' (aka 'int') is not a class, namespace, or scoped enumeration}}
+
+namespace PR18587 {
+
+struct C1 {
+  int a, b, c;
+  typedef int C2;
+  struct B1 {
+    struct B2 {
+      int a, b, c;
+    };
+  };
+};
+struct C2 { static const unsigned N1 = 1; };
+struct B1 {
+  enum E1 { B2 = 2 };
+  static const int B3 = 3;
+};
+const int N1 = 2;
+
+// Function declarators
+struct S1a { int f(C1::C2); };
+struct S1b { int f(C1:C2); };  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+
+struct S2a {
+  C1::C2 f(C1::C2);
+};
+struct S2b {
+  C1:C2 f(C1::C2);  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+struct S2c {
+  C1::C2 f(C1:C2);  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+struct S3a {
+  int f(C1::C2), C2 : N1;
+  int g : B1::B2;
+};
+struct S3b {
+  int g : B1:B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+// Inside square brackets
+struct S4a {
+  int f[C2::N1];
+};
+struct S4b {
+  int f[C2:N1];  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+struct S5a {
+  int f(int xx[B1::B3 ? C2::N1 : B1::B2]);
+};
+struct S5b {
+  int f(int xx[B1::B3 ? C2::N1 : B1:B2]);  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+struct S5c {
+  int f(int xx[B1:B3 ? C2::N1 : B1::B2]);  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+// Bit fields
+struct S6a {
+  C1::C2 m1 : B1::B2;
+};
+struct S6b {
+  C1:C2 m1 : B1::B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+struct S6c {
+  C1::C2 m1 : B1:B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+struct S6d {
+  int C2:N1;
+};
+struct S6e {
+  static const int N = 3;
+  B1::E1 : N;
+};
+struct S6f {
+  C1:C2 : B1::B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+  B1:E1 : B1::B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+struct S6g {
+  C1::C2 : B1:B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+  B1::E1 : B1:B2;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+// Template parameters
+template <int N> struct T1 {
+  int a,b,c;
+  static const unsigned N1 = N;
+  typedef unsigned C1;
+};
+T1<C2::N1> var_1a;
+T1<C2:N1> var_1b;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+template<int N> int F() {}
+int (*X1)() = (B1::B2 ? F<1> : F<2>);
+int (*X2)() = (B1:B2 ? F<1> : F<2>);  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+
+// Bit fields + templates
+struct S7a {
+  T1<B1::B2>::C1 m1 : T1<B1::B2>::N1;
+};
+struct S7b {
+  T1<B1:B2>::C1 m1 : T1<B1::B2>::N1;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+struct S7c {
+  T1<B1::B2>::C1 m1 : T1<B1:B2>::N1;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+// Member pointers
+struct S8a {
+  C1::C2 C1::*m1;
+};
+struct S8b {
+  C1:C2 C1::*m1;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+// nested types
+struct S9a {
+  typedef C1::C2 t1;
+};
+struct S9b {
+  typedef C1:C2 t1;  // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
+};
+
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to