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<
"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
"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">;
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,
}
}
- // 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);
llvm::raw_string_ostream OS(S);
reinterpret_cast<NestedNameSpecifier*> (Val)->print(OS,
Context.PrintingPolicy);
+ NeedQuotes = false;
} else {
assert(Kind == Diagnostic::ak_declcontext);
DeclContext *DC = reinterpret_cast<DeclContext *> (Val);
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);
}
#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"
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
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;
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}}
}
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; }
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}} \
--- /dev/null
+// 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<typename T>
+struct A {
+ typedef T type;
+
+ type f();
+};
+
+template<typename T>
+A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
+
+template<typename T>
+A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}
}
add_pointer<int&>::type // expected-note{{in instantiation of template class 'struct add_pointer<int &>' requested here}} \
-// expected-error {{unknown type name 'type'}}
+// expected-error {{no type named 'type' in 'struct add_pointer<int &>'}}
test3();
N::X<N::A>::type *ip4 = &i;
N::X<N::B>::type *ip5 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::B>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
N::X<N::C>::type *ip6 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::C>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
N::X<int>::type fail1; // expected-note{{in instantiation of template class 'struct N::X<int>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
template<typename T>
struct Y {
::Y<A>::type ip7 = &i;
::Y<B>::type ip8 = &i; // expected-note{{in instantiation of template class 'struct Y<struct B>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}
::Y<C>::type ip9 = &i; // expected-note{{in instantiation of template class 'struct Y<struct C>' requested here}} \
-// expected-error{{unknown type name 'type'}}
+// expected-error{{no type named 'type' in}}