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,13 +1683,24 @@ 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) || @@ -1697,7 +1708,8 @@ 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(OuterDecl) || !isa(SD) || !Context.hasSameType( Context.getTypeDeclType(cast(OuterDecl)), - Context.getTypeDeclType(cast(SD))))) { + Context.getTypeDeclType(cast(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(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(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 struct T1 { + int a,b,c; + static const unsigned N1 = N; + typedef unsigned C1; +}; +T1 var_1a; +T1 var_1b; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}} +template 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::C1 m1 : T1::N1; +}; +struct S7b { + T1::C1 m1 : T1::N1; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}} +}; +struct S7c { + T1::C1 m1 : T1::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 '::'?}} +}; + +}