summaryrefslogtreecommitdiffstats
path: root/lib/Parse/ParseDecl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Parse/ParseDecl.cpp')
-rw-r--r--lib/Parse/ParseDecl.cpp178
1 files changed, 159 insertions, 19 deletions
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index f429ac9..b5ba8ac 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -356,7 +356,8 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(unsigned Context,
ParsingDeclSpec DS(*this);
if (Attr)
DS.AddAttributes(Attr);
- ParseDeclarationSpecifiers(DS);
+ ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none,
+ getDeclSpecContextFromDeclaratorContext(Context));
// C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
// declaration-specifiers init-declarator-list[opt] ';'
@@ -786,6 +787,20 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
return false;
}
+/// \brief Determine the declaration specifier context from the declarator
+/// context.
+///
+/// \param Context the declarator context, which is one of the
+/// Declarator::TheContext enumerator values.
+Parser::DeclSpecContext
+Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) {
+ if (Context == Declarator::MemberContext)
+ return DSC_class;
+ if (Context == Declarator::FileContext)
+ return DSC_top_level;
+ return DSC_normal;
+}
+
/// ParseDeclarationSpecifiers
/// declaration-specifiers: [C99 6.7]
/// storage-class-specifier declaration-specifiers[opt]
@@ -814,7 +829,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
AccessSpecifier AS,
DeclSpecContext DSContext) {
if (Tok.is(tok::code_completion)) {
- Actions.CodeCompleteOrdinaryName(CurScope);
+ Action::CodeCompletionContext CCC = Action::CCC_Namespace;
+ if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate)
+ CCC = DSContext == DSC_class? Action::CCC_MemberTemplate
+ : Action::CCC_Template;
+ else if (DSContext == DSC_class)
+ CCC = Action::CCC_Class;
+ else if (ObjCImpDecl)
+ CCC = Action::CCC_ObjCImplementation;
+
+ Actions.CodeCompleteOrdinaryName(CurScope, CCC);
ConsumeToken();
}
@@ -854,6 +878,47 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
->Kind == TNK_Type_template) {
// We have a qualified template-id, e.g., N::A<int>
+
+ // C++ [class.qual]p2:
+ // In a lookup in which the constructor is an acceptable lookup
+ // result and the nested-name-specifier nominates a class C:
+ //
+ // - if the name specified after the
+ // nested-name-specifier, when looked up in C, is the
+ // injected-class-name of C (Clause 9), or
+ //
+ // - if the name specified after the nested-name-specifier
+ // is the same as the identifier or the
+ // simple-template-id's template-name in the last
+ // component of the nested-name-specifier,
+ //
+ // the name is instead considered to name the constructor of
+ // class C.
+ //
+ // Thus, if the template-name is actually the constructor
+ // name, then the code is ill-formed; this interpretation is
+ // reinforced by the NAD status of core issue 635.
+ TemplateIdAnnotation *TemplateId
+ = static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue());
+ if (DSContext == DSC_top_level && TemplateId->Name &&
+ Actions.isCurrentClassName(*TemplateId->Name, CurScope, &SS)) {
+ if (isConstructorDeclarator()) {
+ // The user meant this to be an out-of-line constructor
+ // definition, but template arguments are not allowed
+ // there. Just allow this as a constructor; we'll
+ // complain about it later.
+ goto DoneWithDeclSpec;
+ }
+
+ // The user meant this to name a type, but it actually names
+ // a constructor with some extraneous template
+ // arguments. Complain, then parse it as a type as the user
+ // intended.
+ Diag(TemplateId->TemplateNameLoc,
+ diag::err_out_of_line_template_id_names_constructor)
+ << TemplateId->Name;
+ }
+
DS.getTypeSpecScope() = SS;
ConsumeToken(); // The C++ scope.
assert(Tok.is(tok::annot_template_id) &&
@@ -878,13 +943,23 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
if (Next.isNot(tok::identifier))
goto DoneWithDeclSpec;
- // If the next token is the name of the class type that the C++ scope
- // denotes, followed by a '(', then this is a constructor declaration.
- // We're done with the decl-specifiers.
- if (Actions.isCurrentClassName(*Next.getIdentifierInfo(),
- CurScope, &SS) &&
- GetLookAheadToken(2).is(tok::l_paren))
- goto DoneWithDeclSpec;
+ // If we're in a context where the identifier could be a class name,
+ // check whether this is a constructor declaration.
+ if (DSContext == DSC_top_level &&
+ Actions.isCurrentClassName(*Next.getIdentifierInfo(), CurScope,
+ &SS)) {
+ if (isConstructorDeclarator())
+ goto DoneWithDeclSpec;
+
+ // As noted in C++ [class.qual]p2 (cited above), when the name
+ // of the class is qualified in a context where it could name
+ // a constructor, its a constructor name. However, we've
+ // looked at the declarator, and the user probably meant this
+ // to be a type. Complain that it isn't supposed to be treated
+ // as a type, then proceed to parse it as a type.
+ Diag(Next.getLocation(), diag::err_out_of_line_type_names_constructor)
+ << Next.getIdentifierInfo();
+ }
TypeTy *TypeRep = Actions.getTypeName(*Next.getIdentifierInfo(),
Next.getLocation(), CurScope, &SS);
@@ -965,16 +1040,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
goto DoneWithDeclSpec;
}
- // C++: If the identifier is actually the name of the class type
- // being defined and the next token is a '(', then this is a
- // constructor declaration. We're done with the decl-specifiers
- // and will treat this token as an identifier.
- if (getLang().CPlusPlus &&
- (CurScope->isClassScope() ||
- (CurScope->isTemplateParamScope() &&
- CurScope->getParent()->isClassScope())) &&
+ // If we're in a context where the identifier could be a class name,
+ // check whether this is a constructor declaration.
+ if (getLang().CPlusPlus && DSContext == DSC_class &&
Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) &&
- NextToken().getKind() == tok::l_paren)
+ isConstructorDeclarator())
goto DoneWithDeclSpec;
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
@@ -1017,6 +1087,14 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
goto DoneWithDeclSpec;
}
+ // If we're in a context where the template-id could be a
+ // constructor name or specialization, check whether this is a
+ // constructor declaration.
+ if (getLang().CPlusPlus && DSContext == DSC_class &&
+ Actions.isCurrentClassName(*TemplateId->Name, CurScope) &&
+ isConstructorDeclarator())
+ goto DoneWithDeclSpec;
+
// Turn the template-id annotation token into a type annotation
// token, then try again to parse it as a type-specifier.
AnnotateTemplateIdTokenAsType();
@@ -2082,6 +2160,48 @@ bool Parser::isDeclarationSpecifier() {
}
}
+bool Parser::isConstructorDeclarator() {
+ TentativeParsingAction TPA(*this);
+
+ // Parse the C++ scope specifier.
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, 0, true);
+
+ // Parse the constructor name.
+ if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id)) {
+ // We already know that we have a constructor name; just consume
+ // the token.
+ ConsumeToken();
+ } else {
+ TPA.Revert();
+ return false;
+ }
+
+ // Current class name must be followed by a left parentheses.
+ if (Tok.isNot(tok::l_paren)) {
+ TPA.Revert();
+ return false;
+ }
+ ConsumeParen();
+
+ // A right parentheses or ellipsis signals that we have a constructor.
+ if (Tok.is(tok::r_paren) || Tok.is(tok::ellipsis)) {
+ TPA.Revert();
+ return true;
+ }
+
+ // If we need to, enter the specified scope.
+ DeclaratorScopeObj DeclScopeObj(*this, SS);
+ if (SS.isSet() && Actions.ShouldEnterDeclaratorScope(CurScope, SS))
+ DeclScopeObj.EnterDeclaratorScope();
+
+ // Check whether the next token(s) are part of a declaration
+ // specifier, in which case we have the start of a parameter and,
+ // therefore, we know that this is a constructor.
+ bool IsConstructor = isDeclarationSpecifier();
+ TPA.Revert();
+ return IsConstructor;
+}
/// ParseTypeQualifierListOpt
/// type-qualifier-list: [C99 6.7.5]
@@ -2366,10 +2486,16 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
Tok.is(tok::annot_template_id) || Tok.is(tok::tilde)) {
// We found something that indicates the start of an unqualified-id.
// Parse that unqualified-id.
+ bool AllowConstructorName
+ = ((D.getCXXScopeSpec().isSet() &&
+ D.getContext() == Declarator::FileContext) ||
+ (!D.getCXXScopeSpec().isSet() &&
+ D.getContext() == Declarator::MemberContext)) &&
+ !D.getDeclSpec().hasTypeSpecifier();
if (ParseUnqualifiedId(D.getCXXScopeSpec(),
/*EnteringContext=*/true,
/*AllowDestructorName=*/true,
- /*AllowConstructorName=*/!D.getDeclSpec().hasTypeSpecifier(),
+ AllowConstructorName,
/*ObjectType=*/0,
D.getName())) {
D.SetIdentifier(0, Tok.getLocation());
@@ -2396,6 +2522,16 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
// direct-declarator: '(' attributes declarator ')'
// Example: 'char (*X)' or 'int (*XX)(void)'
ParseParenDeclarator(D);
+
+ // If the declarator was parenthesized, we entered the declarator
+ // scope when parsing the parenthesized declarator, then exited
+ // the scope already. Re-enter the scope, if we need to.
+ if (D.getCXXScopeSpec().isSet()) {
+ if (Actions.ShouldEnterDeclaratorScope(CurScope, D.getCXXScopeSpec()))
+ // Change the declaration context for name lookup, until this function
+ // is exited (and the declarator has been parsed).
+ DeclScopeObj.EnterDeclaratorScope();
+ }
} else if (D.mayOmitIdentifier()) {
// This could be something simple like "int" (in which case the declarator
// portion is empty), if an abstract-declarator is allowed.
@@ -3020,6 +3156,8 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
Token OpTok = Tok;
SourceLocation StartLoc = ConsumeToken();
+ const bool hasParens = Tok.is(tok::l_paren);
+
bool isCastExpr;
TypeTy *CastTy;
SourceRange CastRange;
@@ -3027,6 +3165,8 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
isCastExpr,
CastTy,
CastRange);
+ if (hasParens)
+ DS.setTypeofParensRange(CastRange);
if (CastRange.getEnd().isInvalid())
// FIXME: Not accurate, the range gets one token more than it should.
OpenPOWER on IntegriCloud