From 134c68d00270c8762bd463dcd1f007a4c02e7abf Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 3 Mar 2014 21:12:53 +0000 Subject: [PATCH] Improve diagnostics for malformed constructor declarations (where lookup for the type of the first parameter fails, and it is the only, unnamed, parameter). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@202759 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 2 +- lib/Parse/ParseDecl.cpp | 37 +++++++++++++++++++++++++++++------- test/Parser/cxx-class.cpp | 18 +++++++++++++++++- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 137e1b7f4c..ddb22f5128 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1797,7 +1797,7 @@ private: /// \brief Starting with a scope specifier, identifier, or /// template-id that refers to the current class, determine whether /// this is a constructor declarator. - bool isConstructorDeclarator(); + bool isConstructorDeclarator(bool Unqualified); /// \brief Specifies the context in which type-id/expression /// disambiguation will occur. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 8417c27ce8..561683f038 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2592,7 +2592,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if ((DSContext == DSC_top_level || DSContext == DSC_class) && TemplateId->Name && Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) { - if (isConstructorDeclarator()) { + if (isConstructorDeclarator(/*Unqualified*/false)) { // 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 @@ -2642,7 +2642,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if ((DSContext == DSC_top_level || DSContext == DSC_class) && Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(), &SS)) { - if (isConstructorDeclarator()) + if (isConstructorDeclarator(/*Unqualified*/false)) goto DoneWithDeclSpec; // As noted in C++ [class.qual]p2 (cited above), when the name @@ -2789,7 +2789,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // check whether this is a constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DSC_class && Actions.isCurrentClassName(*Tok.getIdentifierInfo(), getCurScope()) && - isConstructorDeclarator()) + isConstructorDeclarator(/*Unqualified*/true)) goto DoneWithDeclSpec; isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, @@ -2825,7 +2825,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DSC_class && Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) && - isConstructorDeclarator()) + isConstructorDeclarator(TemplateId->SS.isEmpty())) goto DoneWithDeclSpec; // Turn the template-id annotation token into a type annotation @@ -4250,7 +4250,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { } } -bool Parser::isConstructorDeclarator() { +bool Parser::isConstructorDeclarator(bool IsUnqualified) { TentativeParsingAction TPA(*this); // Parse the C++ scope specifier. @@ -4332,12 +4332,35 @@ bool Parser::isConstructorDeclarator() { case tok::coloncolon: // C(X :: Y); // C(X :: *p); - case tok::r_paren: - // C(X ) // Assume this isn't a constructor, rather than assuming it's a // constructor with an unnamed parameter of an ill-formed type. break; + case tok::r_paren: + // C(X ) + if (NextToken().is(tok::colon) || NextToken().is(tok::kw_try)) { + // Assume these were meant to be constructors: + // C(X) : (the name of a bit-field cannot be parenthesized). + // C(X) try (this is otherwise ill-formed). + IsConstructor = true; + } + if (NextToken().is(tok::semi) || NextToken().is(tok::l_brace)) { + // If we have a constructor name within the class definition, + // assume these were meant to be constructors: + // C(X) { + // C(X) ; + // ... because otherwise we would be declaring a non-static data + // member that is ill-formed because it's of the same type as its + // surrounding class. + // + // FIXME: We can actually do this whether or not the name is qualified, + // because if it is qualified in this context it must be being used as + // a constructor name. However, we do not implement that rule correctly + // currently, so we're somewhat conservative here. + IsConstructor = IsUnqualified; + } + break; + default: IsConstructor = true; break; diff --git a/test/Parser/cxx-class.cpp b/test/Parser/cxx-class.cpp index d4d8c44943..80cd828801 100644 --- a/test/Parser/cxx-class.cpp +++ b/test/Parser/cxx-class.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s +// RUN: %clang_cc1 -fsyntax-only -verify -pedantic -fcxx-exceptions %s class C; class C { public: @@ -123,6 +123,22 @@ class pr16989 { }; }; +namespace CtorErrors { + struct A { + A(NonExistent); // expected-error {{unknown type name 'NonExistent'}} + }; + struct B { + B(NonExistent) : n(0) {} // expected-error {{unknown type name 'NonExistent'}} + int n; + }; + struct C { + C(NonExistent) try {} catch (...) {} // expected-error {{unknown type name 'NonExistent'}} + }; + struct D { + D(NonExistent) {} // expected-error {{unknown type name 'NonExistent'}} + }; +} + // PR11109 must appear at the end of the source file class pr11109r3 { // expected-note{{to match this '{'}} public // expected-error{{expected ':'}} expected-error{{expected '}'}} expected-error{{expected ';' after class}} -- 2.40.0