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(); }
/// 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
/// 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>();
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;
}
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;
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());
break;
}
+ // 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) {
- PA.Revert();
+ if (Result != TPResult::False && Result != TPResult::Error)
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.
- PA.RevertAnnotations();
}
// Keep going. We know we're inside a template argument list now.
// 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
//
// 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 &&
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 ...
// 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");
// 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:
// 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");
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)) {
<< 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);
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
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,
}
}
- 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())
// 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);
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);
/// \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.
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
- if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
+ if (TemplateId->isInvalid() ||
+ Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
SS,
TemplateId->TemplateKWLoc,
TemplateId->Template,
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
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();
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,
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;
TemplateName, ObjectType,
EnteringContext, Template,
MemberOfUnknownSpecialization);
+ if (TNK == TNK_Non_template)
+ return false;
break;
}
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,
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;
return false;
}
- if (TNK == TNK_Non_template)
- return false;
-
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
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) {
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;
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)) {
} else {
TemplateId = takeTemplateIdAnnotation(Tok);
ConsumeAnnotationToken();
+ if (TemplateId->isInvalid())
+ break;
}
if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,
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
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();
// 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())
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())
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());
/// 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);
}
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);
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;
*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)
NextToken().is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId =
takeTemplateIdAnnotation(NextToken());
+ if (TemplateId->hasInvalidName())
+ return TPResult::Error;
if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1))
return TPResult::True;
}
// (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;
}
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) {
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;
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;
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>();
}
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}}
template<int> struct c { c(int) = delete; typedef void val; operator int() const; };
+int f;
int val;
int foobar;
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'}}
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,
};
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 {
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 '>'}}
};
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}}
}
// 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.
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 {
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;
}();
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'}}
};
// 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{{}}
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() {
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}}
};
}
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>
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 {
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;
}
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'}}
};
}
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>;
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>;