Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 9986cce

Browse files
committed
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@302732 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 84e1942 commit 9986cce

10 files changed

+85
-36
lines changed

include/clang/Basic/DiagnosticSemaKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -8188,7 +8188,9 @@ def err_undeclared_use_suggest : Error<
81888188
"use of undeclared %0; did you mean %1?">;
81898189
def err_undeclared_var_use_suggest : Error<
81908190
"use of undeclared identifier %0; did you mean %1?">;
8191+
def err_no_template : Error<"no template named %0">;
81918192
def err_no_template_suggest : Error<"no template named %0; did you mean %1?">;
8193+
def err_no_member_template : Error<"no template named %0 in %1">;
81928194
def err_no_member_template_suggest : Error<
81938195
"no template named %0 in %1; did you mean %select{|simply }2%3?">;
81948196
def err_non_template_in_template_id : Error<

include/clang/Sema/Sema.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,7 @@ class Sema {
15931593
Scope *S,
15941594
CXXScopeSpec *SS,
15951595
ParsedType &SuggestedType,
1596-
bool AllowClassTemplates = false);
1596+
bool IsTemplateName = false);
15971597

15981598
/// Attempt to behave like MSVC in situations where lookup of an unqualified
15991599
/// type name has failed in a dependent context. In these situations, we

