diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0aeefb3ee909..9e50ca65fbe8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -104,7 +104,7 @@ def err_using_requires_qualname : Error< def err_using_typename_non_type : Error< "'typename' keyword used on a non-type">; def err_using_decl_nested_name_specifier_is_not_a_base_class : Error< - "using declaration refers into %0, which is not a base class of %1">; + "using declaration refers into '%0', which is not a base class of %1">; def err_using_decl_can_not_refer_to_class_member : Error< "using declaration can not refer to class member">; def err_using_decl_can_not_refer_to_namespace : Error< @@ -990,7 +990,7 @@ def err_template_spec_extra_headers : Error< "extraneous template parameter list in template specialization or " "out-of-line template definition">; def err_template_qualified_declarator_no_match : Error< - "nested name specifier %0 for declaration does not refer into a class, " + "nested name specifier '%0' for declaration does not refer into a class, " "class template or class template partial specialization">; // C++ Class Template Partial Specialization @@ -1111,6 +1111,8 @@ def err_typename_nested_not_type : Error< "typename specifier refers to non-type member %0 in %1">; def note_typename_refers_here : Note< "referenced member %0 is declared here">; +def err_typename_missing : Error< + "missing 'typename' prior to dependent type name '%0%1'">; def err_template_kw_refers_to_non_template : Error< "%0 following the 'template' keyword does not refer to a template">; diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 5e0a360df196..1ee14701fa2d 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -201,6 +201,36 @@ public: return DeclSpec::TST_unspecified; } + /// \brief Action called as part of error recovery when the parser has + /// determined that the given name must refer to a type, but + /// \c getTypeName() did not return a result. + /// + /// This callback permits the action to give a detailed diagnostic when an + /// unknown type name is encountered and, potentially, to try to recover + /// by producing a new type in \p SuggestedType. + /// + /// \param II the name that should be a type. + /// + /// \param IILoc the location of the name in the source. + /// + /// \param S the scope in which name lookup was performed. + /// + /// \param SS if non-NULL, the C++ scope specifier that preceded the name. + /// + /// \param SuggestedType if the action sets this type to a non-NULL type, + /// the parser will recovery by consuming the type name token and then + /// pretending that the given type was the type it parsed. + /// + /// \returns true if a diagnostic was emitted, false otherwise. When false, + /// the parser itself will emit a generic "unknown type name" diagnostic. + virtual bool DiagnoseUnknownTypeName(const IdentifierInfo &II, + SourceLocation IILoc, + Scope *S, + const CXXScopeSpec *SS, + TypeTy *&SuggestedType) { + return false; + } + /// isCurrentClassName - Return true if the specified name is the /// name of the innermost C++ class type currently being defined. virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index e15a4cd68805..b56c33170cbf 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -688,13 +688,36 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, } } - // Since this is almost certainly an invalid type name, emit a - // diagnostic that says it, eat the token, and mark the declspec as - // invalid. - SourceRange R; - if (SS) R = SS->getRange(); + // This is almost certainly an invalid type name. Let the action emit a + // diagnostic and attempt to recover. + Action::TypeTy *T = 0; + if (Actions.DiagnoseUnknownTypeName(*Tok.getIdentifierInfo(), Loc, + CurScope, SS, T)) { + // The action emitted a diagnostic, so we don't have to. + if (T) { + // The action has suggested that the type T could be used. Set that as + // the type in the declaration specifiers, consume the would-be type + // name token, and we're done. + const char *PrevSpec; + unsigned DiagID; + DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T, + false); + DS.SetRangeEnd(Tok.getLocation()); + ConsumeToken(); + + // There may be other declaration specifiers after this. + return true; + } + + // Fall through; the action had no suggestion for us. + } else { + // The action did not emit a diagnostic, so emit one now. + SourceRange R; + if (SS) R = SS->getRange(); + Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo() << R; + } - Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo() << R; + // Mark this as an error. const char *PrevSpec; unsigned DiagID; DS.SetTypeSpecType(DeclSpec::TST_error, Loc, PrevSpec, DiagID); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index c289abfea869..ba05a07f2654 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -117,6 +117,7 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val, llvm::raw_string_ostream OS(S); reinterpret_cast (Val)->print(OS, Context.PrintingPolicy); + NeedQuotes = false; } else { assert(Kind == Diagnostic::ak_declcontext); DeclContext *DC = reinterpret_cast (Val); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 465eca70ed3f..04addfd3a98b 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -523,7 +523,12 @@ public: Scope *S, const CXXScopeSpec *SS, bool isClassName = false); virtual DeclSpec::TST isTagName(IdentifierInfo &II, Scope *S); - + virtual bool DiagnoseUnknownTypeName(const IdentifierInfo &II, + SourceLocation IILoc, + Scope *S, + const CXXScopeSpec *SS, + TypeTy *&SuggestedType); + virtual DeclPtrTy ActOnDeclarator(Scope *S, Declarator &D) { return HandleDeclarator(S, D, MultiTemplateParamsArg(*this), false); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9f03cb284ca5..606b33f5f74b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -23,6 +23,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/Parse/DeclSpec.h" +#include "clang/Parse/ParseDiagnostic.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -199,6 +200,36 @@ DeclSpec::TST Sema::isTagName(IdentifierInfo &II, Scope *S) { return DeclSpec::TST_unspecified; } +bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, + SourceLocation IILoc, + Scope *S, + const CXXScopeSpec *SS, + TypeTy *&SuggestedType) { + // We don't have anything to suggest (yet). + SuggestedType = 0; + + // FIXME: Should we move the logic that tries to recover from a missing tag + // (struct, union, enum) from Parser::ParseImplicitInt here, instead? + + if (!SS) + Diag(IILoc, diag::err_unknown_typename) << &II; + else if (DeclContext *DC = computeDeclContext(*SS, false)) + Diag(IILoc, diag::err_typename_nested_not_found) + << &II << DC << SS->getRange(); + else if (isDependentScopeSpecifier(*SS)) { + Diag(SS->getRange().getBegin(), diag::err_typename_missing) + << (NestedNameSpecifier *)SS->getScopeRep() << II.getName() + << SourceRange(SS->getRange().getBegin(), IILoc) + << CodeModificationHint::CreateInsertion(SS->getRange().getBegin(), + "typename "); + SuggestedType = ActOnTypenameType(SourceLocation(), *SS, II, IILoc).get(); + } else { + assert(SS && SS->isInvalid() && + "Invalid scope specifier has already been diagnosed"); + } + + return true; +} // Determines the context to return to after temporarily entering a // context. This depends in an unnecessarily complicated way on the diff --git a/clang/test/CXX/class/class.friend/p1.cpp b/clang/test/CXX/class/class.friend/p1.cpp index 3f8778885ce2..7065a7e917fd 100644 --- a/clang/test/CXX/class/class.friend/p1.cpp +++ b/clang/test/CXX/class/class.friend/p1.cpp @@ -67,7 +67,7 @@ class A { class facet {}; }; -A::UndeclaredSoFar y; // expected-error {{ unknown type name 'UndeclaredSoFar' }} +A::UndeclaredSoFar y; // expected-error {{no type named 'UndeclaredSoFar' in 'class A'}} class PreDeclared; diff --git a/clang/test/SemaCXX/namespace.cpp b/clang/test/SemaCXX/namespace.cpp index ae8dfc581366..5ed6ba50ce1b 100644 --- a/clang/test/SemaCXX/namespace.cpp +++ b/clang/test/SemaCXX/namespace.cpp @@ -67,4 +67,4 @@ namespace foo { static foo::x test1; // ok -static foo::X test2; // typo: expected-error {{unknown type name 'X'}} +static foo::X test2; // typo: expected-error {{no type named 'X' in}} diff --git a/clang/test/SemaCXX/nested-name-spec.cpp b/clang/test/SemaCXX/nested-name-spec.cpp index 4ddf3bbce4e5..5178557030b7 100644 --- a/clang/test/SemaCXX/nested-name-spec.cpp +++ b/clang/test/SemaCXX/nested-name-spec.cpp @@ -13,8 +13,8 @@ namespace A { } A:: ; // expected-error {{expected unqualified-id}} -::A::ax::undef ex3; // expected-error {{no member named}} expected-error {{unknown type name 'undef'}} -A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{unknown type name 'undef2'}} +::A::ax::undef ex3; // expected-error {{no member named}} +A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} int A::C::Ag1() { return 0; } @@ -166,7 +166,7 @@ void N::f() { } // okay struct Y; // expected-note{{forward declaration of 'struct Y'}} Y::foo y; // expected-error{{incomplete type 'struct Y' named in nested name specifier}} \ - // expected-error{{unknown type name 'foo'}} + // expected-error{{no type named 'foo' in}} X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}} \ // expected-error{{C++ requires a type specifier for all declarations}} \ diff --git a/clang/test/SemaCXX/unknown-type-name.cpp b/clang/test/SemaCXX/unknown-type-name.cpp new file mode 100644 index 000000000000..054212951338 --- /dev/null +++ b/clang/test/SemaCXX/unknown-type-name.cpp @@ -0,0 +1,29 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// PR3990 +namespace N { + struct Wibble { + }; + + typedef Wibble foo; +} +using namespace N; + +foo::bar x; // expected-error{{no type named 'bar' in 'struct N::Wibble'}} + +void f() { + foo::bar = 4; // expected-error{{no member named 'bar' in 'struct N::Wibble'}} +} + +template +struct A { + typedef T type; + + type f(); +}; + +template +A::type g(T t) { return t; } // expected-error{{missing 'typename'}} + +template +A::type A::f() { return type(); } // expected-error{{missing 'typename'}} diff --git a/clang/test/SemaTemplate/instantiate-typedef.cpp b/clang/test/SemaTemplate/instantiate-typedef.cpp index d30309cc86ca..e092b531582b 100644 --- a/clang/test/SemaTemplate/instantiate-typedef.cpp +++ b/clang/test/SemaTemplate/instantiate-typedef.cpp @@ -12,5 +12,5 @@ add_pointer::type test2(int * ptr) { } add_pointer::type // expected-note{{in instantiation of template class 'struct add_pointer' requested here}} \ -// expected-error {{unknown type name 'type'}} +// expected-error {{no type named 'type' in 'struct add_pointer'}} test3(); diff --git a/clang/test/SemaTemplate/typename-specifier.cpp b/clang/test/SemaTemplate/typename-specifier.cpp index 62795eb0eeea..2501b8d048ff 100644 --- a/clang/test/SemaTemplate/typename-specifier.cpp +++ b/clang/test/SemaTemplate/typename-specifier.cpp @@ -43,12 +43,12 @@ namespace N { N::X::type *ip4 = &i; N::X::type *ip5 = &i; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ -// expected-error{{unknown type name 'type'}} +// expected-error{{no type named 'type' in}} N::X::type *ip6 = &i; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ -// expected-error{{unknown type name 'type'}} +// expected-error{{no type named 'type' in}} N::X::type fail1; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ -// expected-error{{unknown type name 'type'}} +// expected-error{{no type named 'type' in}} template struct Y { @@ -70,6 +70,6 @@ struct C { ::Y::type ip7 = &i; ::Y::type ip8 = &i; // expected-note{{in instantiation of template class 'struct Y' requested here}} \ -// expected-error{{unknown type name 'type'}} +// expected-error{{no type named 'type' in}} ::Y::type ip9 = &i; // expected-note{{in instantiation of template class 'struct Y' requested here}} \ -// expected-error{{unknown type name 'type'}} +// expected-error{{no type named 'type' in}}