From 52f8d19ceda6a166497e71c5faaa6a4c07f0bbef Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 10 May 2017 21:32:16 +0000 Subject: [PATCH] Improve diagnosis of unknown template name. When an undeclared identifier in a context that requires a type is followed by '<', only look for type templates when typo-correcting, tweak the diagnostic text to say that a template name (not a type name) was undeclared, and parse the template arguments when recovering from the error. llvm-svn: 302732 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Parse/ParseDecl.cpp | 11 ++- clang/lib/Sema/SemaDecl.cpp | 80 ++++++++++++++++------ clang/lib/Sema/SemaTemplate.cpp | 4 +- clang/test/SemaCXX/invalid-member-expr.cpp | 4 +- clang/test/SemaCXX/typo-correction.cpp | 11 +-- clang/test/SemaTemplate/deduction-crash.cpp | 2 +- clang/test/SemaTemplate/explicit-instantiation.cpp | 2 +- .../ms-lookup-template-base-classes.cpp | 3 +- 10 files changed, 85 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 07bb0bf..5ef040d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8188,7 +8188,9 @@ def err_undeclared_use_suggest : Error< "use of undeclared %0; did you mean %1?">; def err_undeclared_var_use_suggest : Error< "use of undeclared identifier %0; did you mean %1?">; +def err_no_template : Error<"no template named %0">; def err_no_template_suggest : Error<"no template named %0; did you mean %1?">; +def err_no_member_template : Error<"no template named %0 in %1">; def err_no_member_template_suggest : Error< "no template named %0 in %1; did you mean %select{|simply }2%3?">; def err_non_template_in_template_id : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4faf2a3..ef96f54 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1593,7 +1593,7 @@ public: Scope *S, CXXScopeSpec *SS, ParsedType &SuggestedType, - bool AllowClassTemplates = false); + bool IsTemplateName = false); /// Attempt to behave like MSVC in situations where lookup of an unqualified /// type name has failed in a dependent context. In these situations, we diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 1465d21..4ccee74 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2577,9 +2577,9 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, // and attempt to recover. ParsedType T; IdentifierInfo *II = Tok.getIdentifierInfo(); + bool IsTemplateName = getLangOpts().CPlusPlus && NextToken().is(tok::less); Actions.DiagnoseUnknownTypeName(II, Loc, getCurScope(), SS, T, - getLangOpts().CPlusPlus && - NextToken().is(tok::less)); + IsTemplateName); if (T) { // The action has suggested that the type T could be used. Set that as // the type in the declaration specifiers, consume the would-be type @@ -2604,6 +2604,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, DS.SetRangeEnd(Tok.getLocation()); ConsumeToken(); + // Eat any following template arguments. + if (IsTemplateName) { + SourceLocation LAngle, RAngle; + TemplateArgList Args; + ParseTemplateIdAfterTemplateName(true, LAngle, Args, RAngle); + } + // TODO: Could inject an invalid typedef decl in an enclosing scope to // avoid rippling error messages on subsequent uses of the same type, // could be useful if #include was forgotten. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 367a134..0197129 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -64,22 +64,45 @@ namespace { class TypeNameValidatorCCC : public CorrectionCandidateCallback { public: - TypeNameValidatorCCC(bool AllowInvalid, bool WantClass=false, - bool AllowTemplates=false) - : AllowInvalidDecl(AllowInvalid), WantClassName(WantClass), - AllowTemplates(AllowTemplates) { - WantExpressionKeywords = false; - WantCXXNamedCasts = false; - WantRemainingKeywords = false; + TypeNameValidatorCCC(bool AllowInvalid, bool WantClass = false, + bool AllowTemplates = false, + bool AllowNonTemplates = true) + : AllowInvalidDecl(AllowInvalid), WantClassName(WantClass), + AllowTemplates(AllowTemplates), AllowNonTemplates(AllowNonTemplates) { + WantExpressionKeywords = false; + WantCXXNamedCasts = false; + WantRemainingKeywords = false; } bool ValidateCandidate(const TypoCorrection &candidate) override { if (NamedDecl *ND = candidate.getCorrectionDecl()) { + if (!AllowInvalidDecl && ND->isInvalidDecl()) + return false; + + if (getAsTypeTemplateDecl(ND)) + return AllowTemplates; + bool IsType = isa(ND) || isa(ND); - bool AllowedTemplate = AllowTemplates && getAsTypeTemplateDecl(ND); - return (IsType || AllowedTemplate) && - (AllowInvalidDecl || !ND->isInvalidDecl()); + if (!IsType) + return false; + + if (AllowNonTemplates) + return true; + + // An injected-class-name of a class template (specialization) is valid + // as a template or as a non-template. + if (AllowTemplates) { + auto *RD = dyn_cast(ND); + if (!RD || !RD->isInjectedClassName()) + return false; + RD = cast(RD->getDeclContext()); + return RD->getDescribedClassTemplate() || + isa(RD); + } + + return false; } + return !WantClassName && candidate.isKeyword(); } @@ -87,6 +110,7 @@ class TypeNameValidatorCCC : public CorrectionCandidateCallback { bool AllowInvalidDecl; bool WantClassName; bool AllowTemplates; + bool AllowNonTemplates; }; } // end anonymous namespace @@ -627,7 +651,7 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, Scope *S, CXXScopeSpec *SS, ParsedType &SuggestedType, - bool AllowClassTemplates) { + bool IsTemplateName) { // Don't report typename errors for editor placeholders. if (II->isEditorPlaceholder()) return; @@ -639,28 +663,41 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, if (TypoCorrection Corrected = CorrectTypo(DeclarationNameInfo(II, IILoc), LookupOrdinaryName, S, SS, llvm::make_unique( - false, false, AllowClassTemplates), + false, false, IsTemplateName, !IsTemplateName), CTK_ErrorRecovery)) { + // FIXME: Support error recovery for the template-name case. + bool CanRecover = !IsTemplateName; if (Corrected.isKeyword()) { // We corrected to a keyword. - diagnoseTypo(Corrected, PDiag(diag::err_unknown_typename_suggest) << II); + diagnoseTypo(Corrected, + PDiag(IsTemplateName ? diag::err_no_template_suggest + : diag::err_unknown_typename_suggest) + << II); II = Corrected.getCorrectionAsIdentifierInfo(); } else { // We found a similarly-named type or interface; suggest that. if (!SS || !SS->isSet()) { diagnoseTypo(Corrected, - PDiag(diag::err_unknown_typename_suggest) << II); + PDiag(IsTemplateName ? diag::err_no_template_suggest + : diag::err_unknown_typename_suggest) + << II, CanRecover); } else if (DeclContext *DC = computeDeclContext(*SS, false)) { std::string CorrectedStr(Corrected.getAsString(getLangOpts())); bool DroppedSpecifier = Corrected.WillReplaceSpecifier() && II->getName().equals(CorrectedStr); diagnoseTypo(Corrected, - PDiag(diag::err_unknown_nested_typename_suggest) - << II << DC << DroppedSpecifier << SS->getRange()); + PDiag(IsTemplateName + ? diag::err_no_member_template_suggest + : diag::err_unknown_nested_typename_suggest) + << II << DC << DroppedSpecifier << SS->getRange(), + CanRecover); } else { llvm_unreachable("could not have corrected a typo here"); } + if (!CanRecover) + return; + CXXScopeSpec tmpSS; if (Corrected.getCorrectionSpecifier()) tmpSS.MakeTrivial(Context, Corrected.getCorrectionSpecifier(), @@ -675,7 +712,7 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, return; } - if (getLangOpts().CPlusPlus) { + if (getLangOpts().CPlusPlus && !IsTemplateName) { // See if II is a class template that the user forgot to pass arguments to. UnqualifiedId Name; Name.setIdentifier(II, IILoc); @@ -700,10 +737,13 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, // (struct, union, enum) from Parser::ParseImplicitInt here, instead? if (!SS || (!SS->isSet() && !SS->isInvalid())) - Diag(IILoc, diag::err_unknown_typename) << II; + Diag(IILoc, IsTemplateName ? diag::err_no_template + : diag::err_unknown_typename) + << II; else if (DeclContext *DC = computeDeclContext(*SS, false)) - Diag(IILoc, diag::err_typename_nested_not_found) - << II << DC << SS->getRange(); + Diag(IILoc, IsTemplateName ? diag::err_no_member_template + : diag::err_typename_nested_not_found) + << II << DC << SS->getRange(); else if (isDependentScopeSpecifier(*SS)) { unsigned DiagID = diag::err_typename_missing; if (getLangOpts().MSVCCompat && isMicrosoftMissingTypename(SS, S)) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5e2c9b1..71414be 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -515,11 +515,11 @@ void Sema::diagnoseExprIntendedAsTemplateName(Scope *S, ExprResult TemplateName, diagnoseTypo(Corrected, PDiag(diag::err_non_template_in_member_template_id_suggest) << Name << LookupCtx << DroppedSpecifier - << SS.getRange()); + << SS.getRange(), false); } else { diagnoseTypo(Corrected, PDiag(diag::err_non_template_in_template_id_suggest) - << Name); + << Name, false); } if (Found) Diag(Found->getLocation(), diff --git a/clang/test/SemaCXX/invalid-member-expr.cpp b/clang/test/SemaCXX/invalid-member-expr.cpp index 172be6b..fd50d32 100644 --- a/clang/test/SemaCXX/invalid-member-expr.cpp +++ b/clang/test/SemaCXX/invalid-member-expr.cpp @@ -53,9 +53,7 @@ namespace test3 { namespace rdar11293995 { struct Length { - explicit Length(PassRefPtr); // expected-error {{unknown type name}} \ - expected-error {{expected ')'}} \ - expected-note {{to match this '('}} + explicit Length(PassRefPtr); // expected-error {{no template named 'PassRefPtr}} expected-error {{undeclared identifier 'CalculationValue'}} }; struct LengthSize { diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index c59ee61..2d78f06 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -524,13 +524,16 @@ namespace shadowed_template { template class Fizbin {}; // expected-note {{'::shadowed_template::Fizbin' declared here}} class Baz { int Fizbin(); - // TODO: Teach the parser to recover from the typo correction instead of - // continuing to treat the template name as an implicit-int declaration. - Fizbin qux; // expected-error {{unknown type name 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}} \ - // expected-error {{expected member name or ';' after declaration specifiers}} + Fizbin qux; // expected-error {{no template named 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}} }; } +namespace no_correct_template_id_to_non_template { + struct Frobnatz {}; // expected-note {{declared here}} + Frobnats fn; // expected-error {{unknown type name 'Frobnats'; did you mean 'Frobnatz'?}} + Frobnats fni; // expected-error-re {{no template named 'Frobnats'{{$}}}} +} + namespace PR18852 { void func() { struct foo { diff --git a/clang/test/SemaTemplate/deduction-crash.cpp b/clang/test/SemaTemplate/deduction-crash.cpp index ff7421a..c94c9db 100644 --- a/clang/test/SemaTemplate/deduction-crash.cpp +++ b/clang/test/SemaTemplate/deduction-crash.cpp @@ -2,7 +2,7 @@ // Note that the error count below doesn't matter. We just want to // make sure that the parser doesn't crash. -// CHECK: 16 errors +// CHECK: 17 errors // PR7511 template diff --git a/clang/test/SemaTemplate/explicit-instantiation.cpp b/clang/test/SemaTemplate/explicit-instantiation.cpp index 010716d..42d9f4d 100644 --- a/clang/test/SemaTemplate/explicit-instantiation.cpp +++ b/clang/test/SemaTemplate/explicit-instantiation.cpp @@ -95,7 +95,7 @@ namespace PR7622 { struct basic_streambuf; template - struct basic_streambuf{friend bob<>()}; // expected-error{{unknown type name 'bob'}} \ + struct basic_streambuf{friend bob<>()}; // expected-error{{no template named 'bob'}} \ // expected-error{{expected member name or ';' after declaration specifiers}} template struct basic_streambuf; } diff --git a/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 6afc709..a41248e 100644 --- a/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ b/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -347,8 +347,7 @@ template struct B : A { }; template struct C : A { // Incorrect form. - NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} - //expected-error@-1 {{expected member name or ';' after declaration specifiers}} + NameFromBase m; // expected-error {{no template named 'NameFromBase'}} }; } -- 2.7.4