[clang] 0dd0b10 - [Parser] Avoid spurious 'missing template' error in presence of typos.
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 19 08:15:43 PDT 2020
Author: Haojian Wu
Date: 2020-03-19T16:15:27+01:00
New Revision: 0dd0b1017c807e7a803e69be7bdc5c0ac7b469d8
URL: https://github.com/llvm/llvm-project/commit/0dd0b1017c807e7a803e69be7bdc5c0ac7b469d8
DIFF: https://github.com/llvm/llvm-project/commit/0dd0b1017c807e7a803e69be7bdc5c0ac7b469d8.diff
LOG: [Parser] Avoid spurious 'missing template' error in presence of typos.
Suppress those diagnostics if lhs of a member expression contains
errors. Typo correction produces dependent expressions even in
non-template code, that led to spurious diagnostics before.
previous:
/tmp/t.cpp:6:17: error: use 'template' keyword to treat 'f' as a dependent template name
auto a = bilder.f<int>();
^
template
/tmp/t.cpp:6:10: error: use of undeclared identifier 'bilder'; did you mean 'builder'?
auto a = bilder.f<int>();
^~~~~~
builder
vs now:
/tmp/t.cpp:6:10: error: use of undeclared identifier 'bilder'; did you mean 'builder'?
auto a = bilder.f<int>();
^~~~~~
builder
Original patch from Ilya.
Reviewers: sammccall
Reviewed By: sammccall
Tags: #clang
Differential Revision: https://reviews.llvm.org/D65592
Added:
clang/test/SemaTemplate/dependent-typos-recovery.cpp
Modified:
clang/include/clang/Parse/Parser.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Parse/ParseOpenMP.cpp
clang/lib/Parse/ParseStmtAsm.cpp
clang/lib/Parse/ParseTemplate.cpp
clang/lib/Parse/Parser.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index a9c68a2e231e..9a22bac75e62 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1814,7 +1814,9 @@ class Parser : public CodeCompletionHandler {
bool EnteringContext, IdentifierInfo &II,
CXXScopeSpec &SS);
- bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, ParsedType ObjectType,
+ bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
+ ParsedType ObjectType,
+ bool ObjectHasErrors,
bool EnteringContext,
bool *MayBePseudoDestructor = nullptr,
bool IsTypename = false,
@@ -2908,11 +2910,12 @@ class Parser : public CodeCompletionHandler {
AccessSpecifier getAccessSpecifierIfPresent() const;
bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
+ ParsedType ObjectType,
+ bool ObjectHadErrors,
SourceLocation TemplateKWLoc,
IdentifierInfo *Name,
SourceLocation NameLoc,
bool EnteringContext,
- ParsedType ObjectType,
UnqualifiedId &Id,
bool AssumeTemplateId);
bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
@@ -3081,13 +3084,12 @@ class Parser : public CodeCompletionHandler {
bool ParseOpenMPVarList(OpenMPDirectiveKind DKind, OpenMPClauseKind Kind,
SmallVectorImpl<Expr *> &Vars,
OpenMPVarListDataTy &Data);
- bool ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
- bool AllowDestructorName,
- bool AllowConstructorName,
+ bool ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
+ bool ObjectHadErrors, bool EnteringContext,
+ bool AllowDestructorName, bool AllowConstructorName,
bool AllowDeductionGuide,
- ParsedType ObjectType,
- SourceLocation *TemplateKWLoc,
- UnqualifiedId &Result);
+ SourceLocation *TemplateKWLoc, UnqualifiedId &Result);
+
/// Parses the mapper modifier in map, to, and from clauses.
bool parseMapperModifier(OpenMPVarListDataTy &Data);
/// Parses map-type-modifiers in map clause.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 17298dad4564..ba4f5d86612a 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4421,7 +4421,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
ColonProtectionRAIIObject X(*this, AllowDeclaration);
CXXScopeSpec Spec;
- if (ParseOptionalCXXScopeSpecifier(Spec, nullptr,
+ if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/true))
return;
@@ -5254,7 +5255,8 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) {
// Parse the C++ scope specifier.
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, nullptr,
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/true)) {
TPA.Revert();
return false;
@@ -5634,7 +5636,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
D.getContext() == DeclaratorContext::FileContext ||
D.getContext() == DeclaratorContext::MemberContext;
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, EnteringContext);
if (SS.isNotEmpty()) {
if (Tok.isNot(tok::star)) {
@@ -5857,8 +5860,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
bool EnteringContext =
D.getContext() == DeclaratorContext::FileContext ||
D.getContext() == DeclaratorContext::MemberContext;
- ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), nullptr,
- EnteringContext);
+ ParseOptionalCXXScopeSpecifier(
+ D.getCXXScopeSpec(), /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, EnteringContext);
}
if (D.getCXXScopeSpec().isValid()) {
@@ -5932,10 +5936,11 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
bool HadScope = D.getCXXScopeSpec().isValid();
if (ParseUnqualifiedId(D.getCXXScopeSpec(),
+ /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/true,
/*AllowDestructorName=*/true, AllowConstructorName,
- AllowDeductionGuide, nullptr, nullptr,
- D.getName()) ||
+ AllowDeductionGuide, nullptr, D.getName()) ||
// Once we're past the identifier, if the scope was bad, mark the
// whole declarator bad.
D.getCXXScopeSpec().isInvalid()) {
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 09e5c7996fcd..85dc4e3e706a 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -290,7 +290,9 @@ Decl *Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc,
CXXScopeSpec SS;
// Parse (optional) nested-name-specifier.
- ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false,
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
/*MayBePseudoDestructor=*/nullptr,
/*IsTypename=*/false,
/*LastII=*/nullptr,
@@ -530,7 +532,9 @@ Decl *Parser::ParseUsingDirective(DeclaratorContext Context,
CXXScopeSpec SS;
// Parse (optional) nested-name-specifier.
- ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false,
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
/*MayBePseudoDestructor=*/nullptr,
/*IsTypename=*/false,
/*LastII=*/nullptr,
@@ -597,7 +601,9 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
// Parse nested-name-specifier.
IdentifierInfo *LastII = nullptr;
- if (ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false,
+ if (ParseOptionalCXXScopeSpecifier(D.SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
/*MayBePseudoDtor=*/nullptr,
/*IsTypename=*/false,
/*LastII=*/&LastII,
@@ -632,12 +638,12 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
D.Name.setConstructorName(Type, IdLoc, IdLoc);
} else {
if (ParseUnqualifiedId(
- D.SS, /*EnteringContext=*/false,
+ D.SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext=*/false,
/*AllowDestructorName=*/true,
- /*AllowConstructorName=*/!(Tok.is(tok::identifier) &&
- NextToken().is(tok::equal)),
- /*AllowDeductionGuide=*/false,
- nullptr, nullptr, D.Name))
+ /*AllowConstructorName=*/
+ !(Tok.is(tok::identifier) && NextToken().is(tok::equal)),
+ /*AllowDeductionGuide=*/false, nullptr, D.Name))
return true;
}
@@ -1115,7 +1121,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
// Parse optional nested-name-specifier
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false))
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false))
return true;
BaseLoc = Tok.getLocation();
@@ -1547,7 +1555,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
CXXScopeSpec Spec;
bool HasValidSpec = true;
- if (ParseOptionalCXXScopeSpecifier(Spec, nullptr, EnteringContext)) {
+ if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ EnteringContext)) {
DS.SetTypeSpecError();
HasValidSpec = false;
}
@@ -2501,7 +2511,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
if (isAccessDecl) {
// Collect the scope specifier token we annotated earlier.
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, nullptr,
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false);
if (SS.isInvalid()) {
@@ -2512,8 +2523,9 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
// Try to parse an unqualified-id.
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
- if (ParseUnqualifiedId(SS, false, true, true, false, nullptr,
- &TemplateKWLoc, Name)) {
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, false, true, true,
+ false, &TemplateKWLoc, Name)) {
SkipUntil(tok::semi);
return nullptr;
}
@@ -3493,7 +3505,9 @@ void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) {
MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
// parse '::'[opt] nested-name-specifier[opt]
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false))
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false))
return true;
// : identifier
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index b038e6935d87..7bd1230a7750 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1529,7 +1529,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
// type, translate it into a type and continue parsing as a
// cast expression.
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, nullptr,
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false);
AnnotateTemplateIdTokenAsType(SS);
return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr,
@@ -1983,9 +1984,9 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
if (LHS.isInvalid())
break;
- ParseOptionalCXXScopeSpecifier(SS, ObjectType,
- /*EnteringContext=*/false,
- &MayBePseudoDestructor);
+ ParseOptionalCXXScopeSpecifier(
+ SS, ObjectType, LHS.get() && LHS.get()->containsErrors(),
+ /*EnteringContext=*/false, &MayBePseudoDestructor);
if (SS.isNotEmpty())
ObjectType = nullptr;
}
@@ -2045,14 +2046,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
IdentifierInfo *Id = Tok.getIdentifierInfo();
SourceLocation Loc = ConsumeToken();
Name.setIdentifier(Id, Loc);
- } else if (ParseUnqualifiedId(SS,
- /*EnteringContext=*/false,
- /*AllowDestructorName=*/true,
- /*AllowConstructorName=*/
- getLangOpts().MicrosoftExt &&
- SS.isNotEmpty(),
- /*AllowDeductionGuide=*/false,
- ObjectType, &TemplateKWLoc, Name)) {
+ } else if (ParseUnqualifiedId(
+ SS, ObjectType, LHS.get() && LHS.get()->containsErrors(),
+ /*EnteringContext=*/false,
+ /*AllowDestructorName=*/true,
+ /*AllowConstructorName=*/
+ getLangOpts().MicrosoftExt && SS.isNotEmpty(),
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name)) {
(void)Actions.CorrectDelayedTyposInExpr(LHS);
LHS = ExprError();
}
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 10608644a8fe..b8d91c19228f 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -124,6 +124,10 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
/// the "." or "->" of a member access expression, this parameter provides the
/// type of the object whose members are being accessed.
///
+/// \param ObjectHadErrors if this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+/// When true, diagnostics for missing 'template' keyword will be supressed.
+///
/// \param EnteringContext whether we will be entering into the context of
/// the nested-name-specifier after parsing it.
///
@@ -146,14 +150,10 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
///
///
/// \returns true if there was an error parsing a scope specifier
-bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
- ParsedType ObjectType,
- bool EnteringContext,
- bool *MayBePseudoDestructor,
- bool IsTypename,
- IdentifierInfo **LastII,
- bool OnlyNamespace,
- bool InUsingDeclaration) {
+bool Parser::ParseOptionalCXXScopeSpecifier(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename,
+ IdentifierInfo **LastII, bool OnlyNamespace, bool InUsingDeclaration) {
assert(getLangOpts().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
@@ -511,17 +511,21 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
(IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
- // We have something like t::getAs<T>, where getAs is a
- // member of an unknown specialization. However, this will only
- // parse correctly as a template, so suggest the keyword 'template'
- // before 'getAs' and treat this as a dependent template name.
- unsigned DiagID = diag::err_missing_dependent_template_keyword;
- if (getLangOpts().MicrosoftExt)
- DiagID = diag::warn_missing_dependent_template_keyword;
-
- Diag(Tok.getLocation(), DiagID)
- << II.getName()
- << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
+ // If we had errors before, ObjectType can be dependent even without any
+ // 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
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ unsigned DiagID = diag::err_missing_dependent_template_keyword;
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::warn_missing_dependent_template_keyword;
+
+ Diag(Tok.getLocation(), DiagID)
+ << II.getName()
+ << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
+ }
if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, Tok.getLocation(), TemplateName, ObjectType,
@@ -593,12 +597,12 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
default:
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
- if (ParseUnqualifiedId(SS,
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
- /*AllowDeductionGuide=*/false,
- /*ObjectType=*/nullptr, &TemplateKWLoc, Name))
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name))
return ExprError();
// This is only the direct operand of an & operator if it is not
@@ -666,7 +670,9 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
// '::' unqualified-id
//
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false);
Token Replacement;
ExprResult Result =
@@ -1769,10 +1775,10 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
// If there is a '<', the second type name is a template-id. Parse
// it as such.
if (Tok.is(tok::less) &&
- ParseUnqualifiedIdTemplateId(SS, SourceLocation(),
- Name, NameLoc,
- false, ObjectType, SecondTypeName,
- /*AssumeTemplateId=*/true))
+ ParseUnqualifiedIdTemplateId(
+ SS, ObjectType, Base && Base->containsErrors(), SourceLocation(),
+ Name, NameLoc, false, SecondTypeName,
+ /*AssumeTemplateId=*/true))
return ExprError();
return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
@@ -2259,6 +2265,12 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// \param SS the nested-name-specifier that precedes this template-id, if
/// we're actually parsing a qualified-id.
///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param ObjectHadErrors this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+///
/// \param Name for constructor and destructor names, this is the actual
/// identifier that may be a template-name.
///
@@ -2268,9 +2280,6 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// \param EnteringContext whether we're entering the scope of the
/// nested-name-specifier.
///
-/// \param ObjectType if this unqualified-id occurs within a member access
-/// expression, the type of the base object whose member is being accessed.
-///
/// \param Id as input, describes the template-name or operator-function-id
/// that precedes the '<'. If template arguments were parsed successfully,
/// will be updated with the template-id.
@@ -2279,14 +2288,10 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// refers to a template without performing name lookup to verify.
///
/// \returns true if a parse error occurred, false otherwise.
-bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
- SourceLocation TemplateKWLoc,
- IdentifierInfo *Name,
- SourceLocation NameLoc,
- bool EnteringContext,
- ParsedType ObjectType,
- UnqualifiedId &Id,
- bool AssumeTemplateId) {
+bool Parser::ParseUnqualifiedIdTemplateId(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc,
+ bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) {
assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
TemplateTy Template;
@@ -2318,23 +2323,27 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
ObjectType && isTemplateArgumentList(0) == TPResult::True) {
- // We have something like t->getAs<T>(), where getAs is a
- // member of an unknown specialization. However, this will only
- // parse correctly as a template, so suggest the keyword 'template'
- // before 'getAs' and treat this as a dependent template name.
- std::string Name;
- if (Id.getKind() == UnqualifiedIdKind::IK_Identifier)
- Name = std::string(Id.Identifier->getName());
- else {
- Name = "operator ";
- if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId)
- Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
- else
- Name += Id.Identifier->getName();
+ // If we had errors before, ObjectType can be dependent even without any
+ // 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
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ std::string Name;
+ if (Id.getKind() == UnqualifiedIdKind::IK_Identifier)
+ Name = std::string(Id.Identifier->getName());
+ else {
+ Name = "operator ";
+ if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId)
+ Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
+ else
+ Name += Id.Identifier->getName();
+ }
+ Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+ << Name
+ << FixItHint::CreateInsertion(Id.StartLocation, "template ");
}
- Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
- << Name
- << FixItHint::CreateInsertion(Id.StartLocation, "template ");
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
@@ -2691,6 +2700,13 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
/// \param SS The nested-name-specifier that preceded this unqualified-id. If
/// non-empty, then we are parsing the unqualified-id of a qualified-id.
///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param ObjectHadErrors if this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+/// When true, diagnostics for missing 'template' keyword will be supressed.
+///
/// \param EnteringContext whether we are entering the scope of the
/// nested-name-specifier.
///
@@ -2700,17 +2716,14 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
///
/// \param AllowDeductionGuide whether we allow parsing a deduction guide name.
///
-/// \param ObjectType if this unqualified-id occurs within a member access
-/// expression, the type of the base object whose member is being accessed.
-///
/// \param Result on a successful parse, contains the parsed unqualified-id.
///
/// \returns true if parsing fails, false otherwise.
-bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
+bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
+ bool ObjectHadErrors, bool EnteringContext,
bool AllowDestructorName,
bool AllowConstructorName,
bool AllowDeductionGuide,
- ParsedType ObjectType,
SourceLocation *TemplateKWLoc,
UnqualifiedId &Result) {
if (TemplateKWLoc)
@@ -2769,8 +2782,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
TemplateTy Template;
if (Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(
- SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
- EnteringContext, ObjectType, Result, TemplateSpecified);
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
+ EnteringContext, Result, TemplateSpecified);
else if (TemplateSpecified &&
Actions.ActOnDependentTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
@@ -2847,9 +2861,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) &&
Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(
- SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
- SourceLocation(), EnteringContext, ObjectType, Result,
- TemplateSpecified);
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
+ SourceLocation(), EnteringContext, Result, TemplateSpecified);
else if (TemplateSpecified &&
Actions.ActOnDependentTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
@@ -2899,7 +2913,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
AnnotateScopeToken(SS, /*NewAnnotation*/true);
SS.clear();
}
- if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, EnteringContext))
+ if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, ObjectHadErrors,
+ EnteringContext))
return true;
if (SS.isNotEmpty())
ObjectType = nullptr;
@@ -2926,8 +2941,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
if (Tok.is(tok::less)) {
Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
return ParseUnqualifiedIdTemplateId(
- SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
- ClassNameLoc, EnteringContext, ObjectType, Result, TemplateSpecified);
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
+ ClassNameLoc, EnteringContext, Result, TemplateSpecified);
}
// Note that this is a destructor name.
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 84e7b7c9995c..6f6de47aa0bb 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2287,12 +2287,14 @@ bool Parser::ParseOpenMPSimpleVarList(
NoIdentIsFound = false;
if (AllowScopeSpecifier && getLangOpts().CPlusPlus &&
- ParseOptionalCXXScopeSpecifier(SS, nullptr, false)) {
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, false)) {
IsCorrect = false;
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
- } else if (ParseUnqualifiedId(SS, false, false, false, false, nullptr,
- nullptr, Name)) {
+ } else if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, false, false,
+ false, false, nullptr, Name)) {
IsCorrect = false;
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
@@ -2874,11 +2876,12 @@ static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec,
return false;
}
}
- return P.ParseUnqualifiedId(ReductionIdScopeSpec, /*EnteringContext*/ false,
- /*AllowDestructorName*/ false,
- /*AllowConstructorName*/ false,
- /*AllowDeductionGuide*/ false,
- nullptr, nullptr, ReductionId);
+ return P.ParseUnqualifiedId(
+ ReductionIdScopeSpec, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext*/ false,
+ /*AllowDestructorName*/ false,
+ /*AllowConstructorName*/ false,
+ /*AllowDeductionGuide*/ false, nullptr, ReductionId);
}
/// Checks if the token is a valid map-type-modifier.
@@ -2906,6 +2909,7 @@ bool Parser::parseMapperModifier(OpenMPVarListDataTy &Data) {
if (getLangOpts().CPlusPlus)
ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec,
/*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false);
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) {
Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier);
@@ -3011,6 +3015,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
if (getLangOpts().CPlusPlus)
ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec,
/*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false);
InvalidReductionId = ParseReductionId(
*this, Data.ReductionOrMapperIdScopeSpec, UnqualifiedReductionId);
diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp
index 2e369448ab6a..262def2b38a1 100644
--- a/clang/lib/Parse/ParseStmtAsm.cpp
+++ b/clang/lib/Parse/ParseStmtAsm.cpp
@@ -220,9 +220,10 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
// Parse an optional scope-specifier if we're in C++.
CXXScopeSpec SS;
- if (getLangOpts().CPlusPlus) {
- ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false);
- }
+ if (getLangOpts().CPlusPlus)
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false);
// Require an identifier here.
SourceLocation TemplateKWLoc;
@@ -233,12 +234,13 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
Result = ParseCXXThis();
Invalid = false;
} else {
- Invalid = ParseUnqualifiedId(SS,
- /*EnteringContext=*/false,
- /*AllowDestructorName=*/false,
- /*AllowConstructorName=*/false,
- /*AllowDeductionGuide=*/false,
- /*ObjectType=*/nullptr, &TemplateKWLoc, Id);
+ Invalid =
+ ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
+ /*AllowDestructorName=*/false,
+ /*AllowConstructorName=*/false,
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Id);
// Perform the lookup.
Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,
IsUnevaluatedContext);
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 0406820f74a3..802fe35d4f62 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -363,9 +363,11 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
DiagnoseAndSkipCXX11Attributes();
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
- /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr,
- /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) ||
+ if (ParseOptionalCXXScopeSpecifier(
+ SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) ||
SS.isInvalid()) {
SkipUntil(tok::semi);
return nullptr;
@@ -376,12 +378,12 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
diag::err_concept_definition_not_identifier);
UnqualifiedId Result;
- if (ParseUnqualifiedId(SS, /*EnteringContext=*/false,
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
- /*ObjectType=*/ParsedType(), /*TemplateKWLoc=*/nullptr,
- Result)) {
+ /*TemplateKWLoc=*/nullptr, Result)) {
SkipUntil(tok::semi);
return nullptr;
}
@@ -682,19 +684,19 @@ bool Parser::TryAnnotateTypeConstraint() {
return false;
CXXScopeSpec SS;
bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
- if (ParseOptionalCXXScopeSpecifier(
- SS, ParsedType(),
- /*EnteringContext=*/false,
- /*MayBePseudoDestructor=*/nullptr,
- // If this is not a type-constraint, then
- // this scope-spec is part of the typename
- // of a non-type template parameter
- /*IsTypename=*/true, /*LastII=*/nullptr,
- // We won't find concepts in
- // non-namespaces anyway, so might as well
- // parse this correctly for possible type
- // names.
- /*OnlyNamespace=*/false))
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ // If this is not a type-constraint, then
+ // this scope-spec is part of the typename
+ // of a non-type template parameter
+ /*IsTypename=*/true, /*LastII=*/nullptr,
+ // We won't find concepts in
+ // non-namespaces anyway, so might as well
+ // parse this correctly for possible type
+ // names.
+ /*OnlyNamespace=*/false))
return true;
if (Tok.is(tok::identifier)) {
@@ -754,7 +756,8 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
TemplateIdAnnotation *TypeConstraint = nullptr;
bool TypenameKeyword = false;
SourceLocation KeyLoc;
- ParseOptionalCXXScopeSpecifier(TypeConstraintSS, nullptr,
+ ParseOptionalCXXScopeSpecifier(TypeConstraintSS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext*/ false);
if (Tok.is(tok::annot_template_id)) {
// Consume the 'type-constraint'.
@@ -1438,7 +1441,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
// followed by a token that terminates a template argument, such as ',',
// '>', or (in some cases) '>>'.
CXXScopeSpec SS; // nested-name-specifier, if present
- ParseOptionalCXXScopeSpecifier(SS, nullptr,
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false);
ParsedTemplateArgument Result;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 27cb8a2a5e76..0a63ac2d5e1b 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1605,7 +1605,9 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) {
CXXScopeSpec SS;
if (getLangOpts().CPlusPlus &&
- ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext))
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ EnteringContext))
return ANK_Error;
if (Tok.isNot(tok::identifier) || SS.isInvalid()) {
@@ -1842,6 +1844,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
SourceLocation TypenameLoc = ConsumeToken();
CXXScopeSpec SS;
if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false, nullptr,
/*IsTypename*/ true))
return true;
@@ -1914,7 +1917,9 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
CXXScopeSpec SS;
if (getLangOpts().CPlusPlus)
- if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext*/false))
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext*/ false))
return true;
return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation);
@@ -2043,7 +2048,9 @@ bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!");
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext))
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ EnteringContext))
return true;
if (SS.isEmpty())
return false;
@@ -2152,7 +2159,8 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
// Parse nested-name-specifier.
if (getLangOpts().CPlusPlus)
- ParseOptionalCXXScopeSpecifier(Result.SS, nullptr,
+ ParseOptionalCXXScopeSpecifier(Result.SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false);
// Check nested-name specifier.
@@ -2163,10 +2171,12 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
// Parse the unqualified-id.
SourceLocation TemplateKWLoc; // FIXME: parsed, but unused.
- if (ParseUnqualifiedId(
- Result.SS, /*EnteringContext*/false, /*AllowDestructorName*/true,
- /*AllowConstructorName*/true, /*AllowDeductionGuide*/false, nullptr,
- &TemplateKWLoc, Result.Name)) {
+ if (ParseUnqualifiedId(Result.SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext*/ false,
+ /*AllowDestructorName*/ true,
+ /*AllowConstructorName*/ true,
+ /*AllowDeductionGuide*/ false, &TemplateKWLoc,
+ Result.Name)) {
T.skipToEnd();
return true;
}
diff --git a/clang/test/SemaTemplate/dependent-typos-recovery.cpp b/clang/test/SemaTemplate/dependent-typos-recovery.cpp
new file mode 100644
index 000000000000..d05b7144d908
--- /dev/null
+++ b/clang/test/SemaTemplate/dependent-typos-recovery.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// There should be no extra errors about missing 'template' keywords.
+struct B {
+ template <typename T>
+ int f(){};
+} builder; // expected-note 2{{'builder' declared here}}
+
+auto a = bilder.f<int>(); // expected-error{{undeclared identifier 'bilder'; did you mean}}
+auto b = (*(&bilder+0)).f<int>(); // expected-error{{undeclared identifier 'bilder'; did you mean}}
More information about the cfe-commits
mailing list