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