Improve recovery from invalid template-ids.

Instead of bailing out of parsing when we encounter an invalid
template-name or template arguments in a template-id, produce an
annotation token describing the invalid construct.

This avoids duplicate errors and generally allows us to recover better.
In principle we should be able to extend this to store some kinds of
invalid template-id in the AST for tooling use, but that isn't handled
as part of this change.
This commit is contained in:
Richard Smith 2020-03-27 17:08:26 -07:00
parent 569e4f9bc9
commit b3f6e3d6d6
27 changed files with 218 additions and 149 deletions

View File

@ -278,6 +278,7 @@ namespace clang {
inline ExprResult ExprError() { return ExprResult(true); }
inline StmtResult StmtError() { return StmtResult(true); }
inline TypeResult TypeError() { return TypeResult(true); }
inline ExprResult ExprError(const DiagnosticBuilder&) { return ExprError(); }
inline StmtResult StmtError(const DiagnosticBuilder&) { return StmtError(); }

View File

@ -169,7 +169,9 @@ namespace clang {
/// template-name.
ParsedTemplateTy Template;
/// The kind of template that Template refers to.
/// The kind of template that Template refers to. If this is
/// TNK_Non_template, an error was encountered and diagnosed
/// when parsing or looking up the template name.
TemplateNameKind Kind;
/// The location of the '<' before the template argument
@ -183,6 +185,10 @@ namespace clang {
/// NumArgs - The number of template arguments.
unsigned NumArgs;
/// Whether an error was encountered in the template arguments.
/// If so, NumArgs and the trailing argumentst are best-effort.
bool ArgsInvalid;
/// Retrieves a pointer to the template arguments
ParsedTemplateArgument *getTemplateArgs() {
return getTrailingObjects<ParsedTemplateArgument>();
@ -195,13 +201,13 @@ namespace clang {
IdentifierInfo *Name, OverloadedOperatorKind OperatorKind,
ParsedTemplateTy OpaqueTemplateName, TemplateNameKind TemplateKind,
SourceLocation LAngleLoc, SourceLocation RAngleLoc,
ArrayRef<ParsedTemplateArgument> TemplateArgs,
ArrayRef<ParsedTemplateArgument> TemplateArgs, bool ArgsInvalid,
SmallVectorImpl<TemplateIdAnnotation *> &CleanupList) {
TemplateIdAnnotation *TemplateId = new (llvm::safe_malloc(
totalSizeToAlloc<ParsedTemplateArgument>(TemplateArgs.size())))
TemplateIdAnnotation(TemplateKWLoc, TemplateNameLoc, Name,
OperatorKind, OpaqueTemplateName, TemplateKind,
LAngleLoc, RAngleLoc, TemplateArgs);
LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid);
CleanupList.push_back(TemplateId);
return TemplateId;
}
@ -213,6 +219,20 @@ namespace clang {
this->~TemplateIdAnnotation();
free(this);
}
/// Determine whether this might be a type template.
bool mightBeType() const {
return Kind == TNK_Non_template ||
Kind == TNK_Type_template ||
Kind == TNK_Dependent_template_name ||
Kind == TNK_Undeclared_template;
}
bool hasInvalidName() const { return Kind == TNK_Non_template; }
bool hasInvalidArgs() const { return ArgsInvalid; }
bool isInvalid() const { return hasInvalidName() || hasInvalidArgs(); }
private:
TemplateIdAnnotation(const TemplateIdAnnotation &) = delete;
@ -222,11 +242,12 @@ namespace clang {
ParsedTemplateTy OpaqueTemplateName,
TemplateNameKind TemplateKind,
SourceLocation LAngleLoc, SourceLocation RAngleLoc,
ArrayRef<ParsedTemplateArgument> TemplateArgs) noexcept
ArrayRef<ParsedTemplateArgument> TemplateArgs,
bool ArgsInvalid) noexcept
: TemplateKWLoc(TemplateKWLoc), TemplateNameLoc(TemplateNameLoc),
Name(Name), Operator(OperatorKind), Template(OpaqueTemplateName),
Kind(TemplateKind), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc),
NumArgs(TemplateArgs.size()) {
NumArgs(TemplateArgs.size()), ArgsInvalid(ArgsInvalid) {
std::uninitialized_copy(TemplateArgs.begin(), TemplateArgs.end(),
getTemplateArgs());

View File

@ -1114,17 +1114,14 @@ bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
break;
}
// If what follows could be a declaration, it is a declaration.
if (Result != TPResult::False && Result != TPResult::Error) {
PA.Revert();
return true;
}
// In the uncommon case that we decide the following tokens are part
// of a template argument, revert any annotations we've performed in
// those tokens. We're not going to look them up until we've parsed
// the rest of the class, and that might add more declarations.
// Put the token stream back and undo any annotations we performed
// after the comma. They may reflect a different parse than the one
// we will actually perform at the end of the class.
PA.RevertAnnotations();
// If what follows could be a declaration, it is a declaration.
if (Result != TPResult::False && Result != TPResult::Error)
return true;
}
// Keep going. We know we're inside a template argument list now.

View File

@ -3162,9 +3162,19 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// We are looking for a qualified typename.
Token Next = NextToken();
if (Next.is(tok::annot_template_id) &&
static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
->Kind == TNK_Type_template) {
TemplateIdAnnotation *TemplateId = Next.is(tok::annot_template_id)
? takeTemplateIdAnnotation(Next)
: nullptr;
if (TemplateId && TemplateId->hasInvalidName()) {
// We found something like 'T::U<Args> x', but U is not a template.
// Assume it was supposed to be a type.
DS.SetTypeSpecError();
ConsumeAnnotationToken();
break;
}
if (TemplateId && TemplateId->Kind == TNK_Type_template) {
// We have a qualified template-id, e.g., N::A<int>
// If this would be a valid constructor declaration with template
@ -3174,7 +3184,6 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
//
// To improve diagnostics for this case, parse the declaration as a
// constructor (and reject the extra template arguments later).
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next);
if ((DSContext == DeclSpecContext::DSC_top_level ||
DSContext == DeclSpecContext::DSC_class) &&
TemplateId->Name &&
@ -3195,9 +3204,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
continue;
}
if (Next.is(tok::annot_template_id) &&
static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
->Kind == TNK_Concept_template &&
if (TemplateId && TemplateId->Kind == TNK_Concept_template &&
GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) {
DS.getTypeSpecScope() = SS;
// This is a qualified placeholder-specifier, e.g., ::C<int> auto ...
@ -3459,7 +3466,18 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// type-name or placeholder-specifier
case tok::annot_template_id: {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->hasInvalidName()) {
DS.SetTypeSpecError();
break;
}
if (TemplateId->Kind == TNK_Concept_template) {
// If we've already diagnosed that this type-constraint has invalid
// arguemnts, drop it and just form 'auto' or 'decltype(auto)'.
if (TemplateId->hasInvalidArgs())
TemplateId = nullptr;
if (NextToken().is(tok::identifier)) {
Diag(Loc, diag::err_placeholder_expected_auto_or_decltype_auto)
<< FixItHint::CreateInsertion(NextToken().getLocation(), "auto");
@ -5197,14 +5215,30 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
// placeholder-type-specifier
case tok::annot_template_id: {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->hasInvalidName())
return true;
// FIXME: What about type templates that have only been annotated as
// annot_template_id, not as annot_typename?
return isTypeConstraintAnnotation() &&
(NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype));
(NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype));
}
case tok::annot_cxxscope:
case tok::annot_cxxscope: {
TemplateIdAnnotation *TemplateId =
NextToken().is(tok::annot_template_id)
? takeTemplateIdAnnotation(NextToken())
: nullptr;
if (TemplateId && TemplateId->hasInvalidName())
return true;
// FIXME: What about type templates that have only been annotated as
// annot_template_id, not as annot_typename?
if (NextToken().is(tok::identifier) && TryAnnotateTypeConstraint())
return true;
return isTypeConstraintAnnotation() &&
GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype);
}
case tok::kw___declspec:
case tok::kw___cdecl:
case tok::kw___stdcall:

View File

@ -1147,9 +1147,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
// Check whether we have a template-id that names a type.
if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->Kind == TNK_Type_template ||
TemplateId->Kind == TNK_Dependent_template_name ||
TemplateId->Kind == TNK_Undeclared_template) {
if (TemplateId->mightBeType()) {
AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true);
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
@ -1176,7 +1174,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
if (Tok.is(tok::less)) {
// It looks the user intended to write a template-id here, but the
// template-name was wrong. Try to fix that.
TemplateNameKind TNK = TNK_Type_template;
// FIXME: Invoke ParseOptionalCXXScopeSpecifier in a "'template' is neither
// required nor permitted" mode, and do this there.
TemplateNameKind TNK = TNK_Non_template;
TemplateTy Template;
if (!Actions.DiagnoseUnknownTemplateName(*Id, IdLoc, getCurScope(),
&SS, Template, TNK)) {
@ -1184,14 +1184,6 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
<< Id;
}
if (!Template) {
TemplateArgList TemplateArgs;
SourceLocation LAngleLoc, RAngleLoc;
ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs,
RAngleLoc);
return true;
}
// Form the template name
UnqualifiedId TemplateName;
TemplateName.setIdentifier(Id, IdLoc);
@ -1200,7 +1192,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
TemplateName))
return true;
if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name)
if (Tok.is(tok::annot_template_id) &&
takeTemplateIdAnnotation(Tok)->mightBeType())
AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true);
// If we didn't end up with a typename token, there's nothing more we
@ -1630,9 +1623,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
NameLoc = ConsumeAnnotationToken();
if (TemplateId->Kind == TNK_Undeclared_template) {
// Try to resolve the template name to a type template.
Actions.ActOnUndeclaredTypeTemplateName(getCurScope(), TemplateId->Template,
TemplateId->Kind, NameLoc, Name);
// Try to resolve the template name to a type template. May update Kind.
Actions.ActOnUndeclaredTypeTemplateName(
getCurScope(), TemplateId->Template, TemplateId->Kind, NameLoc, Name);
if (TemplateId->Kind == TNK_Undeclared_template) {
RecoverFromUndeclaredTemplateName(
Name, NameLoc,
@ -1641,10 +1634,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
}
}
if (TemplateId && TemplateId->Kind != TNK_Type_template &&
TemplateId->Kind != TNK_Dependent_template_name) {
if (TemplateId && !TemplateId->mightBeType()) {
// The template-name in the simple-template-id refers to
// something other than a class template. Give an appropriate
// something other than a type template. Give an appropriate
// error message and skip to the ';'.
SourceRange Range(NameLoc);
if (SS.isNotEmpty())
@ -1816,7 +1808,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// or explicit instantiation.
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
if (TemplateId->isInvalid()) {
// Can't build the declaration.
} else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
TUK == Sema::TUK_Declaration) {
// This is an explicit instantiation of a class template.
ProhibitAttributes(attrs);
@ -3533,9 +3527,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
TemplateIdAnnotation *TemplateId = Tok.is(tok::annot_template_id)
? takeTemplateIdAnnotation(Tok)
: nullptr;
if (TemplateId && (TemplateId->Kind == TNK_Type_template ||
TemplateId->Kind == TNK_Dependent_template_name ||
TemplateId->Kind == TNK_Undeclared_template)) {
if (TemplateId && TemplateId->mightBeType()) {
AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true);
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
TemplateTypeTy = getTypeAnnotation(Tok);

View File

@ -134,7 +134,7 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
/// \param MayBePseudoDestructor When non-NULL, points to a flag that
/// indicates whether this nested-name-specifier may be part of a
/// pseudo-destructor name. In this case, the flag will be set false
/// if we don't actually end up parsing a destructor name. Moreorover,
/// if we don't actually end up parsing a destructor name. Moreover,
/// if we do end up determining that we are parsing a destructor name,
/// the last component of the nested-name-specifier is not parsed as
/// part of the scope specifier.
@ -356,7 +356,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
if (TemplateId->isInvalid() ||
Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
SS,
TemplateId->TemplateKWLoc,
TemplateId->Template,
@ -512,7 +513,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
(IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
// If we had errors before, ObjectType can be dependent even without any
// templates, do not report missing template keyword in that case.
// templates. Do not report missing template keyword in that case.
if (!ObjectHadErrors) {
// We have something like t::getAs<T>, where getAs is a
// member of an unknown specialization. However, this will only
@ -1738,8 +1739,11 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
CCLoc = ConsumeToken();
} else if (Tok.is(tok::annot_template_id)) {
FirstTypeName.setTemplateId(
(TemplateIdAnnotation *)Tok.getAnnotationValue());
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
// FIXME: Carry on and build an AST representation for tooling.
if (TemplateId->isInvalid())
return ExprError();
FirstTypeName.setTemplateId(TemplateId);
ConsumeAnnotationToken();
assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
CCLoc = ConsumeToken();
@ -2306,8 +2310,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
if (TNK == TNK_Non_template)
return true;
} else {
bool MemberOfUnknownSpecialization;
TNK = Actions.isTemplateName(getCurScope(), SS,
@ -2347,8 +2349,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
if (TNK == TNK_Non_template)
return true;
} else if (TNK == TNK_Non_template) {
return false;
}
}
break;
@ -2361,6 +2363,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(
TemplateName, ObjectType,
EnteringContext, Template,
MemberOfUnknownSpecialization);
if (TNK == TNK_Non_template)
return false;
break;
}
@ -2372,8 +2376,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true);
if (TNK == TNK_Non_template)
return true;
} else {
TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
TemplateName, ObjectType,
@ -2383,7 +2385,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
Diag(NameLoc, diag::err_destructor_template_id)
<< Name << SS.getRange();
return true;
// Carry on to parse the template arguments before bailing out.
}
}
break;
@ -2393,9 +2395,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(
return false;
}
if (TNK == TNK_Non_template)
return false;
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
@ -2403,6 +2402,10 @@ bool Parser::ParseUnqualifiedIdTemplateId(
RAngleLoc))
return true;
// If this is a non-template, we already issued a diagnostic.
if (TNK == TNK_Non_template)
return true;
if (Id.getKind() == UnqualifiedIdKind::IK_Identifier ||
Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) {
@ -2420,7 +2423,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK,
LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds);
LAngleLoc, RAngleLoc, TemplateArgs, /*ArgsInvalid*/false, TemplateIds);
Id.setTemplateId(TemplateId);
return false;
@ -2800,6 +2803,13 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
// FIXME: Consider passing invalid template-ids on to callers; they may
// be able to recover better than we can.
if (TemplateId->isInvalid()) {
ConsumeAnnotationToken();
return true;
}
// If the template-name names the current class, then this is a constructor
if (AllowConstructorName && TemplateId->Name &&
Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) {
@ -3541,6 +3551,8 @@ ExprResult Parser::ParseRequiresExpression() {
} else {
TemplateId = takeTemplateIdAnnotation(Tok);
ConsumeAnnotationToken();
if (TemplateId->isInvalid())
break;
}
if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,

View File

@ -1220,16 +1220,17 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
if (Invalid) {
// Try to find the closing '>'.
// FIXME: Handle `>>`, `>>>`.
if (ConsumeLastToken)
SkipUntil(tok::greater, StopAtSemi);
else
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
return true;
}
}
return ParseGreaterThanInTemplateList(RAngleLoc, ConsumeLastToken,
/*ObjCGenericList=*/false);
/*ObjCGenericList=*/false) ||
Invalid;
}
/// Replace the tokens that form a simple-template-id with an
@ -1280,12 +1281,13 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
bool AllowTypeAnnotation,
bool TypeConstraint) {
assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++");
assert(Template && (Tok.is(tok::less) || TypeConstraint) &&
assert((Tok.is(tok::less) || TypeConstraint) &&
"Parser isn't at the beginning of a template-id");
assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be "
"a type annotation");
assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint "
"must accompany a concept name");
assert((Template || TNK == TNK_Non_template) && "missing template name");
// Consume the template-name.
SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin();
@ -1293,40 +1295,31 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
bool ArgsInvalid = false;
if (!TypeConstraint || Tok.is(tok::less)) {
bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
TemplateArgs,
RAngleLoc);
if (Invalid) {
// If we failed to parse the template ID but skipped ahead to a >, we're not
// going to be able to form a token annotation. Eat the '>' if present.
TryConsumeToken(tok::greater);
// FIXME: Annotate the token stream so we don't produce the same errors
// again if we're doing this annotation as part of a tentative parse.
ArgsInvalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
TemplateArgs, RAngleLoc);
// If we couldn't recover from invalid arguments, don't form an annotation
// token -- we don't know how much to annotate.
// FIXME: This can lead to duplicate diagnostics if we retry parsing this
// template-id in another context. Try to annotate anyway?
if (RAngleLoc.isInvalid())
return true;
}
}
ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
// Build the annotation token.
if (TNK == TNK_Type_template && AllowTypeAnnotation) {
TypeResult Type = Actions.ActOnTemplateIdType(
getCurScope(), SS, TemplateKWLoc, Template, TemplateName.Identifier,
TemplateNameLoc, LAngleLoc, TemplateArgsPtr, RAngleLoc);
if (Type.isInvalid()) {
// If we failed to parse the template ID but skipped ahead to a >, we're
// not going to be able to form a token annotation. Eat the '>' if
// present.
TryConsumeToken(tok::greater);
// FIXME: Annotate the token stream so we don't produce the same errors
// again if we're doing this annotation as part of a tentative parse.
return true;
}
TypeResult Type = ArgsInvalid
? TypeError()
: Actions.ActOnTemplateIdType(
getCurScope(), SS, TemplateKWLoc, Template,
TemplateName.Identifier, TemplateNameLoc,
LAngleLoc, TemplateArgsPtr, RAngleLoc);
Tok.setKind(tok::annot_typename);
setTypeAnnotation(Tok, Type.get());
setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get());
if (SS.isNotEmpty())
Tok.setLocation(SS.getBeginLoc());
else if (TemplateKWLoc.isValid())
@ -1350,7 +1343,7 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK,
LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds);
LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid, TemplateIds);
Tok.setAnnotationValue(TemplateId);
if (TemplateKWLoc.isValid())
@ -1386,26 +1379,21 @@ void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
assert((TemplateId->Kind == TNK_Type_template ||
TemplateId->Kind == TNK_Dependent_template_name ||
TemplateId->Kind == TNK_Undeclared_template) &&
assert(TemplateId->mightBeType() &&
"Only works for type and dependent templates");
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult Type
= Actions.ActOnTemplateIdType(getCurScope(),
SS,
TemplateId->TemplateKWLoc,
TemplateId->Template,
TemplateId->Name,
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->RAngleLoc,
/*IsCtorOrDtorName*/false,
IsClassName);
TypeResult Type =
TemplateId->isInvalid()
? TypeError()
: Actions.ActOnTemplateIdType(
getCurScope(), SS, TemplateId->TemplateKWLoc,
TemplateId->Template, TemplateId->Name,
TemplateId->TemplateNameLoc, TemplateId->LAngleLoc,
TemplateArgsPtr, TemplateId->RAngleLoc,
/*IsCtorOrDtorName*/ false, IsClassName);
// Create the new "type" annotation token.
Tok.setKind(tok::annot_typename);
setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get());
@ -1420,6 +1408,7 @@ void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
/// Determine whether the given token can end a template argument.
static bool isEndOfTemplateArgument(Token Tok) {
// FIXME: Handle '>>>'.
return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater);
}
@ -1572,10 +1561,8 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) {
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc);
if (Arg.isInvalid()) {
SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
if (Arg.isInvalid())
return true;
}
// Save this template argument.
TemplateArgs.push_back(Arg);

View File

@ -984,10 +984,16 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
NextToken().is(tok::kw_operator)))) &&
mayHaveIdentifier) {
// declarator-id
if (Tok.is(tok::annot_cxxscope))
if (Tok.is(tok::annot_cxxscope)) {
CXXScopeSpec SS;
Actions.RestoreNestedNameSpecifierAnnotation(
Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
if (SS.isInvalid())
return TPResult::Error;
ConsumeAnnotationToken();
else if (Tok.is(tok::identifier))
} else if (Tok.is(tok::identifier)) {
TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo());
}
if (Tok.is(tok::kw_operator)) {
if (TryParseOperatorId() == TPResult::Error)
return TPResult::Error;
@ -1547,6 +1553,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
*InvalidAsDeclSpec = NextToken().is(tok::l_paren);
return TPResult::Ambiguous;
}
if (TemplateId->hasInvalidName())
return TPResult::Error;
if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0))
return TPResult::True;
if (TemplateId->Kind != TNK_Type_template)
@ -1566,6 +1574,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
NextToken().is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId =
takeTemplateIdAnnotation(NextToken());
if (TemplateId->hasInvalidName())
return TPResult::Error;
if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1))
return TPResult::True;
}
@ -2012,17 +2022,14 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
// (a) the previous parameter did, and
// (b) this must be the first declaration of the function, so we can't
// inherit any default arguments from elsewhere.
// If we see an ')', then we've reached the end of a
// parameter-declaration-clause, and the last param is missing its default
// argument.
// FIXME: If we reach a ')' without consuming any '>'s, then this must
// also be a function parameter (that's missing its default argument).
if (VersusTemplateArgument)
return Tok.isOneOf(tok::equal, tok::r_paren) ? TPResult::True
: TPResult::False;
return Tok.is(tok::equal) ? TPResult::True : TPResult::False;
if (Tok.is(tok::equal)) {
// '=' assignment-expression
// Parse through assignment-expression.
// FIXME: assignment-expression may contain an unparenthesized comma.
if (!SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch))
return TPResult::Error;
}

View File

@ -1878,6 +1878,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
Tok.getLocation());
} else if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->isInvalid())
return true;
if (TemplateId->Kind != TNK_Type_template &&
TemplateId->Kind != TNK_Dependent_template_name &&
TemplateId->Kind != TNK_Undeclared_template) {

View File

@ -30,6 +30,9 @@ using namespace clang;
void UnqualifiedId::setTemplateId(TemplateIdAnnotation *TemplateId) {
assert(TemplateId && "NULL template-id annotation?");
assert(!TemplateId->isInvalid() &&
"should not convert invalid template-ids to unqualified-ids");
Kind = UnqualifiedIdKind::IK_TemplateId;
this->TemplateId = TemplateId;
StartLocation = TemplateId->TemplateNameLoc;
@ -38,6 +41,9 @@ void UnqualifiedId::setTemplateId(TemplateIdAnnotation *TemplateId) {
void UnqualifiedId::setConstructorTemplateId(TemplateIdAnnotation *TemplateId) {
assert(TemplateId && "NULL template-id annotation?");
assert(!TemplateId->isInvalid() &&
"should not convert invalid template-ids to unqualified-ids");
Kind = UnqualifiedIdKind::IK_ConstructorTemplateId;
this->TemplateId = TemplateId;
StartLocation = TemplateId->TemplateNameLoc;

View File

@ -123,7 +123,7 @@ namespace dr305 { // dr305: no
template<typename T> using T2 = T;
};
void k(Z *z) {
z->~T1<int>(); // expected-error {{no member named 'T1' in 'dr305::Z'}} expected-error +{{}}
z->~T1<int>(); // expected-error {{no member named 'T1' in 'dr305::Z'}}
z->~T2<int>(); // expected-error {{no member named '~int'}}
z->~T2<Z>();
}

View File

@ -7,9 +7,7 @@ template<typename T> struct S {
template<typename T> int S<T>::a, S<T>::b; // expected-error {{can only declare a single entity}}
template<typename T> struct A { static A a; } A<T>::a; // expected-error {{expected ';' after struct}} \
expected-error {{use of undeclared identifier 'T'}} \
expected-error {{no member named 'a'}} \
expected-warning {{extra qualification}}
expected-error {{use of undeclared identifier 'T'}}
template<typename T> struct B { } f(); // expected-error {{expected ';' after struct}} \
expected-error {{requires a type specifier}}

View File

@ -2,6 +2,7 @@
template<int> struct c { c(int) = delete; typedef void val; operator int() const; };
int f;
int val;
int foobar;
struct S {
@ -29,6 +30,12 @@ struct S {
int k6 = a < b, c < d > (n) // expected-error {{undeclared identifier 'n'}}
);
static void f1b(
int k6 = a < b, c < d > (f)
);
using f1b_T = decltype(f1b(0)); // only one parameter, because second param
// would be missing its default argument
void f2a(
// T3<int> here is a parameter type, so must be declared before it is used.
int k1 = c < b, T3 < int > x = 0 // expected-error {{no template named 'T3'}}
@ -56,14 +63,21 @@ struct S {
int missing_default // expected-error {{missing default argument on parameter}}
);
// FIXME: We should ideally disambiguate this as two parameters.
void f6(
int k = b < c,
unsigned int (missing_default) // expected-error {{missing default argument on parameter}}
int k = b < c, // expected-error {{unexpected end of default argument}}
unsigned int (missing_default)
);
template<int, int=0> struct a { static const int val = 0; operator int(); }; // expected-note {{here}}
template<int, int = 0> struct a { // expected-note {{here}}
a();
a(int);
static const int val = 0;
operator int();
};
static const int b = 0, c = 1, d = 2, goobar = 3;
template<int, typename> struct e { operator int(); };
static const int f = 0;
int mp1 = 0 < 1,
a<b<c,b<c>::*mp2,

View File

@ -211,9 +211,9 @@ namespace DtorErrors {
};
struct T {};
T t1 = t1.T::~T<int>; // expected-error {{destructor name 'T' does not refer to a template}} expected-error {{expected '(' for function-style cast or type construction}} expected-error {{expected expression}}
T t1 = t1.T::~T<int>; // expected-error {{destructor name 'T' does not refer to a template}}
// Emit the same diagnostic as for the previous case, plus something about ~.
T t2 = t2.~T::T<int>; // expected-error {{'~' in destructor name should be after nested name specifier}} expected-error {{destructor name 'T' does not refer to a template}} expected-error {{expected '(' for function-style cast or type construction}} expected-error {{expected expression}}
T t2 = t2.~T::T<int>; // expected-error {{'~' in destructor name should be after nested name specifier}} expected-error {{destructor name 'T' does not refer to a template}}
}
namespace BadFriend {

View File

@ -103,5 +103,5 @@ class G {
void l(int x = C<int, C<int, int>::C1>().f()) {}
// This isn't, but it shouldn't crash. The diagnostics don't matter much.
void m(int x = C<int, union int>().f()) {} // expected-error {{declaration of anonymous union must be a definition}} expected-error {{expected a type}}
void m(int x = C<int, union int>().f()) {} // expected-error {{declaration of anonymous union must be a definition}} expected-error {{expected a type}} expected-error {{expected '>'}}
};

View File

@ -235,11 +235,11 @@ struct base { };
struct t1 : base<int,
public: // expected-error {{expected expression}}
};
}; // expected-error {{expected '>'}}
// expected-error@-1 {{expected '{' after base class list}}
struct t2 : base<int,
public // expected-error {{expected expression}}
};
}; // expected-error {{expected '>'}}
// expected-error@-1 {{expected '{' after base class list}}
}

View File

@ -1,11 +1,12 @@
// RUN: not %clang_cc1 %s -fsyntax-only 2>&1 | FileCheck %s
// CHECK: error: expected expression
// CHECK: error: expected '>'
// CHECK: error: expected member name or ';' after declaration specifiers
// CHECK: error: expected '}'
// CHECK: note: to match this '{'
// CHECK: error: expected ';' after class
// CHECK: 4 errors generated.
// CHECK: 5 errors generated.
// Do not add anything to the end of this file. This requires the whitespace
// plus EOF after the '<' token.

View File

@ -133,7 +133,10 @@ Foo<int> missingSemiBeforeFunctionReturningTemplateId2();
namespace PR17084 {
enum class EnumID {};
template <typename> struct TempID;
template <> struct TempID<BadType> : BadType, EnumID::Garbage; // expected-error{{use of undeclared identifier 'BadType'}}
template <> struct TempID<BadType> // expected-error{{use of undeclared identifier 'BadType'}}
: BadType, // expected-error {{expected class name}}
EnumID::Garbage // expected-error {{expected class name}}
; // expected-error@-1 {{expected '{' after base class list}}
}
namespace pr15133 {

View File

@ -4,8 +4,7 @@ template <typename T>
struct X {};
auto b = []() {
struct S {
static typename X<decltype(int)>::type Run(){};
// expected-error@-1 4{{}}
static typename X<decltype(int)>::type Run(){}; // expected-error {{expected '('}}
};
return 5;
}();
@ -16,6 +15,5 @@ class PC {
template <typename T>
class P {
static typename PC<T, Invalid>::Type Foo();
// expected-error@-1 4{{}}
static typename PC<T, Invalid>::Type Foo(); // expected-error {{undeclared identifier 'Invalid'}}
};

View File

@ -2,6 +2,6 @@
// Don't crash.
template<typename>struct ae_same;
template<typename>struct ae_same; // expected-note {{}}
template<typename>struct ts{}ap() // expected-error {{expected ';' after struct}} expected-error {{requires a type specifier}}
{ts<a>::ap<ae_same<int>::&ae_same<>>::p(a); }; // expected-error {{use of undeclared identifier 'a'}}
{ts<a>::ap<ae_same<int>::&ae_same<>>::p(a); }; // expected-error {{use of undeclared identifier 'a'}} expected-error 5{{}}

View File

@ -14,8 +14,7 @@ template<int (*Compare)(const char *s1, const char *s2)>
int equal(const char *s1, const char *s2) {
return Compare(s1, s2) == 0;
}
// FIXME: Our error recovery here sucks
template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}} expected-error {{expected unqualified-id}} expected-error {{expected ')'}} expected-note {{to match this '('}}
template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}}
// PR13195
void f2() {

View File

@ -56,7 +56,7 @@ namespace ExceptionSpecification {
namespace DefaultArgument {
struct Default {
struct T {
T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to implicitly-deleted default constructor}}
T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to implicitly-deleted default constructor}} expected-error {{expected '>'}}
} t; // expected-note {{has no default constructor}}
};
}