lib/Parse/ParseDecl.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -2577,9 +2577,9 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
25772577
// and attempt to recover.
25782578
ParsedType T;
25792579
IdentifierInfo *II = Tok.getIdentifierInfo();
2580+
bool IsTemplateName = getLangOpts().CPlusPlus && NextToken().is(tok::less);
25802581
Actions.DiagnoseUnknownTypeName(II, Loc, getCurScope(), SS, T,
2581-
getLangOpts().CPlusPlus &&
2582-
NextToken().is(tok::less));
2582+
IsTemplateName);
25832583
if (T) {
25842584
// The action has suggested that the type T could be used. Set that as
25852585
// the type in the declaration specifiers, consume the would-be type
@@ -2604,6 +2604,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
26042604
DS.SetRangeEnd(Tok.getLocation());
26052605
ConsumeToken();
26062606

2607+
// Eat any following template arguments.
2608+
if (IsTemplateName) {
2609+
SourceLocation LAngle, RAngle;
2610+
TemplateArgList Args;
2611+
ParseTemplateIdAfterTemplateName(true, LAngle, Args, RAngle);
2612+
}
2613+
26072614
// TODO: Could inject an invalid typedef decl in an enclosing scope to
26082615
// avoid rippling error messages on subsequent uses of the same type,
26092616
// could be useful if #include was forgotten.

lib/Sema/SemaDecl.cpp

+60-20
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,53 @@ namespace {
6464

6565
class TypeNameValidatorCCC : public CorrectionCandidateCallback {
6666
public:
67-
TypeNameValidatorCCC(bool AllowInvalid, bool WantClass=false,
68-
bool AllowTemplates=false)
69-
: AllowInvalidDecl(AllowInvalid), WantClassName(WantClass),
70-
AllowTemplates(AllowTemplates) {
71-
WantExpressionKeywords = false;
72-
WantCXXNamedCasts = false;
73-
WantRemainingKeywords = false;
67+
TypeNameValidatorCCC(bool AllowInvalid, bool WantClass = false,
68+
bool AllowTemplates = false,
69+
bool AllowNonTemplates = true)
70+
: AllowInvalidDecl(AllowInvalid), WantClassName(WantClass),
71+
AllowTemplates(AllowTemplates), AllowNonTemplates(AllowNonTemplates) {
72+
WantExpressionKeywords = false;
73+
WantCXXNamedCasts = false;
74+
WantRemainingKeywords = false;
7475
}
7576

7677
bool ValidateCandidate(const TypoCorrection &candidate) override {
7778
if (NamedDecl *ND = candidate.getCorrectionDecl()) {
79+
if (!AllowInvalidDecl && ND->isInvalidDecl())
80+
return false;
81+
82+
if (getAsTypeTemplateDecl(ND))
83+
return AllowTemplates;
84+
7885
bool IsType = isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND);
79-
bool AllowedTemplate = AllowTemplates && getAsTypeTemplateDecl(ND);
80-
return (IsType || AllowedTemplate) &&
81-
(AllowInvalidDecl || !ND->isInvalidDecl());
86+
if (!IsType)
87+
return false;
88+
89+
if (AllowNonTemplates)
90+
return true;
91+
92+
// An injected-class-name of a class template (specialization) is valid
93+
// as a template or as a non-template.
94+
if (AllowTemplates) {
95+
auto *RD = dyn_cast<CXXRecordDecl>(ND);
96+
if (!RD || !RD->isInjectedClassName())
97+
return false;
98+
RD = cast<CXXRecordDecl>(RD->getDeclContext());
99+
return RD->getDescribedClassTemplate() ||
100+
isa<ClassTemplateSpecializationDecl>(RD);
101+
}
102+
103+
return false;
82104
}
105+
83106
return !WantClassName && candidate.isKeyword();
84107
}
85108

86109
private:
87110
bool AllowInvalidDecl;
88111
bool WantClassName;
89112
bool AllowTemplates;
113+
bool AllowNonTemplates;
90114
};
91115

92116
} // end anonymous namespace
@@ -627,7 +651,7 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
627651
Scope *S,
628652
CXXScopeSpec *SS,
629653
ParsedType &SuggestedType,
630-
bool AllowClassTemplates) {
654+
bool IsTemplateName) {
631655
// Don't report typename errors for editor placeholders.
632656
if (II->isEditorPlaceholder())
633657
return;
@@ -639,28 +663,41 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
639663
if (TypoCorrection Corrected =
640664
CorrectTypo(DeclarationNameInfo(II, IILoc), LookupOrdinaryName, S, SS,
641665
llvm::make_unique<TypeNameValidatorCCC>(
642-
false, false, AllowClassTemplates),
666+
false, false, IsTemplateName, !IsTemplateName),
643667
CTK_ErrorRecovery)) {
668+
// FIXME: Support error recovery for the template-name case.
669+
bool CanRecover = !IsTemplateName;
644670
if (Corrected.isKeyword()) {
645671
// We corrected to a keyword.
646-
diagnoseTypo(Corrected, PDiag(diag::err_unknown_typename_suggest) << II);
672+
diagnoseTypo(Corrected,
673+
PDiag(IsTemplateName ? diag::err_no_template_suggest
674+
: diag::err_unknown_typename_suggest)
675+
<< II);
647676
II = Corrected.getCorrectionAsIdentifierInfo();
648677
} else {
649678
// We found a similarly-named type or interface; suggest that.
650679
if (!SS || !SS->isSet()) {
651680
diagnoseTypo(Corrected,
652-
PDiag(diag::err_unknown_typename_suggest) << II);
681+
PDiag(IsTemplateName ? diag::err_no_template_suggest
682+
: diag::err_unknown_typename_suggest)
683+
<< II, CanRecover);
653684
} else if (DeclContext *DC = computeDeclContext(*SS, false)) {
654685
std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
655686
bool DroppedSpecifier = Corrected.WillReplaceSpecifier() &&
656687
II->getName().equals(CorrectedStr);
657688
diagnoseTypo(Corrected,
658-
PDiag(diag::err_unknown_nested_typename_suggest)
659-
<< II << DC << DroppedSpecifier << SS->getRange());
689+
PDiag(IsTemplateName
690+
? diag::err_no_member_template_suggest
691+
: diag::err_unknown_nested_typename_suggest)
692+
<< II << DC << DroppedSpecifier << SS->getRange(),
693+
CanRecover);
660694
} else {
661695
llvm_unreachable("could not have corrected a typo here");
662696
}
663697

698+
if (!CanRecover)
699+
return;
700+
664701
CXXScopeSpec tmpSS;
665702
if (Corrected.getCorrectionSpecifier())
666703
tmpSS.MakeTrivial(Context, Corrected.getCorrectionSpecifier(),
@@ -675,7 +712,7 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
675712
return;
676713
}
677714

