diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1815,6 +1815,16 @@ CXXScopeSpec &SS); bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, ParsedType ObjectType, + bool ObjectHasErrors, + bool EnteringContext, + bool *MayBePseudoDestructor = nullptr, + bool IsTypename = false, + IdentifierInfo **LastII = nullptr, + bool OnlyNamespace = false, + bool InUsingDeclaration = false); + /// An overload for the common case of non-member expressions, i.e. when + /// ObjectType is null. + bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, bool EnteringContext, bool *MayBePseudoDestructor = nullptr, bool IsTypename = false, @@ -1865,9 +1875,8 @@ //===--------------------------------------------------------------------===// // C++ 5.2.4: C++ Pseudo-Destructor Expressions ExprResult ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, - tok::TokenKind OpKind, - CXXScopeSpec &SS, - ParsedType ObjectType); + tok::TokenKind OpKind, CXXScopeSpec &SS, + ParsedType ObjectType); //===--------------------------------------------------------------------===// // C++ 9.3.2: C++ 'this' pointer @@ -2907,13 +2916,12 @@ BaseResult ParseBaseSpecifier(Decl *ClassDecl); AccessSpecifier getAccessSpecifierIfPresent() const; - bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, + bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, ParsedType ObjectType, + bool ObjectHadErrors, SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc, - bool EnteringContext, - ParsedType ObjectType, - UnqualifiedId &Id, + bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId); bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, ParsedType ObjectType, @@ -3079,13 +3087,17 @@ bool ParseOpenMPVarList(OpenMPDirectiveKind DKind, OpenMPClauseKind Kind, SmallVectorImpl &Vars, OpenMPVarListDataTy &Data); + bool ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, + bool ObjectHadErrors, bool EnteringContext, + bool AllowDestructorName, bool AllowConstructorName, + bool AllowDeductionGuide, + SourceLocation *TemplateKWLoc, UnqualifiedId &Result); + /// A convenient overload for the common case of non-member expressions, + /// i.e. when ObjectType is null. bool ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, - bool AllowDestructorName, - bool AllowConstructorName, + 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/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6904,12 +6904,10 @@ SourceLocation TemplateKWLoc = SourceLocation(), AssumedTemplateKind *ATK = nullptr); - TemplateNameKind isTemplateName(Scope *S, - CXXScopeSpec &SS, + TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, const UnqualifiedId &Name, - ParsedType ObjectType, - bool EnteringContext, + ParsedType ObjectType, bool EnteringContext, TemplateTy &Template, bool &MemberOfUnknownSpecialization); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4415,8 +4415,7 @@ ColonProtectionRAIIObject X(*this, AllowDeclaration); CXXScopeSpec Spec; - if (ParseOptionalCXXScopeSpecifier(Spec, nullptr, - /*EnteringContext=*/true)) + if (ParseOptionalCXXScopeSpecifier(Spec, /*EnteringContext=*/true)) return; if (Spec.isSet() && Tok.isNot(tok::identifier)) { @@ -5248,8 +5247,7 @@ // Parse the C++ scope specifier. CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, - /*EnteringContext=*/true)) { + if (ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/true)) { TPA.Revert(); return false; } @@ -5628,7 +5626,7 @@ D.getContext() == DeclaratorContext::FileContext || D.getContext() == DeclaratorContext::MemberContext; CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext); + ParseOptionalCXXScopeSpecifier(SS, EnteringContext); if (SS.isNotEmpty()) { if (Tok.isNot(tok::star)) { @@ -5851,8 +5849,7 @@ bool EnteringContext = D.getContext() == DeclaratorContext::FileContext || D.getContext() == DeclaratorContext::MemberContext; - ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), nullptr, - EnteringContext); + ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), EnteringContext); } if (D.getCXXScopeSpec().isValid()) { @@ -5928,8 +5925,7 @@ if (ParseUnqualifiedId(D.getCXXScopeSpec(), /*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 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -290,7 +290,7 @@ CXXScopeSpec SS; // Parse (optional) nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false, + ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, /*IsTypename=*/false, /*LastII=*/nullptr, @@ -530,7 +530,7 @@ CXXScopeSpec SS; // Parse (optional) nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false, + ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, /*IsTypename=*/false, /*LastII=*/nullptr, @@ -597,7 +597,7 @@ // Parse nested-name-specifier. IdentifierInfo *LastII = nullptr; - if (ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false, + if (ParseOptionalCXXScopeSpecifier(D.SS, /*EnteringContext=*/false, /*MayBePseudoDtor=*/nullptr, /*IsTypename=*/false, /*LastII=*/&LastII, @@ -634,10 +634,9 @@ if (ParseUnqualifiedId( D.SS, /*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 +1114,7 @@ // Parse optional nested-name-specifier CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false)) + if (ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false)) return true; BaseLoc = Tok.getLocation(); @@ -1547,7 +1546,7 @@ CXXScopeSpec Spec; bool HasValidSpec = true; - if (ParseOptionalCXXScopeSpecifier(Spec, nullptr, EnteringContext)) { + if (ParseOptionalCXXScopeSpecifier(Spec, EnteringContext)) { DS.SetTypeSpecError(); HasValidSpec = false; } @@ -2501,8 +2500,7 @@ if (isAccessDecl) { // Collect the scope specifier token we annotated earlier. CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, - /*EnteringContext=*/false); + ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false); if (SS.isInvalid()) { SkipUntil(tok::semi); @@ -2512,8 +2510,8 @@ // Try to parse an unqualified-id. SourceLocation TemplateKWLoc; UnqualifiedId Name; - if (ParseUnqualifiedId(SS, false, true, true, false, nullptr, - &TemplateKWLoc, Name)) { + if (ParseUnqualifiedId(SS, false, true, true, false, &TemplateKWLoc, + Name)) { SkipUntil(tok::semi); return nullptr; } @@ -3493,7 +3491,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { // parse '::'[opt] nested-name-specifier[opt] CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false)) + if (ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false)) return true; // : identifier diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1529,8 +1529,7 @@ // type, translate it into a type and continue parsing as a // cast expression. CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, - /*EnteringContext=*/false); + ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false); AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, @@ -1983,9 +1982,9 @@ 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 +2044,13 @@ 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 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -124,6 +124,10 @@ /// 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 @@ /// /// /// \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 @@ if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) && (IsTypename || isTemplateArgumentList(1) == TPResult::True)) { - // We have something like t::getAs, 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, 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, @@ -553,6 +557,16 @@ return false; } +bool Parser::ParseOptionalCXXScopeSpecifier( + CXXScopeSpec &SS, bool EnteringContext, bool *MayBePseudoDestructor, + bool IsTypename, IdentifierInfo **LastII, bool OnlyNamespace, + bool IsUsingDeclaration) { + return ParseOptionalCXXScopeSpecifier( + SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, EnteringContext, MayBePseudoDestructor, + IsTypename, LastII, OnlyNamespace, IsUsingDeclaration); +} + ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand, Token &Replacement) { @@ -597,8 +611,7 @@ /*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 +679,7 @@ // '::' unqualified-id // CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); + ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false); Token Replacement; ExprResult Result = @@ -1769,10 +1782,10 @@ // 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 +2272,12 @@ /// \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 +2287,6 @@ /// \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 +2295,10 @@ /// 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 +2330,27 @@ if (TNK == TNK_Non_template && MemberOfUnknownSpecialization && ObjectType && isTemplateArgumentList(0) == TPResult::True) { - // We have something like t->getAs(), 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(), 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 +2707,13 @@ /// \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 +2723,14 @@ /// /// \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 +2789,9 @@ 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 +2868,9 @@ 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 +2920,8 @@ 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 +2948,9 @@ 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. @@ -2947,6 +2970,18 @@ return true; } +bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, + bool AllowDestructorName, + bool AllowConstructorName, + bool AllowDeductionGuide, + SourceLocation *TemplateKWLoc, + UnqualifiedId &Result) { + return ParseUnqualifiedId(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, EnteringContext, + AllowDestructorName, AllowConstructorName, + AllowDeductionGuide, TemplateKWLoc, Result); +} + /// ParseCXXNewExpression - Parse a C++ new-expression. New is used to allocate /// memory in a typesafe manner and call constructors. /// diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2287,12 +2287,12 @@ NoIdentIsFound = false; if (AllowScopeSpecifier && getLangOpts().CPlusPlus && - ParseOptionalCXXScopeSpecifier(SS, nullptr, false)) { + ParseOptionalCXXScopeSpecifier(SS, 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)) { + Name)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); @@ -2851,8 +2851,8 @@ return P.ParseUnqualifiedId(ReductionIdScopeSpec, /*EnteringContext*/ false, /*AllowDestructorName*/ false, /*AllowConstructorName*/ false, - /*AllowDeductionGuide*/ false, - nullptr, nullptr, ReductionId); + /*AllowDeductionGuide*/ false, nullptr, + ReductionId); } /// Checks if the token is a valid map-type-modifier. @@ -2879,7 +2879,6 @@ // Parse mapper-identifier if (getLangOpts().CPlusPlus) ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec, - /*ObjectType=*/nullptr, /*EnteringContext=*/false); if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) { Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier); @@ -2984,7 +2983,6 @@ ColonProtectionRAIIObject ColonRAII(*this); if (getLangOpts().CPlusPlus) ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec, - /*ObjectType=*/nullptr, /*EnteringContext=*/false); InvalidReductionId = ParseReductionId( *this, Data.ReductionOrMapperIdScopeSpec, UnqualifiedReductionId); diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -220,9 +220,8 @@ // 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, /*EnteringContext=*/false); // Require an identifier here. SourceLocation TemplateKWLoc; @@ -233,12 +232,12 @@ Result = ParseCXXThis(); Invalid = false; } else { - Invalid = ParseUnqualifiedId(SS, - /*EnteringContext=*/false, - /*AllowDestructorName=*/false, - /*AllowConstructorName=*/false, - /*AllowDeductionGuide=*/false, - /*ObjectType=*/nullptr, &TemplateKWLoc, Id); + Invalid = + ParseUnqualifiedId(SS, + /*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 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -363,9 +363,9 @@ DiagnoseAndSkipCXX11Attributes(); CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), - /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, - /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) || + if (ParseOptionalCXXScopeSpecifier( + SS, /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) || SS.isInvalid()) { SkipUntil(tok::semi); return nullptr; @@ -380,8 +380,7 @@ /*AllowDestructorName=*/false, /*AllowConstructorName=*/false, /*AllowDeductionGuide=*/false, - /*ObjectType=*/ParsedType(), /*TemplateKWLoc=*/nullptr, - Result)) { + /*TemplateKWLoc=*/nullptr, Result)) { SkipUntil(tok::semi); return nullptr; } @@ -683,7 +682,7 @@ CXXScopeSpec SS; bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); if (ParseOptionalCXXScopeSpecifier( - SS, ParsedType(), + SS, ParsedType(), /*ObjectHadErrors*/false, /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, // If this is not a type-constraint, then @@ -753,7 +752,7 @@ TemplateIdAnnotation *TypeConstraint = nullptr; bool TypenameKeyword = false; SourceLocation KeyLoc; - ParseOptionalCXXScopeSpecifier(TypeConstraintSS, nullptr, + ParseOptionalCXXScopeSpecifier(TypeConstraintSS, /*EnteringContext*/ false); if (Tok.is(tok::annot_template_id)) { // Consume the 'type-constraint'. @@ -1437,8 +1436,7 @@ // 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, - /*EnteringContext=*/false); + ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false); ParsedTemplateArgument Result; SourceLocation EllipsisLoc; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1604,7 +1604,7 @@ CXXScopeSpec SS; if (getLangOpts().CPlusPlus && - ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext)) + ParseOptionalCXXScopeSpecifier(SS, EnteringContext)) return ANK_Error; if (Tok.isNot(tok::identifier) || SS.isInvalid()) { @@ -1840,8 +1840,7 @@ // simple-template-id SourceLocation TypenameLoc = ConsumeToken(); CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, - /*EnteringContext=*/false, nullptr, + if (ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext=*/false, nullptr, /*IsTypename*/ true)) return true; if (SS.isEmpty()) { @@ -1913,7 +1912,7 @@ CXXScopeSpec SS; if (getLangOpts().CPlusPlus) - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext*/false)) + if (ParseOptionalCXXScopeSpecifier(SS, /*EnteringContext*/ false)) return true; return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation); @@ -2042,7 +2041,7 @@ assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!"); CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext)) + if (ParseOptionalCXXScopeSpecifier(SS, EnteringContext)) return true; if (SS.isEmpty()) return false; @@ -2151,8 +2150,7 @@ // Parse nested-name-specifier. if (getLangOpts().CPlusPlus) - ParseOptionalCXXScopeSpecifier(Result.SS, nullptr, - /*EnteringContext=*/false); + ParseOptionalCXXScopeSpecifier(Result.SS, /*EnteringContext=*/false); // Check nested-name specifier. if (Result.SS.isInvalid()) { @@ -2163,8 +2161,8 @@ // Parse the unqualified-id. SourceLocation TemplateKWLoc; // FIXME: parsed, but unused. if (ParseUnqualifiedId( - Result.SS, /*EnteringContext*/false, /*AllowDestructorName*/true, - /*AllowConstructorName*/true, /*AllowDeductionGuide*/false, nullptr, + Result.SS, /*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 --- /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 + int f(){}; +} builder; // expected-note 2{{'builder' declared here}} + +auto a = bilder.f(); // expected-error{{undeclared identifier 'bilder'; did you mean}} +auto b = (*(&bilder+0)).f(); // expected-error{{undeclared identifier 'bilder'; did you mean}}