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
This commit is contained in:
Richard Smith 2017-05-10 21:32:16 +00:00
parent 80f8cfb37f
commit 52f8d19ced
10 changed files with 85 additions and 36 deletions

View File

@ -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<

View File

@ -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

View File

@ -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.

View File

@ -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<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(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<CXXRecordDecl>(ND);
if (!RD || !RD->isInjectedClassName())
return false;
RD = cast<CXXRecordDecl>(RD->getDeclContext());
return RD->getDescribedClassTemplate() ||
isa<ClassTemplateSpecializationDecl>(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<TypeNameValidatorCCC>(
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))

View File

@ -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(),

View File

@ -53,9 +53,7 @@ namespace test3 {
namespace rdar11293995 {
struct Length {
explicit Length(PassRefPtr<CalculationValue>); // expected-error {{unknown type name}} \
expected-error {{expected ')'}} \
expected-note {{to match this '('}}
explicit Length(PassRefPtr<CalculationValue>); // expected-error {{no template named 'PassRefPtr}} expected-error {{undeclared identifier 'CalculationValue'}}
};
struct LengthSize {

View File

@ -524,13 +524,16 @@ namespace shadowed_template {
template <typename T> 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<int> qux; // expected-error {{unknown type name 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}} \
// expected-error {{expected member name or ';' after declaration specifiers}}
Fizbin<int> 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<int> fni; // expected-error-re {{no template named 'Frobnats'{{$}}}}
}
namespace PR18852 {
void func() {
struct foo {

View File

@ -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<a>

View File

@ -95,7 +95,7 @@ namespace PR7622 {
struct basic_streambuf;
template<typename,typename>
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<int>;
}

View File

@ -347,8 +347,7 @@ template <typename T> struct B : A<T> {
};
template <typename T> struct C : A<T> {
// Incorrect form.
NameFromBase<T> m; // expected-error {{unknown type name 'NameFromBase'}}
//expected-error@-1 {{expected member name or ';' after declaration specifiers}}
NameFromBase<T> m; // expected-error {{no template named 'NameFromBase'}}
};
}