diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 61b871d37684..b470bb4e78e5 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -482,6 +482,8 @@ def err_expected_rbrace_or_comma : Error<"expected '}' or ','">; def err_expected_rsquare_or_comma : Error<"expected ']' or ','">; def err_using_namespace_in_class : Error< "'using namespace' is not allowed in classes">; +def err_constructor_bad_name : Error< + "missing return type for function %0; did you mean the constructor name %1?">; def err_destructor_tilde_identifier : Error< "expected a class name after '~' to name a destructor">; def err_destructor_template_id : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a70e609aedae..f5a93d442ca9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4606,6 +4606,7 @@ public: // bool isCurrentClassName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS = 0); + bool isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS); bool ActOnAccessSpecifier(AccessSpecifier Access, SourceLocation ASLoc, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 31349ce99461..0f480c133536 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2095,6 +2095,8 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, DS.getStorageClassSpec() == DeclSpec::SCS_auto) { // Don't require a type specifier if we have the 'auto' storage class // specifier in C++98 -- we'll promote it to a type specifier. + if (SS) + AnnotateScopeToken(*SS, /*IsNewAnnotation*/false); return false; } @@ -2156,16 +2158,6 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, // Look ahead to the next token to try to figure out what this declaration // was supposed to be. switch (NextToken().getKind()) { - case tok::comma: - case tok::equal: - case tok::kw_asm: - case tok::l_brace: - case tok::l_square: - case tok::semi: - // This looks like a variable declaration. The type is probably missing. - // We're done parsing decl-specifiers. - return false; - case tok::l_paren: { // static x(4); // 'x' is not a type // x(int n); // 'x' is not a type @@ -2178,12 +2170,37 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, ConsumeToken(); TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false); PA.Revert(); - if (TPR == TPResult::False()) - return false; - // The identifier is followed by a parenthesized declarator. - // It's supposed to be a type. - break; + + if (TPR != TPResult::False()) { + // The identifier is followed by a parenthesized declarator. + // It's supposed to be a type. + break; + } + + // If we're in a context where we could be declaring a constructor, + // check whether this is a constructor declaration with a bogus name. + if (DSC == DSC_class || (DSC == DSC_top_level && SS)) { + IdentifierInfo *II = Tok.getIdentifierInfo(); + if (Actions.isCurrentClassNameTypo(II, SS)) { + Diag(Loc, diag::err_constructor_bad_name) + << Tok.getIdentifierInfo() << II + << FixItHint::CreateReplacement(Tok.getLocation(), II->getName()); + Tok.setIdentifierInfo(II); + } + } + // Fall through. } + case tok::comma: + case tok::equal: + case tok::kw_asm: + case tok::l_brace: + case tok::l_square: + case tok::semi: + // This looks like a variable or function declaration. The type is + // probably missing. We're done parsing decl-specifiers. + if (SS) + AnnotateScopeToken(*SS, /*IsNewAnnotation*/false); + return false; default: // This is probably supposed to be a type. This includes cases like: diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6ec344d1e3c0..655c9e2c3d0d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1227,6 +1227,32 @@ bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *, return false; } +/// \brief Determine whether the identifier II is a typo for the name of +/// the class type currently being defined. If so, update it to the identifier +/// that should have been used. +bool Sema::isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS) { + assert(getLangOpts().CPlusPlus && "No class names in C!"); + + if (!getLangOpts().SpellChecking) + return false; + + CXXRecordDecl *CurDecl; + if (SS && SS->isSet() && !SS->isInvalid()) { + DeclContext *DC = computeDeclContext(*SS, true); + CurDecl = dyn_cast_or_null(DC); + } else + CurDecl = dyn_cast_or_null(CurContext); + + if (CurDecl && CurDecl->getIdentifier() && II != CurDecl->getIdentifier() && + 3 * II->getName().edit_distance(CurDecl->getIdentifier()->getName()) + < II->getLength()) { + II = CurDecl->getIdentifier(); + return true; + } + + return false; +} + /// \brief Determine whether the given class is a base class of the given /// class, including looking at dependent bases. static bool findCircularInheritance(const CXXRecordDecl *Class, diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp index 88ae6ba0e446..c67c51aa720f 100644 --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -223,14 +223,18 @@ namespace dr122 { // dr122: yes // dr124: dup 201 // dr125: yes -struct dr125_A { struct dr125_B {}; }; +struct dr125_A { struct dr125_B {}; }; // expected-note {{here}} dr125_A::dr125_B dr125_C(); namespace dr125_B { dr125_A dr125_C(); } namespace dr125 { struct X { friend dr125_A::dr125_B (::dr125_C)(); // ok friend dr125_A (::dr125_B::dr125_C)(); // ok - friend dr125_A::dr125_B::dr125_C(); // expected-error {{requires a type specifier}} + friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}} + // expected-warning@-1 {{missing exception specification}} +#if __cplusplus >= 201103L + // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}} +#endif }; } diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp index ea20354bf9a7..06272504bc53 100644 --- a/clang/test/Parser/cxx-decl.cpp +++ b/clang/test/Parser/cxx-decl.cpp @@ -223,6 +223,15 @@ void foo() { } } +namespace PR17567 { + struct Foobar { // expected-note 2{{declared here}} + FooBar(); // expected-error {{missing return type for function 'FooBar'; did you mean the constructor name 'Foobar'?}} + ~FooBar(); // expected-error {{expected the class name after '~' to name a destructor}} + }; + FooBar::FooBar() {} // expected-error {{undeclared}} expected-error {{missing return type}} + FooBar::~FooBar() {} // expected-error {{undeclared}} expected-error {{expected the class name}} +} + // PR8380 extern "" // expected-error {{unknown linkage language}} test6a { ;// expected-error {{C++ requires a type specifier for all declarations}} \ diff --git a/clang/test/Parser/cxx0x-in-cxx98.cpp b/clang/test/Parser/cxx0x-in-cxx98.cpp index b4bda89d2781..724993811631 100644 --- a/clang/test/Parser/cxx0x-in-cxx98.cpp +++ b/clang/test/Parser/cxx0x-in-cxx98.cpp @@ -21,3 +21,10 @@ void NewBracedInitList() { // A warning on this would be sufficient once we can handle it correctly. new int {}; // expected-error {{}} } + +struct Auto { + static int n; +}; +auto Auto::n = 0; // expected-warning {{'auto' type specifier is a C++11 extension}} +auto Auto::m = 0; // expected-error {{no member named 'm' in 'Auto'}} + // expected-warning@-1 {{'auto' type specifier is a C++11 extension}} diff --git a/clang/test/SemaCXX/nested-name-spec.cpp b/clang/test/SemaCXX/nested-name-spec.cpp index 855af93c2387..df4f1b269d70 100644 --- a/clang/test/SemaCXX/nested-name-spec.cpp +++ b/clang/test/SemaCXX/nested-name-spec.cpp @@ -167,9 +167,7 @@ void N::f() { } // okay struct Y; // expected-note{{forward declaration of 'Y'}} Y::foo y; // expected-error{{incomplete type 'Y' named in nested name specifier}} -X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}} \ - // expected-error{{C++ requires a type specifier for all declarations}} \ - // expected-error{{only constructors take base initializers}} +X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}} struct foo_S { static bool value; diff --git a/clang/test/SemaTemplate/alias-nested-nontag.cpp b/clang/test/SemaTemplate/alias-nested-nontag.cpp index 4b5226b26ebb..2d0f0874e5e7 100644 --- a/clang/test/SemaTemplate/alias-nested-nontag.cpp +++ b/clang/test/SemaTemplate/alias-nested-nontag.cpp @@ -2,5 +2,4 @@ template using Id = T; // expected-note {{type alias template 'Id' declared here}} struct U { static Id V; }; -Id ::U::V; // expected-error {{type 'Id' (aka 'int') cannot be used prior to '::' because it has no members}} \ - expected-error {{C++ requires a type specifier}} +Id ::U::V; // expected-error {{type 'Id' (aka 'int') cannot be used prior to '::' because it has no members}}