678-
if (getLangOpts().CPlusPlus) {
715+
if (getLangOpts().CPlusPlus && !IsTemplateName) {
679716
// See if II is a class template that the user forgot to pass arguments to.
680717
UnqualifiedId Name;
681718
Name.setIdentifier(II, IILoc);
@@ -700,10 +737,13 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
700737
// (struct, union, enum) from Parser::ParseImplicitInt here, instead?
701738

702739
if (!SS || (!SS->isSet() && !SS->isInvalid()))
703-
Diag(IILoc, diag::err_unknown_typename) << II;
740+
Diag(IILoc, IsTemplateName ? diag::err_no_template
741+
: diag::err_unknown_typename)
742+
<< II;
704743
else if (DeclContext *DC = computeDeclContext(*SS, false))
705-
Diag(IILoc, diag::err_typename_nested_not_found)
706-
<< II << DC << SS->getRange();
744+
Diag(IILoc, IsTemplateName ? diag::err_no_member_template
745+
: diag::err_typename_nested_not_found)
746+
<< II << DC << SS->getRange();
707747
else if (isDependentScopeSpecifier(*SS)) {
708748
unsigned DiagID = diag::err_typename_missing;
709749
if (getLangOpts().MSVCCompat && isMicrosoftMissingTypename(SS, S))

lib/Sema/SemaTemplate.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -515,11 +515,11 @@ void Sema::diagnoseExprIntendedAsTemplateName(Scope *S, ExprResult TemplateName,
515515
diagnoseTypo(Corrected,
516516
PDiag(diag::err_non_template_in_member_template_id_suggest)
517517
<< Name << LookupCtx << DroppedSpecifier
518-
<< SS.getRange());
518+
<< SS.getRange(), false);
519519
} else {
520520
diagnoseTypo(Corrected,
521521
PDiag(diag::err_non_template_in_template_id_suggest)
522-
<< Name);
522+
<< Name, false);
523523
}
524524
if (Found)
525525
Diag(Found->getLocation(),

test/SemaCXX/invalid-member-expr.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ namespace test3 {
5353
namespace rdar11293995 {
5454

5555
struct Length {
56-
explicit Length(PassRefPtr<CalculationValue>); // expected-error {{unknown type name}} \
57-
expected-error {{expected ')'}} \
58-
expected-note {{to match this '('}}
56+
explicit Length(PassRefPtr<CalculationValue>); // expected-error {{no template named 'PassRefPtr}} expected-error {{undeclared identifier 'CalculationValue'}}
5957
};
6058

6159
struct LengthSize {

test/SemaCXX/typo-correction.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,16 @@ namespace shadowed_template {
524524
template <typename T> class Fizbin {}; // expected-note {{'::shadowed_template::Fizbin' declared here}}
525525
class Baz {
526526
int Fizbin();
527-
// TODO: Teach the parser to recover from the typo correction instead of
528-
// continuing to treat the template name as an implicit-int declaration.
529-
Fizbin<int> qux; // expected-error {{unknown type name 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}} \
530-
// expected-error {{expected member name or ';' after declaration specifiers}}
527+
Fizbin<int> qux; // expected-error {{no template named 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}}
531528
};
532529
}
533530

531+
namespace no_correct_template_id_to_non_template {
532+
struct Frobnatz {}; // expected-note {{declared here}}
533+
Frobnats fn; // expected-error {{unknown type name 'Frobnats'; did you mean 'Frobnatz'?}}
534+
Frobnats<int> fni; // expected-error-re {{no template named 'Frobnats'{{$}}}}
535+
}
536+
534537
namespace PR18852 {
535538
void func() {
536539
struct foo {

test/SemaTemplate/deduction-crash.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// Note that the error count below doesn't matter. We just want to
44
// make sure that the parser doesn't crash.
5-
// CHECK: 16 errors
5+
// CHECK: 17 errors
66

77
// PR7511
88
template<a>

test/SemaTemplate/explicit-instantiation.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ namespace PR7622 {
9595
struct basic_streambuf;
9696

9797
template<typename,typename>
98-
struct basic_streambuf{friend bob<>()}; // expected-error{{unknown type name 'bob'}} \
98+
struct basic_streambuf{friend bob<>()}; // expected-error{{no template named 'bob'}} \
9999
// expected-error{{expected member name or ';' after declaration specifiers}}
100100
template struct basic_streambuf<int>;
101101
}

test/SemaTemplate/ms-lookup-template-base-classes.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,7 @@ template <typename T> struct B : A<T> {
347347
};
348348
template <typename T> struct C : A<T> {
349349
// Incorrect form.
350-
NameFromBase<T> m; // expected-error {{unknown type name 'NameFromBase'}}
351-
//expected-error@-1 {{expected member name or ';' after declaration specifiers}}
350+
NameFromBase<T> m; // expected-error {{no template named 'NameFromBase'}}
352351
};
353352
}
354353

0 commit comments

Comments
 (0)