View File

@ -3,7 +3,7 @@
template <class T>
struct X : public Foo<Bar { // expected-error {{unknown template name 'Foo'}} expected-error {{use of undeclared identifier 'Bar'}}
X();
}; // expected-error {{expected '{' after base class list}}
}; // expected-error {{expected '>'}} expected-error {{expected '{' after base class list}}
template <class T>

View File

@ -54,10 +54,7 @@ namespace test3 {
namespace rdar11293995 {
struct Length {
// FIXME: We try to annotate the template-id here during tentative parsing,
// and fail, then try again during the actual parse. This results in the same
// diagnostic being produced twice. :(
explicit Length(PassRefPtr<CalculationValue>); // expected-error 2{{undeclared identifier 'CalculationValue'}}
explicit Length(PassRefPtr<CalculationValue>); // expected-error {{undeclared identifier 'CalculationValue'}}
};
struct LengthSize {

View File

@ -50,7 +50,7 @@ template <typename T = Bar<Weber>> // expected-error {{use of undeclared identi
struct Foo {
static_assert(sizeof(T) == 4, "Bar should have gotten int");
// FIXME: These diagnostics are bad.
}; // expected-error {{expected ',' or '>' in template-parameter-list}}
}; // expected-error {{expected ',' or '>' in template-parameter-list}} expected-error {{expected '>'}}
// expected-warning@-1 {{does not declare anything}}
typedef int Weber;
}

View File

@ -15,6 +15,6 @@ A a4; // expected-error{{use of class template 'A' requires template arguments}}
namespace test0 {
template <class t> class foo {};
template <class t> class bar {
bar(::test0::foo<tee> *ptr) {} // FIXME(redundant): expected-error 2 {{use of undeclared identifier 'tee'}}
bar(::test0::foo<tee> *ptr) {} // expected-error {{use of undeclared identifier 'tee'}}
};
}

View File

@ -102,7 +102,7 @@ namespace PtrMem {
static_assert(!is_same<Ab, Abce>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Ab, Abde>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Abce, Abde>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
using Ae = A<int E::*, e>;
using Ae = A<int E::*, &E::e>;
@ -111,7 +111,7 @@ namespace PtrMem {
static_assert(!is_same<Ae, Aecb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Ae, Aedb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Aecb, Aedb>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
using An = A<int E::*, nullptr>;
using A0 = A<int E::*, (int E::*)0>;