forked from OSchip/llvm-project
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:
parent
80f8cfb37f
commit
52f8d19ced
|
@ -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<
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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'}}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue